From dc8c8d2fcbe367ba765c339541d2a488c802f3df Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Tue, 15 Jan 2019 14:55:31 +0200 Subject: [PATCH 1/9] Add koa support --- README.md | 37 +- package-lock.json | 467 +- package.json | 7 +- src/frameworks/express.js | 24 + src/frameworks/koa.js | 27 + src/middleware.js | 22 +- test/{ => express}/test-server-formdata.js | 2 +- test/{ => express}/test-server-inheritance.js | 4 +- ...t-server-with-options-more-than-1-error.js | 4 +- .../{ => express}/test-server-with-options.js | 4 +- .../test-simple-server-base-route.js | 4 +- .../test-simple-server-with-base-path.js | 6 +- .../test-simple-server-with-coercion.js | 2 +- test/{ => express}/test-simple-server.js | 4 +- test/koa/test-server-formdata.js | 55 + test/koa/test-server-inheritance.js | 57 + ...t-server-with-options-more-than-1-error.js | 52 + test/koa/test-server-with-options.js | 63 + test/koa/test-simple-server-base-route.js | 47 + test/koa/test-simple-server-with-base-path.js | 50 + test/koa/test-simple-server-with-coercion.js | 51 + test/koa/test-simple-server.js | 43 + test/middleware-test.js | 4514 +++++++++-------- 23 files changed, 3284 insertions(+), 2262 deletions(-) create mode 100644 src/frameworks/express.js create mode 100644 src/frameworks/koa.js rename test/{ => express}/test-server-formdata.js (96%) rename test/{ => express}/test-server-inheritance.js (95%) rename test/{ => express}/test-server-with-options-more-than-1-error.js (95%) rename test/{ => express}/test-server-with-options.js (96%) rename test/{ => express}/test-simple-server-base-route.js (94%) rename test/{ => express}/test-simple-server-with-base-path.js (92%) rename test/{ => express}/test-simple-server-with-coercion.js (96%) rename test/{ => express}/test-simple-server.js (94%) create mode 100644 test/koa/test-server-formdata.js create mode 100644 test/koa/test-server-inheritance.js create mode 100644 test/koa/test-server-with-options-more-than-1-error.js create mode 100644 test/koa/test-server-with-options.js create mode 100644 test/koa/test-simple-server-base-route.js create mode 100644 test/koa/test-simple-server-with-base-path.js create mode 100644 test/koa/test-simple-server-with-coercion.js create mode 100644 test/koa/test-simple-server.js diff --git a/README.md b/README.md index d62de8d..243fb32 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ The function return Promise. ##### Options Options currently supports: +- `framework` - Defines in which framework the middleware is working ('koa' or 'express'). As default, set to 'express'. - `formats` - Array of formats that can be added to `ajv` configuration, each element in the array should include `name` and `pattern`. - `beautifyErrors`- Boolean that indicates if to beautify the errors, in this case it will create a string from the Ajv error. - Examples: @@ -89,7 +90,7 @@ formats: [ ``` ## Usage Example - +### Express ```js swaggerValidator.init('test/unit-tests/input-validation/pet-store-swagger.yaml') .then(function () { @@ -115,11 +116,45 @@ swaggerValidator.init('test/unit-tests/input-validation/pet-store-swagger.yaml') }); }); ``` +### Koa +```js +'use strict'; +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); +app.use(bodyParser()); +app.use(router.routes()); +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + return Promise.resolve(app); + }); +``` ## Important Notes - Objects - it is important to set any objects with the property `type: object` inside your swagger file, although it isn't a must in the Swagger (OpenAPI) spec in order to validate it accurately with [ajv](https://www.npmjs.com/package/ajv) it must be marked as `object` - multipart/form-data (files) supports is based on [`express/multer`](https://github.com/expressjs/multer) +- koa support - When using this package as middleware for koa, the validations errors are being thrown. +- koa packages - This package supports koa server that uses [`koa-router`](https://www.npmjs.com/package/koa-router), [`koa-bodyparser`](https://www.npmjs.com/package/koa-bodyparser) and [`koa-multer`](https://www.npmjs.com/package/koa-multer) ## Running Tests Using mocha, istanbul and mochawesome diff --git a/package-lock.json b/package-lock.json index d4e2b3c..2d0df47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "dependencies": { "@babel/code-frame": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fcode-frame/-/code-frame-7.0.0.tgz", "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { @@ -15,7 +15,7 @@ }, "@babel/generator": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fgenerator/-/generator-7.2.2.tgz", "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", "dev": true, "requires": { @@ -28,7 +28,7 @@ "dependencies": { "jsesc": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "resolved": "http://npm.zooz.co:8083/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true } @@ -36,7 +36,7 @@ }, "@babel/helper-function-name": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-function-name/-/helper-function-name-7.1.0.tgz", "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { @@ -47,7 +47,7 @@ }, "@babel/helper-get-function-arity": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, "requires": { @@ -56,7 +56,7 @@ }, "@babel/helper-split-export-declaration": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", "dev": true, "requires": { @@ -65,7 +65,7 @@ }, "@babel/highlight": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhighlight/-/highlight-7.0.0.tgz", "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { @@ -76,7 +76,7 @@ "dependencies": { "js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true } @@ -84,13 +84,13 @@ }, "@babel/parser": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fparser/-/parser-7.2.3.tgz", "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==", "dev": true }, "@babel/template": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftemplate/-/template-7.2.2.tgz", "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", "dev": true, "requires": { @@ -101,7 +101,7 @@ }, "@babel/traverse": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftraverse/-/traverse-7.2.3.tgz", "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", "dev": true, "requires": { @@ -118,7 +118,7 @@ "dependencies": { "debug": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "resolved": "http://npm.zooz.co:8083/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { @@ -127,13 +127,13 @@ }, "globals": { "version": "11.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "resolved": "http://npm.zooz.co:8083/globals/-/globals-11.10.0.tgz", "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", "dev": true }, "ms": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "resolved": "http://npm.zooz.co:8083/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } @@ -141,7 +141,7 @@ }, "@babel/types": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftypes/-/types-7.2.2.tgz", "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==", "dev": true, "requires": { @@ -152,7 +152,7 @@ "dependencies": { "to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true } @@ -220,6 +220,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "http://npm.zooz.co:8083/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "append-field": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", @@ -444,6 +450,33 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, + "cache-content-type": { + "version": "1.0.1", + "resolved": "http://npm.zooz.co:8083/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dev": true, + "requires": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "dependencies": { + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + } + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -572,6 +605,96 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, + "co-body": { + "version": "6.0.0", + "resolved": "http://npm.zooz.co:8083/co-body/-/co-body-6.0.0.tgz", + "integrity": "sha512-9ZIcixguuuKIptnY8yemEOuhb71L/lLf+Rl5JfJEUiDNJk0e02MBt7BPxR2GEh5mw8dPthQYR4jPI/BnS1MQgw==", + "dev": true, + "requires": { + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "http://npm.zooz.co:8083/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "qs": { + "version": "6.6.0", + "resolved": "http://npm.zooz.co:8083/qs/-/qs-6.6.0.tgz", + "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "http://npm.zooz.co:8083/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "http://npm.zooz.co:8083/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + } + } + }, "color-convert": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", @@ -660,6 +783,30 @@ "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", "dev": true }, + "cookies": { + "version": "0.7.3", + "resolved": "http://npm.zooz.co:8083/cookies/-/cookies-0.7.3.tgz", + "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "keygrip": "~1.0.3" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + } + } + }, + "copy-to": { + "version": "2.0.1", + "resolved": "http://npm.zooz.co:8083/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -716,6 +863,12 @@ "type-detect": "^4.0.0" } }, + "deep-equal": { + "version": "1.0.1", + "resolved": "http://npm.zooz.co:8083/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -743,6 +896,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", @@ -839,6 +998,12 @@ "is-arrayish": "^0.2.1" } }, + "error-inject": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/error-inject/-/error-inject-1.0.0.tgz", + "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1446,6 +1611,49 @@ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, + "http-assert": { + "version": "1.4.0", + "resolved": "http://npm.zooz.co:8083/http-assert/-/http-assert-1.4.0.tgz", + "integrity": "sha512-tPVv62a6l3BbQoM/N5qo969l0OFxqpnQzNUPeYfTP6Spo4zkgWeDBD1D5thI7sDLg7jCCihXTLB0X8UtdyAy8A==", + "dev": true, + "requires": { + "deep-equal": "~1.0.1", + "http-errors": "~1.7.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.1", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "http://npm.zooz.co:8083/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } + } + }, "http-errors": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", @@ -1487,6 +1695,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "inflation": { + "version": "2.0.0", + "resolved": "http://npm.zooz.co:8083/inflation/-/inflation-2.0.0.tgz", + "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1552,6 +1766,12 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "http://npm.zooz.co:8083/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "dev": true + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -1704,6 +1924,193 @@ "integrity": "sha512-IIG0FXHB/XpUZ7vGbktoc2EGsF+fLHJ1tU+vaqoKkVRBwH2FDxLTmkGkSp0XHRp6Y3KGZPIldH1YW8lOluGYrA==", "dev": true }, + "keygrip": { + "version": "1.0.3", + "resolved": "http://npm.zooz.co:8083/keygrip/-/keygrip-1.0.3.tgz", + "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==", + "dev": true + }, + "koa": { + "version": "2.6.2", + "resolved": "http://npm.zooz.co:8083/koa/-/koa-2.6.2.tgz", + "integrity": "sha512-KdnBFhTgh9ysMMoYe4J4fLvaKjT7mF3nRYV8MjxLzx6qywFNeptqi4xevyUltg1fZl2CFJ+HeLXuCGx07Yvl/A==", + "dev": true, + "requires": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.7.1", + "debug": "~3.1.0", + "delegates": "^1.0.0", + "depd": "^1.1.2", + "destroy": "^1.0.4", + "error-inject": "^1.0.0", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^1.2.0", + "koa-is-json": "^1.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "http://npm.zooz.co:8083/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.1", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "koa-compose": { + "version": "4.1.0", + "resolved": "http://npm.zooz.co:8083/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "dev": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "http://npm.zooz.co:8083/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "http://npm.zooz.co:8083/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + } + } + }, + "koa-bodyparser": { + "version": "4.2.1", + "resolved": "http://npm.zooz.co:8083/koa-bodyparser/-/koa-bodyparser-4.2.1.tgz", + "integrity": "sha512-UIjPAlMZfNYDDe+4zBaOAUKYqkwAGcIU6r2ARf1UOXPAlfennQys5IiShaVeNf7KkVBlf88f2LeLvBFvKylttw==", + "dev": true, + "requires": { + "co-body": "^6.0.0", + "copy-to": "^2.0.1" + } + }, + "koa-compose": { + "version": "3.2.1", + "resolved": "http://npm.zooz.co:8083/koa-compose/-/koa-compose-3.2.1.tgz", + "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, + "koa-convert": { + "version": "1.2.0", + "resolved": "http://npm.zooz.co:8083/koa-convert/-/koa-convert-1.2.0.tgz", + "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", + "dev": true, + "requires": { + "co": "^4.6.0", + "koa-compose": "^3.0.0" + } + }, + "koa-is-json": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/koa-is-json/-/koa-is-json-1.0.0.tgz", + "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=", + "dev": true + }, + "koa-multer": { + "version": "1.0.2", + "resolved": "http://npm.zooz.co:8083/koa-multer/-/koa-multer-1.0.2.tgz", + "integrity": "sha512-0kFzN4atVd+9oiG+4fYxQ9S2T3dPhKNvmhITIY606Qn9wLEmfhW0DhSpOzRYhddN//4rh/TCK95TMtflmFa5lA==", + "dev": true, + "requires": { + "multer": "1.3.0" + } + }, + "koa-router": { + "version": "7.4.0", + "resolved": "http://npm.zooz.co:8083/koa-router/-/koa-router-7.4.0.tgz", + "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "http-errors": "^1.3.1", + "koa-compose": "^3.0.0", + "methods": "^1.0.1", + "path-to-regexp": "^1.1.1", + "urijs": "^1.19.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "http://npm.zooz.co:8083/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "http://npm.zooz.co:8083/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "lcov-parse": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", @@ -1997,7 +2404,7 @@ }, "nyc": { "version": "13.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-13.1.0.tgz", + "resolved": "http://npm.zooz.co:8083/nyc/-/nyc-13.1.0.tgz", "integrity": "sha512-3GyY6TpQ58z9Frpv4GMExE1SV2tAgYqC7HSy2omEhNiCT3mhT9NyiOvIE8zkbuJVFzmvvNTnE4h/7/wQae7xLg==", "dev": true, "requires": { @@ -2396,7 +2803,7 @@ }, "istanbul-lib-instrument": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz", "integrity": "sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==", "dev": true, "requires": { @@ -3188,6 +3595,12 @@ "mimic-fn": "^1.0.0" } }, + "only": { + "version": "0.0.2", + "resolved": "http://npm.zooz.co:8083/only/-/only-0.0.2.tgz", + "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", + "dev": true + }, "ono": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.2.tgz", @@ -4020,6 +4433,12 @@ "os-tmpdir": "~1.0.2" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -4095,6 +4514,12 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "urijs": { + "version": "1.19.1", + "resolved": "http://npm.zooz.co:8083/urijs/-/urijs-1.19.1.tgz", + "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4181,6 +4606,12 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "ylru": { + "version": "1.2.1", + "resolved": "http://npm.zooz.co:8083/ylru/-/ylru-1.2.1.tgz", + "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", + "dev": true + }, "z-schema": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.19.0.tgz", diff --git a/package.json b/package.json index d369b33..e735c5b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "open api", "input validation", "validation", - "validator" + "validator", + "koa" ], "bugs": { "url": "https://github.com/Zooz/express-ajv-swagger-validation" @@ -66,6 +67,10 @@ "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", "express": "^4.16.2", + "koa": "^2.6.2", + "koa-bodyparser": "^4.2.1", + "koa-multer": "^1.0.2", + "koa-router": "^7.4.0", "mocha": "^4.0.1", "multer": "^1.3.0", "nyc": "^13.1.0", diff --git a/src/frameworks/express.js b/src/frameworks/express.js new file mode 100644 index 0000000..7fe0021 --- /dev/null +++ b/src/frameworks/express.js @@ -0,0 +1,24 @@ +function _getParameters(req) { + let requestOptions = {}; + let path = req.baseUrl.concat(req.route.path); + requestOptions.path = path.endsWith('/') ? path.substring(0, path.length - 1) : path; + requestOptions.headers = req.headers; + requestOptions.params = req.params; + requestOptions.query = req.query; + requestOptions.files = req.files; + requestOptions.method = req.method; + requestOptions.body = req.body; + + return requestOptions; +} + +async function validate(validateRequest, req, res, next) { + let requestOptions, errors; + requestOptions = _getParameters(req); + errors = await validateRequest(requestOptions); + next(errors); +} + +module.exports = { + validate: validate +}; diff --git a/src/frameworks/koa.js b/src/frameworks/koa.js new file mode 100644 index 0000000..d5f6bc1 --- /dev/null +++ b/src/frameworks/koa.js @@ -0,0 +1,27 @@ +function _getParameters(ctx) { + let requestOptions = {}; + let path = ctx._matchedRoute; + requestOptions.path = path.endsWith('/') ? path.substring(0, path.length - 1) : path; + requestOptions.headers = ctx.request.req.headers; + requestOptions.params = ctx.params; + requestOptions.query = ctx.query; + requestOptions.files = ctx.req.files; + requestOptions.method = ctx.req.method; + requestOptions.body = ctx.req.body || ctx.request.body; + + return requestOptions; +} + +async function validate(validateRequest, ctx, next) { + let requestOptions, errors; + requestOptions = _getParameters(ctx, next); + errors = await validateRequest(requestOptions); + if (errors) { + throw errors; + } + next(); +} + +module.exports = { + validate: validate +}; diff --git a/src/middleware.js b/src/middleware.js index 7539975..cebaff1 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -13,6 +13,7 @@ var schemas = {}; var middlewareOptions; var ajvConfigBody; var ajvConfigParams; +var framework; /** * Initialize the input validation middleware @@ -23,6 +24,7 @@ function init(swaggerPath, options) { middlewareOptions = options || {}; ajvConfigBody = middlewareOptions.ajvConfigBody || {}; ajvConfigParams = middlewareOptions.ajvConfigParams || {}; + framework = middlewareOptions.framework ? require(`./frameworks/${middlewareOptions.framework}`) : require('./frameworks/express'); const makeOptionalAttributesNullable = middlewareOptions.makeOptionalAttributesNullable || false; return Promise.all([ @@ -100,22 +102,23 @@ function _getValidatedBodySchema(bodySchema) { * @param {any} next * @returns In case of an error will call `next` with `InputValidationError` */ -function validate(req, res, next) { - let path = extractPath(req); +async function validate(...args) { + return framework.validate(_validateRequest, ...args); +} +function _validateRequest(requestOptions) { return Promise.all([ - _validateParams(req.headers, req.params, req.query, req.files, path, req.method.toLowerCase()).catch(e => e), - _validateBody(req.body, path, req.method.toLowerCase()).catch(e => e) + _validateParams(requestOptions.headers, requestOptions.params, requestOptions.query, requestOptions.files, requestOptions.path, requestOptions.method.toLowerCase()).catch(e => e), + _validateBody(requestOptions.body, requestOptions.path, requestOptions.method.toLowerCase()).catch(e => e) ]).then(function (errors) { if (errors[0] || errors[1]) { return errors[0] && errors[1] ? Promise.reject(errors[0].concat(errors[1])) : errors[0] ? Promise.reject(errors[0]) : Promise.reject(errors[1]); } - return next(); }).catch(function (errors) { - const error = new InputValidationError(errors, path, req.method.toLowerCase(), + const error = new InputValidationError(errors, requestOptions.path, requestOptions.method.toLowerCase(), { beautifyErrors: middlewareOptions.beautifyErrors, firstError: middlewareOptions.firstError }); - return next(error); + return Promise.resolve(error); }); } @@ -149,11 +152,6 @@ function addCustomKeyword(ajv, formats) { ajv.addKeyword('content', contentKeyword); } -function extractPath(req) { - let path = req.baseUrl.concat(req.route.path); - return path.endsWith('/') ? path.substring(0, path.length - 1) : path; -} - function buildBodyValidation(schema, swaggerDefinitions, originalSwagger, currentPath, currentMethod, parsedPath) { const defaultAjvOptions = { allErrors: true diff --git a/test/test-server-formdata.js b/test/express/test-server-formdata.js similarity index 96% rename from test/test-server-formdata.js rename to test/express/test-server-formdata.js index 3b2357b..04bebb1 100644 --- a/test/test-server-formdata.js +++ b/test/express/test-server-formdata.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var multer = require('multer'); var upload = multer(); diff --git a/test/test-server-inheritance.js b/test/express/test-server-inheritance.js similarity index 95% rename from test/test-server-inheritance.js rename to test/express/test-server-inheritance.js index 144771a..f4001fc 100644 --- a/test/test-server-inheritance.js +++ b/test/express/test-server-inheritance.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -37,4 +37,4 @@ module.exports = inputValidation.init('test/pet-store-swagger-inheritance.yaml', }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-server-with-options-more-than-1-error.js b/test/express/test-server-with-options-more-than-1-error.js similarity index 95% rename from test/test-server-with-options-more-than-1-error.js rename to test/express/test-server-with-options-more-than-1-error.js index 607251d..3912b52 100644 --- a/test/test-server-with-options-more-than-1-error.js +++ b/test/express/test-server-with-options-more-than-1-error.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -35,4 +35,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValida }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-server-with-options.js b/test/express/test-server-with-options.js similarity index 96% rename from test/test-server-with-options.js rename to test/express/test-server-with-options.js index 0030f54..1429267 100644 --- a/test/test-server-with-options.js +++ b/test/express/test-server-with-options.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -44,4 +44,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValida }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-base-route.js b/test/express/test-simple-server-base-route.js similarity index 94% rename from test/test-simple-server-base-route.js rename to test/express/test-simple-server-base-route.js index b4ddeab..4face09 100644 --- a/test/test-simple-server-base-route.js +++ b/test/express/test-simple-server-base-route.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var router = express.Router(); router.route('/').get(inputValidation.validate, function (req, res, next) { @@ -33,4 +33,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml') }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-with-base-path.js b/test/express/test-simple-server-with-base-path.js similarity index 92% rename from test/test-simple-server-with-base-path.js rename to test/express/test-simple-server-with-base-path.js index 6a77330..625d2c4 100644 --- a/test/test-simple-server-with-base-path.js +++ b/test/express/test-simple-server-with-base-path.js @@ -2,8 +2,8 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); -var router = require('./router'); +var inputValidation = require('../../src/middleware'); +var router = require('../router'); module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yaml', { contentTypeValidation: true }) .then(function () { @@ -31,4 +31,4 @@ module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yam }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-with-coercion.js b/test/express/test-simple-server-with-coercion.js similarity index 96% rename from test/test-simple-server-with-coercion.js rename to test/express/test-simple-server-with-coercion.js index 50510db..ace7d50 100644 --- a/test/test-simple-server-with-coercion.js +++ b/test/express/test-simple-server-with-coercion.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); module.exports = inputValidation.init('test/pet-store-swagger.yaml', { ajvConfigBody: { diff --git a/test/test-simple-server.js b/test/express/test-simple-server.js similarity index 94% rename from test/test-simple-server.js rename to test/express/test-simple-server.js index 049c1d2..531e6c1 100644 --- a/test/test-simple-server.js +++ b/test/express/test-simple-server.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); module.exports = inputValidation.init('test/pet-store-swagger.yaml') .then(function () { @@ -27,4 +27,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml') }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/koa/test-server-formdata.js b/test/koa/test-server-formdata.js new file mode 100644 index 0000000..fbfe063 --- /dev/null +++ b/test/koa/test-server-formdata.js @@ -0,0 +1,55 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +const multer = require('koa-multer'); +const upload = multer(); + +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ }, + { name: 'file', validate: () => { return true } } + ], + beautifyErrors: true, + firstError: true, + expectFormFieldsInBody: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/form-data-swagger.yaml', inputValidationOptions) + .then(function () { + router.post('/pets/import', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/kennels/import', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/login', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-inheritance.js b/test/koa/test-server-inheritance.js new file mode 100644 index 0000000..486f735 --- /dev/null +++ b/test/koa/test-server-inheritance.js @@ -0,0 +1,57 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ } + ], + beautifyErrors: true, + firstError: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger-inheritance.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/:version/pets/:petId', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-with-options-more-than-1-error.js b/test/koa/test-server-with-options-more-than-1-error.js new file mode 100644 index 0000000..d834fff --- /dev/null +++ b/test/koa/test-server-with-options-more-than-1-error.js @@ -0,0 +1,52 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: err.errors}; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,18}$/ } + ], + beautifyErrors: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-with-options.js b/test/koa/test-server-with-options.js new file mode 100644 index 0000000..6821403 --- /dev/null +++ b/test/koa/test-server-with-options.js @@ -0,0 +1,63 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: err.errors }; + } + } +}); +app.use(bodyParser({enableTypes: ['text', 'json']})); +app.use(router.routes()); + +let inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ } + ], + beautifyErrors: true, + firstError: true, + contentTypeValidation: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets/:petId', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/text', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-base-route.js b/test/koa/test-simple-server-base-route.js new file mode 100644 index 0000000..0aa7f7b --- /dev/null +++ b/test/koa/test-simple-server-base-route.js @@ -0,0 +1,47 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router({ + prefix: '/pets' +}); +let router1 = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); +app.use(router1.routes()); + +router.get('/', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router.post('/', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router.get('/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router1.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-with-base-path.js b/test/koa/test-simple-server-with-base-path.js new file mode 100644 index 0000000..1f9414e --- /dev/null +++ b/test/koa/test-simple-server-with-base-path.js @@ -0,0 +1,50 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router({ + prefix: '/v1' +}); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yaml', {framework: 'koa', contentTypeValidation: true}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + router.get('/capital', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-with-coercion.js b/test/koa/test-simple-server-with-coercion.js new file mode 100644 index 0000000..9cbfea9 --- /dev/null +++ b/test/koa/test-simple-server-with-coercion.js @@ -0,0 +1,51 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', { + framework: 'koa', + ajvConfigBody: { + coerceTypes: true }, + makeOptionalAttributesNullable: true}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + router.patch('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server.js b/test/koa/test-simple-server.js new file mode 100644 index 0000000..d52a655 --- /dev/null +++ b/test/koa/test-simple-server.js @@ -0,0 +1,43 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/middleware-test.js b/test/middleware-test.js index 49eb13c..0dbb25b 100644 --- a/test/middleware-test.js +++ b/test/middleware-test.js @@ -28,2293 +28,2377 @@ describe('input-validation middleware tests', function () { }); }); }); - describe('Simple server - no options', function () { - var app; - before(function () { - return require('./test-simple-server').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); + let servers = [{framework: 'koa'}, {framework: 'express'}]; + + servers.forEach(server => { + describe(`Simple server - no options ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-simple-server`).then(function (testServer) { + app = testServer; }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ name: 'name', tag: 'tag', test: { - field1: 'enum1' + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; } - }, - { + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ name: 'name', tag: 'tag', test: { field1: 1234 } }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); + describe(`Simple server - type coercion enabled ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-simple-server-with-coercion`).then(function (testServer) { + app = testServer; }); - }); - }); - describe('Simple server - type coercion enabled', function () { - var app; - before(function () { - return require('./test-simple-server-with-coercion').then(function (testServer) { - app = testServer; }); - }); - it('request with wrong parameter type - should pass validation due to coercion', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, done); - }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('request with wrong parameter type - should pass validation due to coercion', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, done); + }); - it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); + it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); - it('handles request body objects without specified schema correctly', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); + it('handles request body objects without specified schema correctly', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); - it('handles request body without specified schema correctly', function (done) { - request(app) - .patch('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field1).to.equal('enum1'); - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); + it('handles request body without specified schema correctly', function (done) { + request(app) + .patch('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field1).to.equal('enum1'); + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); - it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); + it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); - it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test3: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test3.field1).to.equal('enum1'); - expect(pet.test3.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); + it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test3: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test3.field1).to.equal('enum1'); + expect(pet.test3.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); - it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1', - field3: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field3).to.be.null; - done(); - }); - }); - }); - describe('Simple server - with base path', function () { - var app; - before(function () { - return require('./test-simple-server-with-base-path').then(function (testServer) { - app = testServer; + it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1', + field3: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field3).to.be.null; + done(); + }); }); }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/v1/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/v1/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('content-type must be one of application/json'); - done(); - }); - }); - it('headers are in capital letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('Capital-Letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('headers are in lowercase letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('capital-letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); + describe(`Simple server - with base path ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-simple-server-with-base-path`).then(function (testServer) { + app = testServer; }); + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/v1/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/v1/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('content-type must be one of application/json'); + done(); + }); + }); + it('headers are in capital letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('Capital-Letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('headers are in lowercase letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('capital-letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/v1/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/v1/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/v1/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/v1/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); }); - it('bad query param - under limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); + describe(`Simple server using routes ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-simple-server-base-route`).then(function (testServer) { + app = testServer; }); + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/v1/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); + describe(`Server with options - beautify and one error ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-server-with-options`).then(function (testServer) { + app = testServer; }); + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body should be array'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); + done(); + }); + }); + it('valid content-type when multiple content-types defined - should pass validation', function (done) { + request(app) + .put('/text') + .set('content-type', 'text/plain') + .send('text') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('more detailed content-type - should pass validation', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/json; charset=utf-8') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('valid empty request - should pass validation', function (done) { + request(app) + .put('/pets/1234') + .set('request-id', '1234') + .set('api-version', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/v1/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); + describe(`Server with options - Only beautify errors ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-server-with-options-more-than-1-error`).then(function (testServer) { + app = testServer; }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/v1/pets') - .send([ - { + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body & bad header', function (done) { + request(app) + .post('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: 'enum1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info.length).to.equal(2); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + expect(res.body.more_info[1]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ name: 'name', tag: 'tag', - test: { - field1: 'enum1' + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; } - }, - { + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute (more than one error)', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ name: 'name', tag: 'tag', test: { field1: 1234 } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/v1/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Simple server using routes', function () { - var app; - before(function () { - return require('./test-simple-server-base-route').then(function (testServer) { - app = testServer; + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); + expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ name: 'name', tag: 'tag', test: { - field1: 'enum1' + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; } - }, - { + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info).to.includes('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ name: 'name', tag: 'tag', test: { field1: 1234 } }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Server with options - beautify and one error', function () { - var app; - before(function () { - return require('./test-server-with-options').then(function (testServer) { - app = testServer; + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should be array'); + done(); + }); }); }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); - done(); + describe(`Inheritance ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-server-inheritance`).then(function (testServer) { + app = testServer; }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('should pass', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + packSize: 3 + }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('wrong value for header with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '2.0') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); + done(); + }); + }); + it('wrong value for query with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .query({ PetType: 'bird' }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); + done(); + }); + }); + it('missing header with enum definition', function (done) { + request(app) + .get('/pets') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('wrong value for path param with enum definition', function (done) { + request(app) + .get('/v2/pets/12345') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); + done(); + }); + }); + it('should fail for wrong value in discriminator', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'dog', name: 'name', tag: 'tag', test: { - field1: 'enum1' + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; } - }, - { + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing discriminator key', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ name: 'name', tag: 'tag', test: { - field1: 1234 + field1: '1234' } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body should be array'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); - done(); - }); - }); - it('valid content-type when multiple content-types defind - should pass validation', function (done) { - request(app) - .put('/text') - .set('content-type', 'text/plain') - .send('text') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('more detailed content-type - should pass validation', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/json; charset=utf-8') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('valid empty request - should pass validation', function (done) { - request(app) - .put('/pets/1234') - .set('request-id', '1234') - .set('api-version', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - }); - describe('Server with options - Only beautify errors', function () { - var app; - before(function () { - return require('./test-server-with-options-more-than-1-error').then(function (testServer) { - app = testServer; + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body & bad header', function (done) { - request(app) - .post('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: 'enum1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info.length).to.equal(2); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - expect(res.body.more_info[1]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute (more than one error)', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); - expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info).to.includes('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { + it('should fail for missing attribute in inherited object (Dog)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', name: 'name', tag: 'tag', test: { - field1: 'enum1' + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; } - }, - { + expect(res.body.more_info).to.includes('body should have required property \'packSize\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (cat)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Cat', name: 'name', tag: 'tag', test: { - field1: 1234 + field1: '1234' } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should be array'); - done(); - }); - }); - }); - describe('Inheritance', function () { - var app; - before(function () { - return require('./test-server-inheritance').then(function (testServer) { - app = testServer; + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); + done(); + }); }); - }); - it('should pass', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - packSize: 3 - }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('wrong value for header with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '2.0') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); - done(); - }); - }); - it('wrong value for query with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .query({ PetType: 'bird' }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); - done(); - }); - }); - it('missing header with enum definition', function (done) { - request(app) - .get('/pets') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('wrong value for path param with enum definition', function (done) { - request(app) - .get('/v2/pets/12345') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); - done(); - }); - }); - it('should fail for wrong value in discriminator', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing discriminator key', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing attribute in inherited object (Dog)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'packSize\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (cat)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Cat', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (parent)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - tag: 'tag', - chip_number: '123454' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'name\''); - done(); - }); - }); - }); - describe('FormData', function () { - var app; - before(function () { - return require('./test-server-formdata').then(function (testServer) { - app = testServer; + it('should fail for missing attribute in inherited object (parent)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + tag: 'tag', + chip_number: '123454' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'name\''); + done(); + }); }); }); - it('only required files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('required and optional files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .attach('optionalFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing required file should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); - done(); - }); - }); - it('extra files exists but not allowed should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .attach('sourceFile', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); - done(); - }); - }); - it('supports string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .field('password', 'pass') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('supports mix of files and fields', function (done) { - request(app) - .post('/kennels/import') - .set('api-version', '1.0') - .field('name', 'kennel 1 ') - .attach('blueprintFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('validates string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'password\''); - done(); + describe(`FormData ${server.framework}`, function () { + var app; + before(function () { + return require(`./${server.framework}/test-server-formdata`).then(function (testServer) { + app = testServer; }); + }); + beforeEach(function (){ + if (server.framework === 'koa') { + app = app.listen(8888); + } + }); + afterEach(function () { + if (server.framework === 'koa') { + app.close(); + } + }); + it('only required files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('required and optional files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .attach('optionalFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing required file should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); + done(); + }); + }); + it('extra files exists but not allowed should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .attach('sourceFile', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); + done(); + }); + }); + it('supports string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .field('password', 'pass') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('supports mix of files and fields', function (done) { + request(app) + .post('/kennels/import') + .set('api-version', '1.0') + .field('name', 'kennel 1 ') + .attach('blueprintFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('validates string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'password\''); + done(); + }); + }); }); }); }); From aad1c3eb366358a18dc54b3534a9d118b5ec5fd3 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Mon, 21 Jan 2019 15:58:27 +0200 Subject: [PATCH 2/9] Seperating koa tests from express tests --- test/express/middleware-test.js | 2314 +++++++++++++++++++++++++++++ test/koa/middleware-test.js | 2369 ++++++++++++++++++++++++++++++ test/middleware-test.js | 2404 ------------------------------- 3 files changed, 4683 insertions(+), 2404 deletions(-) create mode 100644 test/express/middleware-test.js create mode 100644 test/koa/middleware-test.js delete mode 100644 test/middleware-test.js diff --git a/test/express/middleware-test.js b/test/express/middleware-test.js new file mode 100644 index 0000000..cc81700 --- /dev/null +++ b/test/express/middleware-test.js @@ -0,0 +1,2314 @@ +'use strict'; + +var chai = require('chai'), + expect = chai.expect, + sinon = require('sinon'), + chaiSinon = require('chai-sinon'), + request = require('supertest'); +chai.use(chaiSinon); + +describe('input-validation middleware tests - Express', function () { + describe('init function tests', function () { + it('should reject the promise in case the file doesn\'t exists', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + return middleware.init('test/pet-store-swagger1.yaml') + .catch(function (err) { + expect(err).to.exist; + }); + }); + it('should resolve without formats', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + let addCustomKeyword = middleware.__get__('addCustomKeyword'); + let addCustomKeywordSpy = sinon.spy(addCustomKeyword); + return middleware.init('test/pet-store-swagger.yaml') + .then(function () { + expect(addCustomKeywordSpy).to.have.not.been.called; + }); + }); + }); + describe('Simple server - no options', function () { + var app; + before(function () { + return require('./test-simple-server').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server - type coercion enabled', function () { + var app; + before(function () { + return require('./test-simple-server-with-coercion').then(function (testServer) { + app = testServer; + }); + }); + it('request with wrong parameter type - should pass validation due to coercion', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, done); + }); + it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('handles request body objects without specified schema correctly', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + it('handles request body without specified schema correctly', function (done) { + request(app) + .patch('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field1).to.equal('enum1'); + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test3: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test3.field1).to.equal('enum1'); + expect(pet.test3.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1', + field3: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field3).to.be.null; + done(); + }); + }); + }); + describe('Simple server - with base path', function () { + var app; + before(function () { + return require('./test-simple-server-with-base-path').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/v1/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/v1/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('content-type must be one of application/json'); + done(); + }); + }); + it('headers are in capital letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('Capital-Letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('headers are in lowercase letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('capital-letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/v1/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/v1/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/v1/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/v1/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server using routes', function () { + var app; + before(function () { + return require('./test-simple-server-base-route').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Server with options - beautify and one error', function () { + var app; + before(function () { + return require('./test-server-with-options').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body should be array'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); + done(); + }); + }); + it('valid content-type when multiple content-types defined - should pass validation', function (done) { + request(app) + .put('/text') + .set('content-type', 'text/plain') + .send('text') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('more detailed content-type - should pass validation', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/json; charset=utf-8') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('valid empty request - should pass validation', function (done) { + request(app) + .put('/pets/1234') + .set('request-id', '1234') + .set('api-version', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + }); + describe('Server with options - Only beautify errors', function () { + var app; + before(function () { + return require('./test-server-with-options-more-than-1-error').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body & bad header', function (done) { + request(app) + .post('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: 'enum1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info.length).to.equal(2); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + expect(res.body.more_info[1]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute (more than one error)', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); + expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info).to.includes('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should be array'); + done(); + }); + }); + }); + describe('Inheritance', function () { + var app; + before(function () { + return require('./test-server-inheritance').then(function (testServer) { + app = testServer; + }); + }); + it('should pass', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + packSize: 3 + }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('wrong value for header with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '2.0') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); + done(); + }); + }); + it('wrong value for query with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .query({ PetType: 'bird' }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); + done(); + }); + }); + it('missing header with enum definition', function (done) { + request(app) + .get('/pets') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('wrong value for path param with enum definition', function (done) { + request(app) + .get('/v2/pets/12345') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); + done(); + }); + }); + it('should fail for wrong value in discriminator', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing discriminator key', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing attribute in inherited object (Dog)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'packSize\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (cat)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Cat', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (parent)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + tag: 'tag', + chip_number: '123454' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'name\''); + done(); + }); + }); + }); + describe('FormData', function () { + var app; + before(function () { + return require('./test-server-formdata').then(function (testServer) { + app = testServer; + }); + }); + it('only required files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('required and optional files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .attach('optionalFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing required file should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); + done(); + }); + }); + it('extra files exists but not allowed should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .attach('sourceFile', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); + done(); + }); + }); + it('supports string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .field('password', 'pass') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('supports mix of files and fields', function (done) { + request(app) + .post('/kennels/import') + .set('api-version', '1.0') + .field('name', 'kennel 1 ') + .attach('blueprintFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('validates string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'password\''); + done(); + }); + }); + }); +}); diff --git a/test/koa/middleware-test.js b/test/koa/middleware-test.js new file mode 100644 index 0000000..140be27 --- /dev/null +++ b/test/koa/middleware-test.js @@ -0,0 +1,2369 @@ +'use strict'; + +var chai = require('chai'), + expect = chai.expect, + sinon = require('sinon'), + chaiSinon = require('chai-sinon'), + request = require('supertest'); +chai.use(chaiSinon); + +describe('input-validation middleware tests - Koa', function () { + describe('init function tests', function () { + it('should reject the promise in case the file doesn\'t exists', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + return middleware.init('test/pet-store-swagger1.yaml') + .catch(function (err) { + expect(err).to.exist; + }); + }); + it('should resolve without formats', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + let addCustomKeyword = middleware.__get__('addCustomKeyword'); + let addCustomKeywordSpy = sinon.spy(addCustomKeyword); + return middleware.init('test/pet-store-swagger.yaml') + .then(function () { + expect(addCustomKeywordSpy).to.have.not.been.called; + }); + }); + }); + + describe('Simple server - no options', function () { + var app; + before(function () { + return require('./test-simple-server').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server - type coercion enabled', function () { + var app; + before(function () { + return require('./test-simple-server-with-coercion').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('request with wrong parameter type - should pass validation due to coercion', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, done); + }); + + it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('handles request body objects without specified schema correctly', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + + it('handles request body without specified schema correctly', function (done) { + request(app) + .patch('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field1).to.equal('enum1'); + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test3: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test3.field1).to.equal('enum1'); + expect(pet.test3.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1', + field3: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field3).to.be.null; + done(); + }); + }); + }); + describe('Simple server - with base path', function () { + var app; + before(function () { + return require('./test-simple-server-with-base-path').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/v1/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/v1/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('content-type must be one of application/json'); + done(); + }); + }); + it('headers are in capital letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('Capital-Letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('headers are in lowercase letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('capital-letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/v1/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/v1/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/v1/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/v1/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server using routes', function () { + var app; + before(function () { + return require('./test-simple-server-base-route').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Server with options - beautify and one error', function () { + var app; + before(function () { + return require('./test-server-with-options').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body should be array'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); + done(); + }); + }); + it('valid content-type when multiple content-types defined - should pass validation', function (done) { + request(app) + .put('/text') + .set('content-type', 'text/plain') + .send('text') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('more detailed content-type - should pass validation', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/json; charset=utf-8') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('valid empty request - should pass validation', function (done) { + request(app) + .put('/pets/1234') + .set('request-id', '1234') + .set('api-version', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + }); + describe('Server with options - Only beautify errors', function () { + var app; + before(function () { + return require('./test-server-with-options-more-than-1-error').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body & bad header', function (done) { + request(app) + .post('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: 'enum1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info.length).to.equal(2); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + expect(res.body.more_info[1]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute (more than one error)', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); + expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info).to.includes('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should be array'); + done(); + }); + }); + }); + describe('Inheritance', function () { + var app; + before(function () { + return require('./test-server-inheritance').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('should pass', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + packSize: 3 + }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('wrong value for header with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '2.0') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); + done(); + }); + }); + it('wrong value for query with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .query({ PetType: 'bird' }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); + done(); + }); + }); + it('missing header with enum definition', function (done) { + request(app) + .get('/pets') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('wrong value for path param with enum definition', function (done) { + request(app) + .get('/v2/pets/12345') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); + done(); + }); + }); + it('should fail for wrong value in discriminator', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing discriminator key', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing attribute in inherited object (Dog)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'packSize\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (cat)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Cat', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (parent)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + tag: 'tag', + chip_number: '123454' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'name\''); + done(); + }); + }); + }); + describe('FormData', function () { + var app; + before(function () { + return require('./test-server-formdata').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('only required files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('required and optional files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .attach('optionalFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing required file should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); + done(); + }); + }); + it('extra files exists but not allowed should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .attach('sourceFile', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); + done(); + }); + }); + it('supports string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .field('password', 'pass') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('supports mix of files and fields', function (done) { + request(app) + .post('/kennels/import') + .set('api-version', '1.0') + .field('name', 'kennel 1 ') + .attach('blueprintFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('validates string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'password\''); + done(); + }); + }); + }); +}); diff --git a/test/middleware-test.js b/test/middleware-test.js deleted file mode 100644 index 0dbb25b..0000000 --- a/test/middleware-test.js +++ /dev/null @@ -1,2404 +0,0 @@ -'use strict'; - -var chai = require('chai'), - expect = chai.expect, - sinon = require('sinon'), - chaiSinon = require('chai-sinon'), - request = require('supertest'); -chai.use(chaiSinon); - -describe('input-validation middleware tests', function () { - describe('init function tests', function () { - it('should reject the promise in case the file doesn\'t exists', function () { - let rewire = require('rewire'); - let middleware = rewire('../src/middleware'); - return middleware.init('test/pet-store-swagger1.yaml') - .catch(function (err) { - expect(err).to.exist; - }); - }); - it('should resolve without formats', function () { - let rewire = require('rewire'); - let middleware = rewire('../src/middleware'); - let addCustomKeyword = middleware.__get__('addCustomKeyword'); - let addCustomKeywordSpy = sinon.spy(addCustomKeyword); - return middleware.init('test/pet-store-swagger.yaml') - .then(function () { - expect(addCustomKeywordSpy).to.have.not.been.called; - }); - }); - }); - let servers = [{framework: 'koa'}, {framework: 'express'}]; - - servers.forEach(server => { - describe(`Simple server - no options ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-simple-server`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe(`Simple server - type coercion enabled ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-simple-server-with-coercion`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('request with wrong parameter type - should pass validation due to coercion', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, done); - }); - - it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('handles request body objects without specified schema correctly', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); - - it('handles request body without specified schema correctly', function (done) { - request(app) - .patch('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field1).to.equal('enum1'); - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test3: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test3.field1).to.equal('enum1'); - expect(pet.test3.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1', - field3: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field3).to.be.null; - done(); - }); - }); - }); - describe(`Simple server - with base path ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-simple-server-with-base-path`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/v1/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/v1/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('content-type must be one of application/json'); - done(); - }); - }); - it('headers are in capital letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('Capital-Letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('headers are in lowercase letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('capital-letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/v1/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/v1/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/v1/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/v1/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe(`Simple server using routes ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-simple-server-base-route`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe(`Server with options - beautify and one error ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-server-with-options`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body should be array'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); - done(); - }); - }); - it('valid content-type when multiple content-types defined - should pass validation', function (done) { - request(app) - .put('/text') - .set('content-type', 'text/plain') - .send('text') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('more detailed content-type - should pass validation', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/json; charset=utf-8') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('valid empty request - should pass validation', function (done) { - request(app) - .put('/pets/1234') - .set('request-id', '1234') - .set('api-version', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - }); - describe(`Server with options - Only beautify errors ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-server-with-options-more-than-1-error`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body & bad header', function (done) { - request(app) - .post('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: 'enum1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info.length).to.equal(2); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - expect(res.body.more_info[1]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute (more than one error)', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); - expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info).to.includes('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should be array'); - done(); - }); - }); - }); - describe(`Inheritance ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-server-inheritance`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('should pass', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - packSize: 3 - }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('wrong value for header with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '2.0') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); - done(); - }); - }); - it('wrong value for query with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .query({ PetType: 'bird' }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); - done(); - }); - }); - it('missing header with enum definition', function (done) { - request(app) - .get('/pets') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('wrong value for path param with enum definition', function (done) { - request(app) - .get('/v2/pets/12345') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); - done(); - }); - }); - it('should fail for wrong value in discriminator', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing discriminator key', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing attribute in inherited object (Dog)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'packSize\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (cat)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Cat', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (parent)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - tag: 'tag', - chip_number: '123454' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'name\''); - done(); - }); - }); - }); - describe(`FormData ${server.framework}`, function () { - var app; - before(function () { - return require(`./${server.framework}/test-server-formdata`).then(function (testServer) { - app = testServer; - }); - }); - beforeEach(function (){ - if (server.framework === 'koa') { - app = app.listen(8888); - } - }); - afterEach(function () { - if (server.framework === 'koa') { - app.close(); - } - }); - it('only required files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('required and optional files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .attach('optionalFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing required file should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); - done(); - }); - }); - it('extra files exists but not allowed should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .attach('sourceFile', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); - done(); - }); - }); - it('supports string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .field('password', 'pass') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('supports mix of files and fields', function (done) { - request(app) - .post('/kennels/import') - .set('api-version', '1.0') - .field('name', 'kennel 1 ') - .attach('blueprintFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('validates string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'password\''); - done(); - }); - }); - }); - }); -}); From 57003e097654d6d2d10375e4181768dd6749d391 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Tue, 15 Jan 2019 14:55:31 +0200 Subject: [PATCH 3/9] Add koa support --- README.md | 37 +- package-lock.json | 467 +++++++++++++++++- package.json | 7 +- src/frameworks/express.js | 24 + src/frameworks/koa.js | 27 + src/middleware.js | 22 +- test/{ => express}/test-server-formdata.js | 2 +- test/{ => express}/test-server-inheritance.js | 4 +- ...t-server-with-options-more-than-1-error.js | 4 +- .../{ => express}/test-server-with-options.js | 4 +- .../test-simple-server-base-route.js | 4 +- .../test-simple-server-with-base-path.js | 6 +- .../test-simple-server-with-coercion.js | 2 +- test/{ => express}/test-simple-server.js | 4 +- test/koa/test-server-formdata.js | 55 +++ test/koa/test-server-inheritance.js | 57 +++ ...t-server-with-options-more-than-1-error.js | 52 ++ test/koa/test-server-with-options.js | 63 +++ test/koa/test-simple-server-base-route.js | 47 ++ test/koa/test-simple-server-with-base-path.js | 50 ++ test/koa/test-simple-server-with-coercion.js | 51 ++ test/koa/test-simple-server.js | 43 ++ 22 files changed, 985 insertions(+), 47 deletions(-) create mode 100644 src/frameworks/express.js create mode 100644 src/frameworks/koa.js rename test/{ => express}/test-server-formdata.js (96%) rename test/{ => express}/test-server-inheritance.js (95%) rename test/{ => express}/test-server-with-options-more-than-1-error.js (95%) rename test/{ => express}/test-server-with-options.js (96%) rename test/{ => express}/test-simple-server-base-route.js (94%) rename test/{ => express}/test-simple-server-with-base-path.js (92%) rename test/{ => express}/test-simple-server-with-coercion.js (96%) rename test/{ => express}/test-simple-server.js (94%) create mode 100644 test/koa/test-server-formdata.js create mode 100644 test/koa/test-server-inheritance.js create mode 100644 test/koa/test-server-with-options-more-than-1-error.js create mode 100644 test/koa/test-server-with-options.js create mode 100644 test/koa/test-simple-server-base-route.js create mode 100644 test/koa/test-simple-server-with-base-path.js create mode 100644 test/koa/test-simple-server-with-coercion.js create mode 100644 test/koa/test-simple-server.js diff --git a/README.md b/README.md index d62de8d..243fb32 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ The function return Promise. ##### Options Options currently supports: +- `framework` - Defines in which framework the middleware is working ('koa' or 'express'). As default, set to 'express'. - `formats` - Array of formats that can be added to `ajv` configuration, each element in the array should include `name` and `pattern`. - `beautifyErrors`- Boolean that indicates if to beautify the errors, in this case it will create a string from the Ajv error. - Examples: @@ -89,7 +90,7 @@ formats: [ ``` ## Usage Example - +### Express ```js swaggerValidator.init('test/unit-tests/input-validation/pet-store-swagger.yaml') .then(function () { @@ -115,11 +116,45 @@ swaggerValidator.init('test/unit-tests/input-validation/pet-store-swagger.yaml') }); }); ``` +### Koa +```js +'use strict'; +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); +app.use(bodyParser()); +app.use(router.routes()); +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + return Promise.resolve(app); + }); +``` ## Important Notes - Objects - it is important to set any objects with the property `type: object` inside your swagger file, although it isn't a must in the Swagger (OpenAPI) spec in order to validate it accurately with [ajv](https://www.npmjs.com/package/ajv) it must be marked as `object` - multipart/form-data (files) supports is based on [`express/multer`](https://github.com/expressjs/multer) +- koa support - When using this package as middleware for koa, the validations errors are being thrown. +- koa packages - This package supports koa server that uses [`koa-router`](https://www.npmjs.com/package/koa-router), [`koa-bodyparser`](https://www.npmjs.com/package/koa-bodyparser) and [`koa-multer`](https://www.npmjs.com/package/koa-multer) ## Running Tests Using mocha, istanbul and mochawesome diff --git a/package-lock.json b/package-lock.json index d4e2b3c..2d0df47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "dependencies": { "@babel/code-frame": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fcode-frame/-/code-frame-7.0.0.tgz", "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { @@ -15,7 +15,7 @@ }, "@babel/generator": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fgenerator/-/generator-7.2.2.tgz", "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", "dev": true, "requires": { @@ -28,7 +28,7 @@ "dependencies": { "jsesc": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "resolved": "http://npm.zooz.co:8083/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true } @@ -36,7 +36,7 @@ }, "@babel/helper-function-name": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-function-name/-/helper-function-name-7.1.0.tgz", "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { @@ -47,7 +47,7 @@ }, "@babel/helper-get-function-arity": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, "requires": { @@ -56,7 +56,7 @@ }, "@babel/helper-split-export-declaration": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhelper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", "dev": true, "requires": { @@ -65,7 +65,7 @@ }, "@babel/highlight": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fhighlight/-/highlight-7.0.0.tgz", "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { @@ -76,7 +76,7 @@ "dependencies": { "js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true } @@ -84,13 +84,13 @@ }, "@babel/parser": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2fparser/-/parser-7.2.3.tgz", "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==", "dev": true }, "@babel/template": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftemplate/-/template-7.2.2.tgz", "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", "dev": true, "requires": { @@ -101,7 +101,7 @@ }, "@babel/traverse": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftraverse/-/traverse-7.2.3.tgz", "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", "dev": true, "requires": { @@ -118,7 +118,7 @@ "dependencies": { "debug": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "resolved": "http://npm.zooz.co:8083/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { @@ -127,13 +127,13 @@ }, "globals": { "version": "11.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "resolved": "http://npm.zooz.co:8083/globals/-/globals-11.10.0.tgz", "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", "dev": true }, "ms": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "resolved": "http://npm.zooz.co:8083/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } @@ -141,7 +141,7 @@ }, "@babel/types": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "resolved": "http://npm.zooz.co:8083/@babel%2ftypes/-/types-7.2.2.tgz", "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==", "dev": true, "requires": { @@ -152,7 +152,7 @@ "dependencies": { "to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true } @@ -220,6 +220,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "http://npm.zooz.co:8083/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "append-field": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", @@ -444,6 +450,33 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, + "cache-content-type": { + "version": "1.0.1", + "resolved": "http://npm.zooz.co:8083/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dev": true, + "requires": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "dependencies": { + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + } + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -572,6 +605,96 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, + "co-body": { + "version": "6.0.0", + "resolved": "http://npm.zooz.co:8083/co-body/-/co-body-6.0.0.tgz", + "integrity": "sha512-9ZIcixguuuKIptnY8yemEOuhb71L/lLf+Rl5JfJEUiDNJk0e02MBt7BPxR2GEh5mw8dPthQYR4jPI/BnS1MQgw==", + "dev": true, + "requires": { + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "http://npm.zooz.co:8083/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "qs": { + "version": "6.6.0", + "resolved": "http://npm.zooz.co:8083/qs/-/qs-6.6.0.tgz", + "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "http://npm.zooz.co:8083/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "http://npm.zooz.co:8083/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + } + } + }, "color-convert": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", @@ -660,6 +783,30 @@ "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", "dev": true }, + "cookies": { + "version": "0.7.3", + "resolved": "http://npm.zooz.co:8083/cookies/-/cookies-0.7.3.tgz", + "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "keygrip": "~1.0.3" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + } + } + }, + "copy-to": { + "version": "2.0.1", + "resolved": "http://npm.zooz.co:8083/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -716,6 +863,12 @@ "type-detect": "^4.0.0" } }, + "deep-equal": { + "version": "1.0.1", + "resolved": "http://npm.zooz.co:8083/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -743,6 +896,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", @@ -839,6 +998,12 @@ "is-arrayish": "^0.2.1" } }, + "error-inject": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/error-inject/-/error-inject-1.0.0.tgz", + "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1446,6 +1611,49 @@ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, + "http-assert": { + "version": "1.4.0", + "resolved": "http://npm.zooz.co:8083/http-assert/-/http-assert-1.4.0.tgz", + "integrity": "sha512-tPVv62a6l3BbQoM/N5qo969l0OFxqpnQzNUPeYfTP6Spo4zkgWeDBD1D5thI7sDLg7jCCihXTLB0X8UtdyAy8A==", + "dev": true, + "requires": { + "deep-equal": "~1.0.1", + "http-errors": "~1.7.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.1", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "http://npm.zooz.co:8083/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } + } + }, "http-errors": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", @@ -1487,6 +1695,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "inflation": { + "version": "2.0.0", + "resolved": "http://npm.zooz.co:8083/inflation/-/inflation-2.0.0.tgz", + "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1552,6 +1766,12 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "http://npm.zooz.co:8083/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "dev": true + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -1704,6 +1924,193 @@ "integrity": "sha512-IIG0FXHB/XpUZ7vGbktoc2EGsF+fLHJ1tU+vaqoKkVRBwH2FDxLTmkGkSp0XHRp6Y3KGZPIldH1YW8lOluGYrA==", "dev": true }, + "keygrip": { + "version": "1.0.3", + "resolved": "http://npm.zooz.co:8083/keygrip/-/keygrip-1.0.3.tgz", + "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==", + "dev": true + }, + "koa": { + "version": "2.6.2", + "resolved": "http://npm.zooz.co:8083/koa/-/koa-2.6.2.tgz", + "integrity": "sha512-KdnBFhTgh9ysMMoYe4J4fLvaKjT7mF3nRYV8MjxLzx6qywFNeptqi4xevyUltg1fZl2CFJ+HeLXuCGx07Yvl/A==", + "dev": true, + "requires": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.7.1", + "debug": "~3.1.0", + "delegates": "^1.0.0", + "depd": "^1.1.2", + "destroy": "^1.0.4", + "error-inject": "^1.0.0", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^1.2.0", + "koa-is-json": "^1.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "http://npm.zooz.co:8083/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "http://npm.zooz.co:8083/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.1", + "resolved": "http://npm.zooz.co:8083/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "koa-compose": { + "version": "4.1.0", + "resolved": "http://npm.zooz.co:8083/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "dev": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "http://npm.zooz.co:8083/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://npm.zooz.co:8083/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "http://npm.zooz.co:8083/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "http://npm.zooz.co:8083/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "http://npm.zooz.co:8083/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + } + } + }, + "koa-bodyparser": { + "version": "4.2.1", + "resolved": "http://npm.zooz.co:8083/koa-bodyparser/-/koa-bodyparser-4.2.1.tgz", + "integrity": "sha512-UIjPAlMZfNYDDe+4zBaOAUKYqkwAGcIU6r2ARf1UOXPAlfennQys5IiShaVeNf7KkVBlf88f2LeLvBFvKylttw==", + "dev": true, + "requires": { + "co-body": "^6.0.0", + "copy-to": "^2.0.1" + } + }, + "koa-compose": { + "version": "3.2.1", + "resolved": "http://npm.zooz.co:8083/koa-compose/-/koa-compose-3.2.1.tgz", + "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, + "koa-convert": { + "version": "1.2.0", + "resolved": "http://npm.zooz.co:8083/koa-convert/-/koa-convert-1.2.0.tgz", + "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", + "dev": true, + "requires": { + "co": "^4.6.0", + "koa-compose": "^3.0.0" + } + }, + "koa-is-json": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/koa-is-json/-/koa-is-json-1.0.0.tgz", + "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=", + "dev": true + }, + "koa-multer": { + "version": "1.0.2", + "resolved": "http://npm.zooz.co:8083/koa-multer/-/koa-multer-1.0.2.tgz", + "integrity": "sha512-0kFzN4atVd+9oiG+4fYxQ9S2T3dPhKNvmhITIY606Qn9wLEmfhW0DhSpOzRYhddN//4rh/TCK95TMtflmFa5lA==", + "dev": true, + "requires": { + "multer": "1.3.0" + } + }, + "koa-router": { + "version": "7.4.0", + "resolved": "http://npm.zooz.co:8083/koa-router/-/koa-router-7.4.0.tgz", + "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "http-errors": "^1.3.1", + "koa-compose": "^3.0.0", + "methods": "^1.0.1", + "path-to-regexp": "^1.1.1", + "urijs": "^1.19.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "http://npm.zooz.co:8083/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "http://npm.zooz.co:8083/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "lcov-parse": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", @@ -1997,7 +2404,7 @@ }, "nyc": { "version": "13.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-13.1.0.tgz", + "resolved": "http://npm.zooz.co:8083/nyc/-/nyc-13.1.0.tgz", "integrity": "sha512-3GyY6TpQ58z9Frpv4GMExE1SV2tAgYqC7HSy2omEhNiCT3mhT9NyiOvIE8zkbuJVFzmvvNTnE4h/7/wQae7xLg==", "dev": true, "requires": { @@ -2396,7 +2803,7 @@ }, "istanbul-lib-instrument": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz", + "resolved": "http://npm.zooz.co:8083/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz", "integrity": "sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==", "dev": true, "requires": { @@ -3188,6 +3595,12 @@ "mimic-fn": "^1.0.0" } }, + "only": { + "version": "0.0.2", + "resolved": "http://npm.zooz.co:8083/only/-/only-0.0.2.tgz", + "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", + "dev": true + }, "ono": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.2.tgz", @@ -4020,6 +4433,12 @@ "os-tmpdir": "~1.0.2" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "http://npm.zooz.co:8083/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -4095,6 +4514,12 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "urijs": { + "version": "1.19.1", + "resolved": "http://npm.zooz.co:8083/urijs/-/urijs-1.19.1.tgz", + "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4181,6 +4606,12 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "ylru": { + "version": "1.2.1", + "resolved": "http://npm.zooz.co:8083/ylru/-/ylru-1.2.1.tgz", + "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", + "dev": true + }, "z-schema": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.19.0.tgz", diff --git a/package.json b/package.json index d369b33..e735c5b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "open api", "input validation", "validation", - "validator" + "validator", + "koa" ], "bugs": { "url": "https://github.com/Zooz/express-ajv-swagger-validation" @@ -66,6 +67,10 @@ "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", "express": "^4.16.2", + "koa": "^2.6.2", + "koa-bodyparser": "^4.2.1", + "koa-multer": "^1.0.2", + "koa-router": "^7.4.0", "mocha": "^4.0.1", "multer": "^1.3.0", "nyc": "^13.1.0", diff --git a/src/frameworks/express.js b/src/frameworks/express.js new file mode 100644 index 0000000..7fe0021 --- /dev/null +++ b/src/frameworks/express.js @@ -0,0 +1,24 @@ +function _getParameters(req) { + let requestOptions = {}; + let path = req.baseUrl.concat(req.route.path); + requestOptions.path = path.endsWith('/') ? path.substring(0, path.length - 1) : path; + requestOptions.headers = req.headers; + requestOptions.params = req.params; + requestOptions.query = req.query; + requestOptions.files = req.files; + requestOptions.method = req.method; + requestOptions.body = req.body; + + return requestOptions; +} + +async function validate(validateRequest, req, res, next) { + let requestOptions, errors; + requestOptions = _getParameters(req); + errors = await validateRequest(requestOptions); + next(errors); +} + +module.exports = { + validate: validate +}; diff --git a/src/frameworks/koa.js b/src/frameworks/koa.js new file mode 100644 index 0000000..d5f6bc1 --- /dev/null +++ b/src/frameworks/koa.js @@ -0,0 +1,27 @@ +function _getParameters(ctx) { + let requestOptions = {}; + let path = ctx._matchedRoute; + requestOptions.path = path.endsWith('/') ? path.substring(0, path.length - 1) : path; + requestOptions.headers = ctx.request.req.headers; + requestOptions.params = ctx.params; + requestOptions.query = ctx.query; + requestOptions.files = ctx.req.files; + requestOptions.method = ctx.req.method; + requestOptions.body = ctx.req.body || ctx.request.body; + + return requestOptions; +} + +async function validate(validateRequest, ctx, next) { + let requestOptions, errors; + requestOptions = _getParameters(ctx, next); + errors = await validateRequest(requestOptions); + if (errors) { + throw errors; + } + next(); +} + +module.exports = { + validate: validate +}; diff --git a/src/middleware.js b/src/middleware.js index 7539975..cebaff1 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -13,6 +13,7 @@ var schemas = {}; var middlewareOptions; var ajvConfigBody; var ajvConfigParams; +var framework; /** * Initialize the input validation middleware @@ -23,6 +24,7 @@ function init(swaggerPath, options) { middlewareOptions = options || {}; ajvConfigBody = middlewareOptions.ajvConfigBody || {}; ajvConfigParams = middlewareOptions.ajvConfigParams || {}; + framework = middlewareOptions.framework ? require(`./frameworks/${middlewareOptions.framework}`) : require('./frameworks/express'); const makeOptionalAttributesNullable = middlewareOptions.makeOptionalAttributesNullable || false; return Promise.all([ @@ -100,22 +102,23 @@ function _getValidatedBodySchema(bodySchema) { * @param {any} next * @returns In case of an error will call `next` with `InputValidationError` */ -function validate(req, res, next) { - let path = extractPath(req); +async function validate(...args) { + return framework.validate(_validateRequest, ...args); +} +function _validateRequest(requestOptions) { return Promise.all([ - _validateParams(req.headers, req.params, req.query, req.files, path, req.method.toLowerCase()).catch(e => e), - _validateBody(req.body, path, req.method.toLowerCase()).catch(e => e) + _validateParams(requestOptions.headers, requestOptions.params, requestOptions.query, requestOptions.files, requestOptions.path, requestOptions.method.toLowerCase()).catch(e => e), + _validateBody(requestOptions.body, requestOptions.path, requestOptions.method.toLowerCase()).catch(e => e) ]).then(function (errors) { if (errors[0] || errors[1]) { return errors[0] && errors[1] ? Promise.reject(errors[0].concat(errors[1])) : errors[0] ? Promise.reject(errors[0]) : Promise.reject(errors[1]); } - return next(); }).catch(function (errors) { - const error = new InputValidationError(errors, path, req.method.toLowerCase(), + const error = new InputValidationError(errors, requestOptions.path, requestOptions.method.toLowerCase(), { beautifyErrors: middlewareOptions.beautifyErrors, firstError: middlewareOptions.firstError }); - return next(error); + return Promise.resolve(error); }); } @@ -149,11 +152,6 @@ function addCustomKeyword(ajv, formats) { ajv.addKeyword('content', contentKeyword); } -function extractPath(req) { - let path = req.baseUrl.concat(req.route.path); - return path.endsWith('/') ? path.substring(0, path.length - 1) : path; -} - function buildBodyValidation(schema, swaggerDefinitions, originalSwagger, currentPath, currentMethod, parsedPath) { const defaultAjvOptions = { allErrors: true diff --git a/test/test-server-formdata.js b/test/express/test-server-formdata.js similarity index 96% rename from test/test-server-formdata.js rename to test/express/test-server-formdata.js index 3b2357b..04bebb1 100644 --- a/test/test-server-formdata.js +++ b/test/express/test-server-formdata.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var multer = require('multer'); var upload = multer(); diff --git a/test/test-server-inheritance.js b/test/express/test-server-inheritance.js similarity index 95% rename from test/test-server-inheritance.js rename to test/express/test-server-inheritance.js index 144771a..f4001fc 100644 --- a/test/test-server-inheritance.js +++ b/test/express/test-server-inheritance.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -37,4 +37,4 @@ module.exports = inputValidation.init('test/pet-store-swagger-inheritance.yaml', }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-server-with-options-more-than-1-error.js b/test/express/test-server-with-options-more-than-1-error.js similarity index 95% rename from test/test-server-with-options-more-than-1-error.js rename to test/express/test-server-with-options-more-than-1-error.js index 607251d..3912b52 100644 --- a/test/test-server-with-options-more-than-1-error.js +++ b/test/express/test-server-with-options-more-than-1-error.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -35,4 +35,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValida }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-server-with-options.js b/test/express/test-server-with-options.js similarity index 96% rename from test/test-server-with-options.js rename to test/express/test-server-with-options.js index 0030f54..1429267 100644 --- a/test/test-server-with-options.js +++ b/test/express/test-server-with-options.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var inputValidationOptions = { formats: [ @@ -44,4 +44,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValida }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-base-route.js b/test/express/test-simple-server-base-route.js similarity index 94% rename from test/test-simple-server-base-route.js rename to test/express/test-simple-server-base-route.js index b4ddeab..4face09 100644 --- a/test/test-simple-server-base-route.js +++ b/test/express/test-simple-server-base-route.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); var router = express.Router(); router.route('/').get(inputValidation.validate, function (req, res, next) { @@ -33,4 +33,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml') }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-with-base-path.js b/test/express/test-simple-server-with-base-path.js similarity index 92% rename from test/test-simple-server-with-base-path.js rename to test/express/test-simple-server-with-base-path.js index 6a77330..625d2c4 100644 --- a/test/test-simple-server-with-base-path.js +++ b/test/express/test-simple-server-with-base-path.js @@ -2,8 +2,8 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); -var router = require('./router'); +var inputValidation = require('../../src/middleware'); +var router = require('../router'); module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yaml', { contentTypeValidation: true }) .then(function () { @@ -31,4 +31,4 @@ module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yam }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/test-simple-server-with-coercion.js b/test/express/test-simple-server-with-coercion.js similarity index 96% rename from test/test-simple-server-with-coercion.js rename to test/express/test-simple-server-with-coercion.js index 9ba6610..3c7a43f 100644 --- a/test/test-simple-server-with-coercion.js +++ b/test/express/test-simple-server-with-coercion.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); module.exports = inputValidation.init('test/pet-store-swagger.yaml', { ajvConfigBody: { diff --git a/test/test-simple-server.js b/test/express/test-simple-server.js similarity index 94% rename from test/test-simple-server.js rename to test/express/test-simple-server.js index 049c1d2..531e6c1 100644 --- a/test/test-simple-server.js +++ b/test/express/test-simple-server.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); module.exports = inputValidation.init('test/pet-store-swagger.yaml') .then(function () { @@ -27,4 +27,4 @@ module.exports = inputValidation.init('test/pet-store-swagger.yaml') }); return Promise.resolve(app); - }); \ No newline at end of file + }); diff --git a/test/koa/test-server-formdata.js b/test/koa/test-server-formdata.js new file mode 100644 index 0000000..fbfe063 --- /dev/null +++ b/test/koa/test-server-formdata.js @@ -0,0 +1,55 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +const multer = require('koa-multer'); +const upload = multer(); + +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ }, + { name: 'file', validate: () => { return true } } + ], + beautifyErrors: true, + firstError: true, + expectFormFieldsInBody: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/form-data-swagger.yaml', inputValidationOptions) + .then(function () { + router.post('/pets/import', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/kennels/import', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/login', upload.any(), inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-inheritance.js b/test/koa/test-server-inheritance.js new file mode 100644 index 0000000..486f735 --- /dev/null +++ b/test/koa/test-server-inheritance.js @@ -0,0 +1,57 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ } + ], + beautifyErrors: true, + firstError: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger-inheritance.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/:version/pets/:petId', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-with-options-more-than-1-error.js b/test/koa/test-server-with-options-more-than-1-error.js new file mode 100644 index 0000000..d834fff --- /dev/null +++ b/test/koa/test-server-with-options-more-than-1-error.js @@ -0,0 +1,52 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: err.errors}; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +var inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,18}$/ } + ], + beautifyErrors: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-server-with-options.js b/test/koa/test-server-with-options.js new file mode 100644 index 0000000..6821403 --- /dev/null +++ b/test/koa/test-server-with-options.js @@ -0,0 +1,63 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: err.errors }; + } + } +}); +app.use(bodyParser({enableTypes: ['text', 'json']})); +app.use(router.routes()); + +let inputValidationOptions = { + formats: [ + { name: 'double', pattern: /\d+(\.\d+)?/ }, + { name: 'int64', pattern: /^\d{1,19}$/ }, + { name: 'int32', pattern: /^\d{1,10}$/ } + ], + beautifyErrors: true, + firstError: true, + contentTypeValidation: true, + framework: 'koa' +}; + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', inputValidationOptions) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets/:petId', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/text', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-base-route.js b/test/koa/test-simple-server-base-route.js new file mode 100644 index 0000000..0aa7f7b --- /dev/null +++ b/test/koa/test-simple-server-base-route.js @@ -0,0 +1,47 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router({ + prefix: '/pets' +}); +let router1 = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); +app.use(router1.routes()); + +router.get('/', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router.post('/', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router.get('/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); +router1.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; +}); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-with-base-path.js b/test/koa/test-simple-server-with-base-path.js new file mode 100644 index 0000000..1f9414e --- /dev/null +++ b/test/koa/test-simple-server-with-base-path.js @@ -0,0 +1,50 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router({ + prefix: '/v1' +}); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger-with-base-path.yaml', {framework: 'koa', contentTypeValidation: true}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + router.get('/capital', inputValidation.validate, function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server-with-coercion.js b/test/koa/test-simple-server-with-coercion.js new file mode 100644 index 0000000..9cbfea9 --- /dev/null +++ b/test/koa/test-simple-server-with-coercion.js @@ -0,0 +1,51 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', { + framework: 'koa', + ajvConfigBody: { + coerceTypes: true }, + makeOptionalAttributesNullable: true}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + router.patch('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK', receivedParams: ctx.request.body }; + }); + + return Promise.resolve(app); + }); diff --git a/test/koa/test-simple-server.js b/test/koa/test-simple-server.js new file mode 100644 index 0000000..d52a655 --- /dev/null +++ b/test/koa/test-simple-server.js @@ -0,0 +1,43 @@ +'use strict'; + +const Koa = require('koa'); +const Router = require('koa-router'); +const bodyParser = require('koa-bodyparser'); +const inputValidation = require('../../src/middleware'); +let app = new Koa(); +let router = new Router(); + +app.use(async function(ctx, next) { + try { + await next(); + } catch (err) { + if (err instanceof inputValidation.InputValidationError) { + ctx.status = 400; + ctx.body = { more_info: JSON.stringify(err.errors) }; + } + } +}); +app.use(bodyParser()); +app.use(router.routes()); + +module.exports = inputValidation.init('test/pet-store-swagger.yaml', {framework: 'koa'}) + .then(function () { + router.get('/pets', inputValidation.validate, async function(ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.post('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.get('/pets/:petId', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + router.put('/pets', inputValidation.validate, async function (ctx, next) { + ctx.status = 200; + ctx.body = { result: 'OK' }; + }); + + return Promise.resolve(app); + }); From e380415e47d45f3a91f11dc03ddaa6a459b984a3 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Mon, 21 Jan 2019 15:58:27 +0200 Subject: [PATCH 4/9] Seperating koa tests from express tests --- test/express/middleware-test.js | 2314 ++++++++++++++++++++++++++ test/koa/middleware-test.js | 2369 +++++++++++++++++++++++++++ test/middleware-test.js | 2712 ------------------------------- 3 files changed, 4683 insertions(+), 2712 deletions(-) create mode 100644 test/express/middleware-test.js create mode 100644 test/koa/middleware-test.js diff --git a/test/express/middleware-test.js b/test/express/middleware-test.js new file mode 100644 index 0000000..cc81700 --- /dev/null +++ b/test/express/middleware-test.js @@ -0,0 +1,2314 @@ +'use strict'; + +var chai = require('chai'), + expect = chai.expect, + sinon = require('sinon'), + chaiSinon = require('chai-sinon'), + request = require('supertest'); +chai.use(chaiSinon); + +describe('input-validation middleware tests - Express', function () { + describe('init function tests', function () { + it('should reject the promise in case the file doesn\'t exists', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + return middleware.init('test/pet-store-swagger1.yaml') + .catch(function (err) { + expect(err).to.exist; + }); + }); + it('should resolve without formats', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + let addCustomKeyword = middleware.__get__('addCustomKeyword'); + let addCustomKeywordSpy = sinon.spy(addCustomKeyword); + return middleware.init('test/pet-store-swagger.yaml') + .then(function () { + expect(addCustomKeywordSpy).to.have.not.been.called; + }); + }); + }); + describe('Simple server - no options', function () { + var app; + before(function () { + return require('./test-simple-server').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server - type coercion enabled', function () { + var app; + before(function () { + return require('./test-simple-server-with-coercion').then(function (testServer) { + app = testServer; + }); + }); + it('request with wrong parameter type - should pass validation due to coercion', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, done); + }); + it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('handles request body objects without specified schema correctly', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + it('handles request body without specified schema correctly', function (done) { + request(app) + .patch('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field1).to.equal('enum1'); + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test3: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test3.field1).to.equal('enum1'); + expect(pet.test3.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1', + field3: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field3).to.be.null; + done(); + }); + }); + }); + describe('Simple server - with base path', function () { + var app; + before(function () { + return require('./test-simple-server-with-base-path').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/v1/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/v1/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('content-type must be one of application/json'); + done(); + }); + }); + it('headers are in capital letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('Capital-Letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('headers are in lowercase letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('capital-letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/v1/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/v1/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/v1/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/v1/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server using routes', function () { + var app; + before(function () { + return require('./test-simple-server-base-route').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Server with options - beautify and one error', function () { + var app; + before(function () { + return require('./test-server-with-options').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body should be array'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); + done(); + }); + }); + it('valid content-type when multiple content-types defined - should pass validation', function (done) { + request(app) + .put('/text') + .set('content-type', 'text/plain') + .send('text') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('more detailed content-type - should pass validation', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/json; charset=utf-8') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('valid empty request - should pass validation', function (done) { + request(app) + .put('/pets/1234') + .set('request-id', '1234') + .set('api-version', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + }); + describe('Server with options - Only beautify errors', function () { + var app; + before(function () { + return require('./test-server-with-options-more-than-1-error').then(function (testServer) { + app = testServer; + }); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body & bad header', function (done) { + request(app) + .post('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: 'enum1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info.length).to.equal(2); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + expect(res.body.more_info[1]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute (more than one error)', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); + expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info).to.includes('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should be array'); + done(); + }); + }); + }); + describe('Inheritance', function () { + var app; + before(function () { + return require('./test-server-inheritance').then(function (testServer) { + app = testServer; + }); + }); + it('should pass', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + packSize: 3 + }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('wrong value for header with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '2.0') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); + done(); + }); + }); + it('wrong value for query with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .query({ PetType: 'bird' }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); + done(); + }); + }); + it('missing header with enum definition', function (done) { + request(app) + .get('/pets') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('wrong value for path param with enum definition', function (done) { + request(app) + .get('/v2/pets/12345') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); + done(); + }); + }); + it('should fail for wrong value in discriminator', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing discriminator key', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing attribute in inherited object (Dog)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'packSize\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (cat)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Cat', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (parent)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + tag: 'tag', + chip_number: '123454' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'name\''); + done(); + }); + }); + }); + describe('FormData', function () { + var app; + before(function () { + return require('./test-server-formdata').then(function (testServer) { + app = testServer; + }); + }); + it('only required files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('required and optional files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .attach('optionalFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing required file should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); + done(); + }); + }); + it('extra files exists but not allowed should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .attach('sourceFile', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); + done(); + }); + }); + it('supports string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .field('password', 'pass') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('supports mix of files and fields', function (done) { + request(app) + .post('/kennels/import') + .set('api-version', '1.0') + .field('name', 'kennel 1 ') + .attach('blueprintFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('validates string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'password\''); + done(); + }); + }); + }); +}); diff --git a/test/koa/middleware-test.js b/test/koa/middleware-test.js new file mode 100644 index 0000000..140be27 --- /dev/null +++ b/test/koa/middleware-test.js @@ -0,0 +1,2369 @@ +'use strict'; + +var chai = require('chai'), + expect = chai.expect, + sinon = require('sinon'), + chaiSinon = require('chai-sinon'), + request = require('supertest'); +chai.use(chaiSinon); + +describe('input-validation middleware tests - Koa', function () { + describe('init function tests', function () { + it('should reject the promise in case the file doesn\'t exists', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + return middleware.init('test/pet-store-swagger1.yaml') + .catch(function (err) { + expect(err).to.exist; + }); + }); + it('should resolve without formats', function () { + let rewire = require('rewire'); + let middleware = rewire('../../src/middleware'); + let addCustomKeyword = middleware.__get__('addCustomKeyword'); + let addCustomKeywordSpy = sinon.spy(addCustomKeyword); + return middleware.init('test/pet-store-swagger.yaml') + .then(function () { + expect(addCustomKeywordSpy).to.have.not.been.called; + }); + }); + }); + + describe('Simple server - no options', function () { + var app; + before(function () { + return require('./test-simple-server').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server - type coercion enabled', function () { + var app; + before(function () { + return require('./test-simple-server-with-coercion').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('request with wrong parameter type - should pass validation due to coercion', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, done); + }); + + it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('handles request body objects without specified schema correctly', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + + it('handles request body without specified schema correctly', function (done) { + request(app) + .patch('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test2: { + arbitraryField: 'dummy', + nullField: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field1).to.equal('enum1'); + expect(pet.test2.arbitraryField).to.equal('dummy'); + expect(pet.test2.nullField).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { + request(app) + .post('/pets') + .send({ + name: 1, + tag: 'tag', + age: null, + test: { + field1: 'enum1' + }, + test3: { + field1: 'enum1', + field2: null + } + }) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams; + expect(pet.test3.field1).to.equal('enum1'); + expect(pet.test3.field2).to.be.null; + expect(pet.age).to.be.null; + done(); + }); + }); + + it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { + request(app) + .put('/pets') + .send([{ + name: 1, + tag: 'tag', + test: { + field1: 'enum1', + field3: null + } + }]) + .expect(200) + .end(function(err, res) { + if (err) { + return done(err); + } + const pet = res.body.receivedParams[0]; + expect(pet.test.field3).to.be.null; + done(); + }); + }); + }); + describe('Simple server - with base path', function () { + var app; + before(function () { + return require('./test-simple-server-with-base-path').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/v1/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/v1/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('content-type must be one of application/json'); + done(); + }); + }); + it('headers are in capital letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('Capital-Letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('headers are in lowercase letters - should pass validation', function (done) { + request(app) + .get('/v1/capital') + .set('capital-letters', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/v1/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/v1/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/v1/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/v1/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/v1/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Simple server using routes', function () { + var app; + before(function () { + return require('./test-simple-server-base-route').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('api-version'); + expect(res.body.more_info).to.includes('should match pattern'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('request-id'); + expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '123234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('tag'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '123324') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('name'); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123434') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12334') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('test'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12343') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('page'); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('limit'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('petId'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(moreInfoAsJson.length).to.equal(2); + expect(res.body.more_info).to.includes('field1'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('[1].test.field1'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + let moreInfoAsJson = JSON.parse(res.body.more_info); + expect(moreInfoAsJson).to.be.instanceof(Array); + expect(res.body.more_info).to.includes('should be array'); + done(); + }); + }); + }); + describe('Server with options - beautify and one error', function () { + var app; + before(function () { + return require('./test-server-with-options').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('body should be array'); + done(); + }); + }); + it('bad request - wrong content-type (should be application/json)', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/x-www-form-urlencoded') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('string'); + expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); + done(); + }); + }); + it('valid content-type when multiple content-types defined - should pass validation', function (done) { + request(app) + .put('/text') + .set('content-type', 'text/plain') + .send('text') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('more detailed content-type - should pass validation', function (done) { + request(app) + .put('/pets') + .set('content-type', 'application/json; charset=utf-8') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }]) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('valid empty request - should pass validation', function (done) { + request(app) + .put('/pets/1234') + .set('request-id', '1234') + .set('api-version', '1.0') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + }); + describe('Server with options - Only beautify errors', function () { + var app; + before(function () { + return require('./test-server-with-options-more-than-1-error').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('valid request - should pass validation', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing header - should fail', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('bad header - invalid pattern', function (done) { + request(app) + .get('/pets') + .set('request-id', '123456') + .set('api-version', '1') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); + done(); + }); + }); + it('bad header - empty header', function (done) { + request(app) + .get('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .query({ page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + done(); + }); + }); + it('bad body & bad header', function (done) { + request(app) + .post('/pets') + .set('request-id', '') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: 'enum1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info.length).to.equal(2); + expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); + expect(res.body.more_info[1]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - wrong type', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + name: '111', + tag: 12344, + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/tag should be string'); + done(); + }); + }); + it('bad body - missing required params', function (done) { + request(app) + .post('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .send({ + tag: 'tag', + 'test': { + field1: '1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); + done(); + }); + }); + it('bad body - missing required object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); + done(); + }); + }); + it('bad body - wrong type object attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '12354') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: '' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should be object'); + done(); + }); + }); + it('bad body - missing required nested attribute', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: {} + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); + done(); + }); + }); + it('bad body - wrong format nested attribute (more than one error)', function (done) { + request(app) + .post('/pets') + .set('request-id', '123435') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); + expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad body - wrong enum value', function (done) { + request(app) + .post('/pets') + .set('request-id', '123345') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: 'field1' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); + done(); + }); + }); + it('bad query param - missing required params', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 100 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); + done(); + }); + }); + it('bad query param - over limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 150, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); + done(); + }); + }); + it('bad query param - under limit', function (done) { + request(app) + .get('/pets') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: 0, page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info).to.includes('query/limit should be >= 1'); + done(); + }); + }); + it('bad path param - wrong format', function (done) { + request(app) + .get('/pets/12') + .set('request-id', '1234') + .set('api-version', '1.0') + .query({ limit: '50', page: 0 }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); + done(); + }); + }); + it('bad body - wrong format nested attribute (not parameters)', function (done) { + request(app) + .put('/pets') + .send([{ + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format in array item body (second item)', function (done) { + request(app) + .put('/pets') + .send([ + { + name: 'name', + tag: 'tag', + test: { + field1: 'enum1' + } + }, + { + name: 'name', + tag: 'tag', + test: { + field1: 1234 + } + }]) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); + done(); + }); + }); + it('bad body - wrong format body (should be an array)', function (done) { + request(app) + .put('/pets') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.be.a('array'); + expect(res.body.more_info[0]).to.includes('body should be array'); + done(); + }); + }); + }); + describe('Inheritance', function () { + var app; + before(function () { + return require('./test-server-inheritance').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('should pass', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + packSize: 3 + }) + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('wrong value for header with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '2.0') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); + done(); + }); + }); + it('wrong value for query with enum definition', function (done) { + request(app) + .get('/pets') + .set('api-version', '1.0') + .query({ PetType: 'bird' }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); + done(); + }); + }); + it('missing header with enum definition', function (done) { + request(app) + .get('/pets') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); + done(); + }); + }); + it('wrong value for path param with enum definition', function (done) { + request(app) + .get('/v2/pets/12345') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); + done(); + }); + }); + it('should fail for wrong value in discriminator', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing discriminator key', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); + done(); + }); + }); + it('should fail for missing attribute in inherited object (Dog)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'packSize\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (cat)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Cat', + name: 'name', + tag: 'tag', + test: { + field1: '1234' + } + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); + done(); + }); + }); + it('should fail for missing attribute in inherited object (parent)', function (done) { + request(app) + .post('/pets') + .set('api-version', '1.0') + .send({ + petType: 'Dog', + tag: 'tag', + chip_number: '123454' + }) + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'name\''); + done(); + }); + }); + }); + describe('FormData', function () { + var app; + before(function () { + return require('./test-server-formdata').then(function (testServer) { + app = testServer; + }); + }); + beforeEach(function (){ + app = app.listen(8888); + }); + afterEach(function () { + app.close(); + }); + it('only required files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('required and optional files exists should pass', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile', 'LICENSE') + .attach('optionalFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('missing required file should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); + done(); + }); + }); + it('extra files exists but not allowed should fail', function (done) { + request(app) + .post('/pets/import') + .set('api-version', '1.0') + .attach('sourceFile1', 'LICENSE') + .attach('sourceFile', 'LICENSE') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); + done(); + }); + }); + it('supports string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .field('password', 'pass') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('supports mix of files and fields', function (done) { + request(app) + .post('/kennels/import') + .set('api-version', '1.0') + .field('name', 'kennel 1 ') + .attach('blueprintFile', 'LICENSE') + .expect(200, function (err, res) { + if (err) { + throw err; + } + expect(res.body.result).to.equal('OK'); + done(); + }); + }); + it('validates string formData', function (done) { + request(app) + .post('/login') + .set('api-version', '1.0') + .field('username', 'user') + .expect(400, function (err, res) { + if (err) { + throw err; + } + expect(res.body.more_info).to.includes('body should have required property \'password\''); + done(); + }); + }); + }); +}); diff --git a/test/middleware-test.js b/test/middleware-test.js index e5376f3..e69de29 100644 --- a/test/middleware-test.js +++ b/test/middleware-test.js @@ -1,2712 +0,0 @@ -'use strict'; - -var chai = require('chai'), - expect = chai.expect, - sinon = require('sinon'), - chaiSinon = require('chai-sinon'), - request = require('supertest'); -chai.use(chaiSinon); - -describe('input-validation middleware tests', function () { - describe('init function tests', function () { - it('should reject the promise in case the file doesn\'t exists', function () { - let rewire = require('rewire'); - let middleware = rewire('../src/middleware'); - return middleware.init('test/pet-store-swagger1.yaml') - .catch(function (err) { - expect(err).to.exist; - }); - }); - it('should resolve without formats', function () { - let rewire = require('rewire'); - let middleware = rewire('../src/middleware'); - let addCustomKeyword = middleware.__get__('addCustomKeyword'); - let addCustomKeywordSpy = sinon.spy(addCustomKeyword); - return middleware.init('test/pet-store-swagger.yaml') - .then(function () { - expect(addCustomKeywordSpy).to.have.not.been.called; - }); - }); - }); - describe('Simple server - no options', function () { - var app; - before(function () { - return require('./test-simple-server').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Simple server - type coercion enabled', function () { - var app; - before(function () { - return require('./test-simple-server-with-coercion').then(function (testServer) { - app = testServer; - }); - }); - it('uses default value for an unspecified parameter', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.awards).to.deep.equal([]); - expect(pet.colour).to.equal('unknown'); - done(); - }); - }); - it('request with wrong parameter type - should pass validation due to coercion', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, done); - }); - - it('request with wrong parameter type - should keep null values as null when payload is array', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('handles request body objects without specified schema correctly', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); - - it('handles request body without specified schema correctly', function (done) { - request(app) - .patch('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test2: { - arbitraryField: 'dummy', - nullField: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field1).to.equal('enum1'); - expect(pet.test2.arbitraryField).to.equal('dummy'); - expect(pet.test2.nullField).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type and no required fields defined - should keep null values as null when payload is object', function (done) { - request(app) - .post('/pets') - .send({ - name: 1, - tag: 'tag', - age: null, - test: { - field1: 'enum1' - }, - test3: { - field1: 'enum1', - field2: null - } - }) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams; - expect(pet.test3.field1).to.equal('enum1'); - expect(pet.test3.field2).to.be.null; - expect(pet.age).to.be.null; - done(); - }); - }); - - it('request with wrong parameter type - should keep null values as null when (invalid) swagger with multiple types is provided', function (done) { - request(app) - .put('/pets') - .send([{ - name: 1, - tag: 'tag', - test: { - field1: 'enum1', - field3: null - } - }]) - .expect(200) - .end(function(err, res) { - if (err) { - return done(err); - } - const pet = res.body.receivedParams[0]; - expect(pet.test.field3).to.be.null; - done(); - }); - }); - }); - describe('Simple server - with base path', function () { - var app; - before(function () { - return require('./test-simple-server-with-base-path').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/v1/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/v1/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('content-type must be one of application/json'); - done(); - }); - }); - it('headers are in capital letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('Capital-Letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('headers are in lowercase letters - should pass validation', function (done) { - request(app) - .get('/v1/capital') - .set('capital-letters', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/v1/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/v1/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/v1/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/v1/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/v1/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Simple server using routes', function () { - var app; - before(function () { - return require('./test-simple-server-base-route').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Server with options - beautify and one error', function () { - var app; - before(function () { - return require('./test-server-with-options').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.equal('body/[0].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('body should be array'); - done(); - }); - }); - it('bad request - wrong content-type (should be application/json)', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/x-www-form-urlencoded') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('string'); - expect(res.body.more_info).to.includes('headers content-type must be one of application/json,form-data'); - done(); - }); - }); - it('valid content-type when multiple content-types defind - should pass validation', function (done) { - request(app) - .put('/text') - .set('content-type', 'text/plain') - .send('text') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('more detailed content-type - should pass validation', function (done) { - request(app) - .put('/pets') - .set('content-type', 'application/json; charset=utf-8') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }]) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('valid empty request - should pass validation', function (done) { - request(app) - .put('/pets/1234') - .set('request-id', '1234') - .set('api-version', '1.0') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - }); - describe('Server with options - Only beautify errors', function () { - var app; - before(function () { - return require('./test-server-with-options-more-than-1-error').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/api-version should match pattern "^\\d{1,3}\\.\\d{1,3}$"'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body & bad header', function (done) { - request(app) - .post('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: 'enum1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info.length).to.equal(2); - expect(res.body.more_info[0]).to.includes('headers/request-id should NOT be shorter than 1 characters'); - expect(res.body.more_info[1]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/tag should be string'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'name\''); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should have required property \'test\''); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12354') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should be object'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test should have required property \'field1\''); - done(); - }); - }); - it('bad body - wrong format nested attribute (more than one error)', function (done) { - request(app) - .post('/pets') - .set('request-id', '123435') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be string'); - expect(res.body.more_info[1]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '123345') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/test.field1 should be equal to one of the allowed values [enum1,enum2]'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query should have required property \'page\''); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('query/limit should be <= 100'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info).to.includes('query/limit should be >= 1'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('path/petId should NOT be shorter than 3 characters'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[0].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body/[1].test.field1 should be string'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.be.a('array'); - expect(res.body.more_info[0]).to.includes('body should be array'); - done(); - }); - }); - }); - describe('Server with YAML anchors', function () { - var app; - before(function () { - return require('./test-server-with-yaml-anchors').then(function (testServer) { - app = testServer; - }); - }); - it('valid request - should pass validation', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('valid post request - should pass validation', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing header - should fail', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should have required property \'api-version\''); - done(); - }); - }); - it('bad header - invalid pattern', function (done) { - request(app) - .get('/pets') - .set('request-id', '123456') - .set('api-version', '1') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('api-version'); - expect(res.body.more_info).to.includes('should match pattern'); - done(); - }); - }); - it('bad header - empty header', function (done) { - request(app) - .get('/pets') - .set('request-id', '') - .set('api-version', '1.0') - .query({ page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('request-id'); - expect(res.body.more_info).to.includes('should NOT be shorter than 1 characters'); - done(); - }); - }); - it('bad body - wrong type', function (done) { - request(app) - .post('/pets') - .set('request-id', '123234') - .set('api-version', '1.0') - .send({ - name: '111', - tag: 12344, - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('tag'); - done(); - }); - }); - it('bad body - missing required params', function (done) { - request(app) - .post('/pets') - .set('request-id', '123324') - .set('api-version', '1.0') - .send({ - tag: 'tag', - 'test': { - field1: '1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('name'); - done(); - }); - }); - it('bad body - missing required object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '123434') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - wrong type object attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12334') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: '' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('test'); - done(); - }); - }); - it('bad body - missing required nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: {} - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format nested attribute', function (done) { - request(app) - .post('/pets') - .set('request-id', '12343') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong enum value', function (done) { - request(app) - .post('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: 'field1' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be equal to one of the allowed values'); - done(); - }); - }); - it('bad query param - missing required params', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 100 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('page'); - done(); - }); - }); - it('bad query param - over limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 150, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad query param - under limit', function (done) { - request(app) - .get('/pets') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: 0, page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('limit'); - done(); - }); - }); - it('bad path param - wrong format', function (done) { - request(app) - .get('/pets/12') - .set('request-id', '1234') - .set('api-version', '1.0') - .query({ limit: '50', page: 0 }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('petId'); - done(); - }); - }); - it('bad body - wrong format nested attribute (not parameters)', function (done) { - request(app) - .put('/pets') - .send([{ - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(moreInfoAsJson.length).to.equal(2); - expect(res.body.more_info).to.includes('field1'); - done(); - }); - }); - it('bad body - wrong format in array item body (second item)', function (done) { - request(app) - .put('/pets') - .send([ - { - name: 'name', - tag: 'tag', - test: { - field1: 'enum1' - } - }, - { - name: 'name', - tag: 'tag', - test: { - field1: 1234 - } - }]) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('[1].test.field1'); - done(); - }); - }); - it('bad body - wrong format body (should be an array)', function (done) { - request(app) - .put('/pets') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - let moreInfoAsJson = JSON.parse(res.body.more_info); - expect(moreInfoAsJson).to.be.instanceof(Array); - expect(res.body.more_info).to.includes('should be array'); - done(); - }); - }); - }); - describe('Inheritance', function () { - var app; - before(function () { - return require('./test-server-inheritance').then(function (testServer) { - app = testServer; - }); - }); - it('should pass', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - packSize: 3 - }) - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('wrong value for header with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '2.0') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers/api-version should be equal to one of the allowed values [1.0,1.1]'); - done(); - }); - }); - it('wrong value for query with enum definition', function (done) { - request(app) - .get('/pets') - .set('api-version', '1.0') - .query({ PetType: 'bird' }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('query/PetType should be equal to one of the allowed values [Dog,Cat]'); - done(); - }); - }); - it('missing header with enum definition', function (done) { - request(app) - .get('/pets') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('headers should have required property \'api-version\''); - done(); - }); - }); - it('wrong value for path param with enum definition', function (done) { - request(app) - .get('/v2/pets/12345') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('path/version should be equal to one of the allowed values [v1]'); - done(); - }); - }); - it('should fail for wrong value in discriminator', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing discriminator key', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/petType should be equal to one of the allowed values [Cat,Dog]'); - done(); - }); - }); - it('should fail for missing attribute in inherited object (Dog)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'packSize\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (cat)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Cat', - name: 'name', - tag: 'tag', - test: { - field1: '1234' - } - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'huntingSkill\''); - done(); - }); - }); - it('should fail for missing attribute in inherited object (parent)', function (done) { - request(app) - .post('/pets') - .set('api-version', '1.0') - .send({ - petType: 'Dog', - tag: 'tag', - chip_number: '123454' - }) - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'name\''); - done(); - }); - }); - }); - describe('FormData', function () { - var app; - before(function () { - return require('./test-server-formdata').then(function (testServer) { - app = testServer; - }); - }); - it('only required files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('required and optional files exists should pass', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile', 'LICENSE') - .attach('optionalFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('missing required file should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Missing required files: sourceFile'); - done(); - }); - }); - it('extra files exists but not allowed should fail', function (done) { - request(app) - .post('/pets/import') - .set('api-version', '1.0') - .attach('sourceFile1', 'LICENSE') - .attach('sourceFile', 'LICENSE') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body/files Extra files are not allowed. Not allowed files: sourceFile1'); - done(); - }); - }); - it('supports string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .field('password', 'pass') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('supports mix of files and fields', function (done) { - request(app) - .post('/kennels/import') - .set('api-version', '1.0') - .field('name', 'kennel 1 ') - .attach('blueprintFile', 'LICENSE') - .expect(200, function (err, res) { - if (err) { - throw err; - } - expect(res.body.result).to.equal('OK'); - done(); - }); - }); - it('validates string formData', function (done) { - request(app) - .post('/login') - .set('api-version', '1.0') - .field('username', 'user') - .expect(400, function (err, res) { - if (err) { - throw err; - } - expect(res.body.more_info).to.includes('body should have required property \'password\''); - done(); - }); - }); - }); -}); From b487f8cceb3a38a19bb913febbe7eeefdcd4ad38 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Mon, 21 Jan 2019 16:13:15 +0200 Subject: [PATCH 5/9] Rebase branch to 'master' of https://github.com/Zooz/express-ajv-swagger-validation --- test/middleware-test.js | 0 test/test-server-with-yaml-anchors.js | 30 --------------------------- 2 files changed, 30 deletions(-) delete mode 100644 test/middleware-test.js delete mode 100644 test/test-server-with-yaml-anchors.js diff --git a/test/middleware-test.js b/test/middleware-test.js deleted file mode 100644 index e69de29..0000000 diff --git a/test/test-server-with-yaml-anchors.js b/test/test-server-with-yaml-anchors.js deleted file mode 100644 index 9395dfc..0000000 --- a/test/test-server-with-yaml-anchors.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var express = require('express'); -var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); - -module.exports = inputValidation.init('test/pet-store-swagger-with-yaml-anchors.yaml') - .then(function () { - var app = express(); - app.use(bodyParser.json()); - app.get('/pets', inputValidation.validate, function (req, res, next) { - res.json({ result: 'OK' }); - }); - app.post('/pets', inputValidation.validate, function (req, res, next) { - res.json({ result: 'OK' }); - }); - app.get('/pets/:petId', inputValidation.validate, function (req, res, next) { - res.json({ result: 'OK' }); - }); - app.put('/pets', inputValidation.validate, function (req, res, next) { - res.json({ result: 'OK' }); - }); - app.use(function (err, req, res, next) { - if (err instanceof inputValidation.InputValidationError) { - res.status(400).json({ more_info: JSON.stringify(err.errors) }); - } - }); - - return Promise.resolve(app); - }); \ No newline at end of file From bc27855eed8a62ad6f8716166bae4ad1776a033d Mon Sep 17 00:00:00 2001 From: idantovi Date: Mon, 21 Jan 2019 21:55:36 +0200 Subject: [PATCH 6/9] fix tests --- test/{ => express}/test-server-with-yaml-anchors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/{ => express}/test-server-with-yaml-anchors.js (94%) diff --git a/test/test-server-with-yaml-anchors.js b/test/express/test-server-with-yaml-anchors.js similarity index 94% rename from test/test-server-with-yaml-anchors.js rename to test/express/test-server-with-yaml-anchors.js index 9395dfc..e0388ff 100644 --- a/test/test-server-with-yaml-anchors.js +++ b/test/express/test-server-with-yaml-anchors.js @@ -2,7 +2,7 @@ var express = require('express'); var bodyParser = require('body-parser'); -var inputValidation = require('../src/middleware'); +var inputValidation = require('../../src/middleware'); module.exports = inputValidation.init('test/pet-store-swagger-with-yaml-anchors.yaml') .then(function () { From 717e6e74c16c38e5afec54bebf9cc16b878aca92 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Wed, 23 Jan 2019 15:15:25 +0200 Subject: [PATCH 7/9] Dont run koa tests in v6 --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c7334c..3a802f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,11 @@ node_js: - "node" script: > node_version=$(node -v); - if [ ${node_version:1:1} -ne 6 ]; then - npm test - else - npm run node6-test - fi + if [ ${node_version:1:1} -ne 6 ]; then + npm test + else + npm run node6-test + fi after_success: > node_version=$(node -v); if [ ${node_version:1:1} -ne 6 ]; then From 1fc9f140a3ce4f00bf1962cb08abac7409753e41 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Wed, 23 Jan 2019 15:21:42 +0200 Subject: [PATCH 8/9] Dont run koa tests in v6 --- src/frameworks/express.js | 9 +++++---- src/middleware.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/frameworks/express.js b/src/frameworks/express.js index 7fe0021..cb737a6 100644 --- a/src/frameworks/express.js +++ b/src/frameworks/express.js @@ -12,11 +12,12 @@ function _getParameters(req) { return requestOptions; } -async function validate(validateRequest, req, res, next) { - let requestOptions, errors; +function validate(validateRequest, req, res, next) { + let requestOptions; requestOptions = _getParameters(req); - errors = await validateRequest(requestOptions); - next(errors); + validateRequest(requestOptions).then(function(errors) { + next(errors); + }); } module.exports = { diff --git a/src/middleware.js b/src/middleware.js index cebaff1..a1f62d5 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -102,7 +102,7 @@ function _getValidatedBodySchema(bodySchema) { * @param {any} next * @returns In case of an error will call `next` with `InputValidationError` */ -async function validate(...args) { +function validate(...args) { return framework.validate(_validateRequest, ...args); } From a9b56d327e3e203f2a396d0084e78b356d7c983b Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Mon, 28 Jan 2019 14:01:20 +0200 Subject: [PATCH 9/9] fix koa validation --- src/frameworks/koa.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frameworks/koa.js b/src/frameworks/koa.js index d5f6bc1..9e610d1 100644 --- a/src/frameworks/koa.js +++ b/src/frameworks/koa.js @@ -19,7 +19,7 @@ async function validate(validateRequest, ctx, next) { if (errors) { throw errors; } - next(); + await next(); } module.exports = {