From 8b6f9be176c478c3da059fd7d756f4efa0bb4143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 21 Dec 2016 03:05:21 +0100 Subject: [PATCH] Run test-operation on swagger-relative-refs.yaml too --- test/test-issues.js | 2 +- test/test-operation.js | 1074 ++++++++++++++++++++++++++++------------ 2 files changed, 758 insertions(+), 318 deletions(-) diff --git a/test/test-issues.js b/test/test-issues.js index b5920d1a..20d5e140 100644 --- a/test/test-issues.js +++ b/test/test-issues.js @@ -56,7 +56,7 @@ describe('issues', function () { .catch(function (err) { var errorMessages = [ 'Cannot read property \'consumes\' of null', // Node.js - 'null is not an object (evaluating \'definition.consumes\')' // PhantomJS (browser) + 'null is not an object (evaluating \'definitionFullyResolved.consumes\')' // PhantomJS (browser) ]; assert.ok(errorMessages.indexOf(err.message) > -1); diff --git a/test/test-operation.js b/test/test-operation.js index 60080e3d..e930da78 100644 --- a/test/test-operation.js +++ b/test/test-operation.js @@ -32,260 +32,638 @@ var helpers = require('./helpers'); var Sway = helpers.getSway(); describe('Operation', function () { - var swaggerApi; + context('swaggerApiRelativeRefs', function () { + var swaggerApiRelativePaths; - before(function (done) { - helpers.getSwaggerApi(function (api) { - swaggerApi = api; + before(function (done) { + helpers.getSwaggerApiRelativeRefs(function (api) { + swaggerApiRelativePaths = api; - done(); + done(); + }); }); - }); - it('should handle composite parameters', function () { - var method = 'post'; - var path = '/pet/{petId}'; - var operation = swaggerApi.getOperation(path, method); - var pathDef = swaggerApi.definitionFullyResolved.paths['/pet/{petId}']; + it('should handle composite parameters', function () { + var method = 'post'; + var path = '/pet/{petId}'; + + var operation = swaggerApiRelativePaths.getOperation(path, method); + var pathDef = swaggerApiRelativePaths.definitionFullyResolved.paths['/pet/{petId}']; - assert.equal(operation.pathObject.path, path); - assert.equal(operation.method, method); - assert.equal(operation.ptr, '#/paths/~1pet~1{petId}/' + method); + assert.equal(operation.pathObject.path, path); + assert.equal(operation.method, method); + assert.equal(operation.ptr, '#/paths/~1pet~1{petId}/' + method); + + _.each(operation.definition, function (val, key) { + assert.deepEqual(val, pathDef[method][key]); + }); - _.each(operation.definition, function (val, key) { - assert.deepEqual(val, pathDef[method][key]); + assert.equal(operation.parameterObjects.length, 3); }); - assert.equal(operation.parameterObjects.length, 3); - }); + it('should handle explicit parameters', function () { + var method = 'post'; + var path = '/pet/{petId}/uploadImage'; + var operation = swaggerApiRelativePaths.getOperation(path, method); + var pathDef = swaggerApiRelativePaths.definitionRemotesResolved.paths[path]; + var pathDefFullyResolved = swaggerApiRelativePaths.definitionFullyResolved.paths[path]; - it('should handle explicit parameters', function () { - var method = 'post'; - var path = '/pet/{petId}/uploadImage'; - var operation = swaggerApi.getOperation(path, method); - var pathDef = swaggerApi.definitionRemotesResolved.paths[path]; - var pathDefFullyResolved = swaggerApi.definitionFullyResolved.paths[path]; + assert.equal(operation.pathObject.path, path); + assert.equal(operation.method, method); + assert.equal(operation.ptr, '#/paths/~1pet~1{petId}~1uploadImage/post'); - assert.equal(operation.pathObject.path, path); - assert.equal(operation.method, method); - assert.equal(operation.ptr, '#/paths/~1pet~1{petId}~1uploadImage/post'); + _.each(operation.definition, function (val, key) { + if (key === 'security') { + assert.deepEqual(val, [ + { + 'petstore_auth': [ + 'read:pets', + 'write:pets' + ] + } + ]); + } else { + assert.deepEqual(val, pathDef[method][key]); + } + }); - _.each(operation.definition, function (val, key) { - if (key === 'security') { - assert.deepEqual(val, [ - { - 'petstore_auth': [ - 'read:pets', - 'write:pets' - ] - } - ]); - } else { - assert.deepEqual(val, pathDef[method][key]); - } + _.each(operation.definitionFullyResolved, function (val, key) { + if (key === 'security') { + assert.deepEqual(val, [ + { + 'petstore_auth': [ + 'read:pets', + 'write:pets' + ] + } + ]); + } else { + assert.deepEqual(val, pathDefFullyResolved[method][key]); + } + }); }); - _.each(operation.definitionFullyResolved, function (val, key) { - if (key === 'security') { - assert.deepEqual(val, [ - { - 'petstore_auth': [ - 'read:pets', - 'write:pets' - ] - } - ]); - } else { - assert.deepEqual(val, pathDefFullyResolved[method][key]); - } + it('should handle explicit parameters', function () { + assert.deepEqual(swaggerApiRelativePaths.getOperation('/user/{username}', 'get').security, [ + { + 'api_key': [] + } + ]); }); - }); - - it('should handle explicit parameters', function () { - assert.deepEqual(swaggerApi.getOperation('/user/{username}', 'get').security, [ - { - 'api_key': [] - } - ]); - }); - function validateRegExps (api, basePath) { - var createPet = api.getOperation('/pet', 'post'); - var updatePet = api.getOperation('/pet/{petId}', 'post'); + function validateRegExps (api, basePath) { + var createPet = api.getOperation('/pet', 'post'); + var updatePet = api.getOperation('/pet/{petId}', 'post'); - // Make sure they are of the proper type - assert.ok(createPet.pathObject.regexp instanceof RegExp); - assert.ok(updatePet.pathObject.regexp instanceof RegExp); + // Make sure they are of the proper type + assert.ok(createPet.pathObject.regexp instanceof RegExp); + assert.ok(updatePet.pathObject.regexp instanceof RegExp); - // Make sure they have the proper keys - assert.equal(0, createPet.pathObject.regexp.keys.length); - assert.equal(1, updatePet.pathObject.regexp.keys.length); - assert.equal('petId', updatePet.pathObject.regexp.keys[0].name); + // Make sure they have the proper keys + assert.equal(0, createPet.pathObject.regexp.keys.length); + assert.equal(1, updatePet.pathObject.regexp.keys.length); + assert.equal('petId', updatePet.pathObject.regexp.keys[0].name); - // Make sure they match the expected URLs - assert.ok(_.isArray(createPet.pathObject.regexp.exec(basePath + '/pet'))); - assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets'))); - assert.ok(_.isArray(updatePet.pathObject.regexp.exec(basePath + '/pet/1'))); - assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets/1'))); - } + // Make sure they match the expected URLs + assert.ok(_.isArray(createPet.pathObject.regexp.exec(basePath + '/pet'))); + assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets'))); + assert.ok(_.isArray(updatePet.pathObject.regexp.exec(basePath + '/pet/1'))); + assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets/1'))); + } - it('should create proper regexp (with basePath)', function () { - validateRegExps(swaggerApi, swaggerApi.basePath); - }); + it('should create proper regexp (with basePath)', function () { + validateRegExps(swaggerApiRelativePaths, swaggerApiRelativePaths.basePath); + }); - it('should create proper regexp (with basePath ending in slash)', function (done) { - var cSwagger = _.cloneDeep(helpers.swaggerDoc); + it('should create proper regexp (with basePath ending in slash)', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); - cSwagger.basePath = '/'; + cSwagger.basePath = '/'; - Sway.create({definition: cSwagger}) - .then(function (api) { - validateRegExps(api, ''); - }) - .then(done, done); - }); + Sway.create({definition: cSwagger}) + .then(function (api) { + validateRegExps(api, ''); + }) + .then(done, done); + }); - it('should create proper regexp (without basePath)', function (done) { - var cSwagger = _.cloneDeep(helpers.swaggerDoc); + it('should create proper regexp (without basePath)', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); - delete cSwagger.basePath; + delete cSwagger.basePath; - Sway.create({definition: cSwagger}) - .then(function (api) { - validateRegExps(api, ''); - }) - .then(done, done); - }); + Sway.create({definition: cSwagger}) + .then(function (api) { + validateRegExps(api, ''); + }) + .then(done, done); + }); - describe('#getParameter', function () { - it('should return the proper response', function (done) { - var cSwagger = _.cloneDeep(helpers.swaggerDoc); + describe('#getParameter', function () { + it('should return the proper response', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); - cSwagger.paths['/pet/{petId}'].get.parameters = [ - { - description: 'This is a duplicate name but different location', - name: 'petId', - in: 'query', - type: 'string' - } - ]; + cSwagger.paths['/pet/{petId}'].get.parameters = [ + { + description: 'This is a duplicate name but different location', + name: 'petId', + in: 'query', + type: 'string' + } + ]; - Sway.create({definition: cSwagger}) - .then(function (api) { - var operation = api.getOperation('/pet/{petId}', 'get'); - - assert.ok(_.isUndefined(operation.getParameter())); - assert.ok(_.isUndefined(operation.getParameter('missing'))); - assert.ok(_.isUndefined(operation.getParameter('petId', 'header'))); - assert.deepEqual(operation.getParameter('petId', 'path').definition, - cSwagger.paths['/pet/{petId}'].parameters[0]); - assert.deepEqual(operation.getParameter('petId', 'query').definition, - cSwagger.paths['/pet/{petId}'].get.parameters[0]); - }) - .then(done, done); + Sway.create({definition: cSwagger}) + .then(function (api) { + var operation = api.getOperation('/pet/{petId}', 'get'); + + assert.ok(_.isUndefined(operation.getParameter())); + assert.ok(_.isUndefined(operation.getParameter('missing'))); + assert.ok(_.isUndefined(operation.getParameter('petId', 'header'))); + assert.deepEqual(operation.getParameter('petId', 'path').definition, + cSwagger.paths['/pet/{petId}'].parameters[0]); + assert.deepEqual(operation.getParameter('petId', 'query').definition, + cSwagger.paths['/pet/{petId}'].get.parameters[0]); + }) + .then(done, done); + }); }); - }); - // More vigorous testing of the Parameter object itself and the parameter composition are done elsewhere - describe('#getParameters', function () { - it('should return the proper parameter objects', function () { - var operation = swaggerApi.getOperation('/pet/{petId}', 'post'); + // More vigorous testing of the Parameter object itself and the parameter composition are done elsewhere + describe('#getParameters', function () { + it('should return the proper parameter objects', function () { + var operation = swaggerApiRelativePaths.getOperation('/pet/{petId}', 'post'); - assert.deepEqual(operation.getParameters(), operation.parameterObjects); + assert.deepEqual(operation.getParameters(), operation.parameterObjects); + }); }); - }); - describe('#getSecurity', function () { - it('should return the proper parameter objects', function () { - var op1 = swaggerApi.getOperation('/pet/{petId}', 'post'); - var op2 = swaggerApi.getOperation('/store/inventory', 'get'); + describe('#getSecurity', function () { + it('should return the proper parameter objects', function () { + var op1 = swaggerApiRelativePaths.getOperation('/pet/{petId}', 'post'); + var op2 = swaggerApiRelativePaths.getOperation('/store/inventory', 'get'); - assert.notDeepEqual(op1.getSecurity, op1.security); - assert.deepEqual(op1.getSecurity(), swaggerApi.definition.security); + assert.notDeepEqual(op1.getSecurity, op1.security); + assert.deepEqual(op1.getSecurity(), swaggerApiRelativePaths.definition.security); - assert.deepEqual(op2.getSecurity(), op2.security); + assert.deepEqual(op2.getSecurity(), op2.security); + }); }); - }); - describe('#validateRequest', function () { - describe('validate Content-Type', function () { - var baseRequest = { - url: '/pet', - body: { - name: 'Test Pet', - photoUrls: [] - } - }; + describe('#validateRequest', function () { + describe('validate Content-Type', function () { + var baseRequest = { + url: '/pet', + body: { + name: 'Test Pet', + photoUrls: [] + } + }; + + describe('operation level consumes', function () { + var operation; + + before(function () { + operation = swaggerApiRelativePaths.getOperation('/pet', 'post'); + }); + + it('should return an error for an unsupported value', function () { + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = { + 'content-type': 'application/x-yaml' + }; + + results = operation.validateRequest(request); + + assert.equal(results.warnings.length, 0); + assert.equal(results.errors.length, 1); + }); + + it('should handle an undefined value (defaults to application/octet-stream)', function () { + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = {}; + + results = operation.validateRequest(request); - describe('operation level consumes', function () { - var operation; + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_CONTENT_TYPE', + message: 'Invalid Content-Type (application/octet-stream). ' + + 'These are supported: application/json, application/xml', + path: [] + } + ]); + }); + + it('should not return an error for a supported value', function () { + var request = _.cloneDeep(baseRequest); + var results; - before(function () { - operation = swaggerApi.getOperation('/pet', 'post'); + request.headers = { + 'content-type': 'application/json' + }; + + results = operation.validateRequest(request); + + assert.equal(results.warnings.length, 0); + assert.equal(results.errors.length, 0); + }); }); - it('should return an error for an unsupported value', function () { - var request = _.cloneDeep(baseRequest); - var results; + // We only need one test to make sure that we're using the global consumes - request.headers = { - 'content-type': 'application/x-yaml' - }; + it('should handle global level consumes', function (done) { + var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); - results = operation.validateRequest(request); + cSwaggerDoc.consumes = cSwaggerDoc.paths['/pet'].post.consumes; - assert.equal(results.warnings.length, 0); - assert.equal(results.errors.length, 1); + delete cSwaggerDoc.paths['/pet'].post.consumes; + + Sway.create({ + definition: cSwaggerDoc + }) + .then(function (api) { + var operation = api.getOperation('/pet', 'post'); + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = { + 'content-type': 'application/x-yaml' + }; + + results = operation.validateRequest(request); + + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_CONTENT_TYPE', + message: 'Invalid Content-Type (application/x-yaml). ' + + 'These are supported: application/json, application/xml', + path: [] + } + ]); + }) + .then(done, done); }); - it('should handle an undefined value (defaults to application/octet-stream)', function () { - var request = _.cloneDeep(baseRequest); - var results; + it('should handle mime-type parameters (exact match)', function (done) { + var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); + var mimeType = 'application/x-yaml; charset=utf-8'; + + cSwaggerDoc.paths['/pet'].post.consumes.push(mimeType); + + Sway.create({ + definition: cSwaggerDoc + }) + .then(function (api) { + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = { + 'content-type': mimeType + }; - request.headers = {}; + results = api.getOperation('/pet', 'post').validateRequest(request); - results = operation.validateRequest(request); + assert.equal(results.warnings.length, 0); + assert.equal(results.errors.length, 0); + }) + .then(done, done); + }); + }); + + describe('validate parameters', function () { + // We do not need to exhaustively test parameter validation since we're basically just relying on + // ParameterValue's validation and which is heavily tested elsewhere. + + it('should return an error for invalid non-primitive parameters', function () { + var operation = swaggerApiRelativePaths.getOperation('/pet', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet', + headers: { + 'content-type': 'application/json' + }, + body: {}, + files: {} + }); assert.equal(results.warnings.length, 0); assert.deepEqual(results.errors, [ { - code: 'INVALID_CONTENT_TYPE', - message: 'Invalid Content-Type (application/octet-stream). ' + - 'These are supported: application/json, application/xml', - path: [] + code: 'INVALID_REQUEST_PARAMETER', + errors: [ + { + code: 'OBJECT_MISSING_REQUIRED_PROPERTY', + message: 'Missing required property: photoUrls', + params: ['photoUrls'], + path: [] + }, + { + code: 'OBJECT_MISSING_REQUIRED_PROPERTY', + message: 'Missing required property: name', + params: ['name'], + path: [] + } + ], + in: 'body', + message: 'Invalid parameter (body): Value failed JSON Schema validation', + name: 'body', + path: ['paths', '/pet', 'post', 'parameters', '0'] } ]); }); - it('should not return an error for a supported value', function () { - var request = _.cloneDeep(baseRequest); - var results; + it('should return an error for invalid primitive parameters', function () { + var operation = swaggerApiRelativePaths.getOperation('/pet/{petId}/uploadImage', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet/notANumber/uploadImage', + headers: { + 'content-type': 'multipart/form-data' + }, + body: {}, + files: {} + }); - request.headers = { - 'content-type': 'application/json' - }; + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_REQUEST_PARAMETER', + errors: [ + { + code: 'INVALID_TYPE', + message: 'Expected type integer but found type string', + path: [] + } + ], + in: 'path', + message: 'Invalid parameter (petId): Expected type integer but found type string', + name: 'petId', + path: [] + } + ]); + }); - results = operation.validateRequest(request); + it('should not return an error for valid parameters', function () { + var operation = swaggerApiRelativePaths.getOperation('/pet/{petId}', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet/1', + headers: { + 'content-type': 'application/x-www-form-urlencoded' + }, + body: { + name: 'New Pet', + status: 'available' + } + }); - assert.equal(results.warnings.length, 0); assert.equal(results.errors.length, 0); + assert.equal(results.warnings.length, 0); }); }); + }); - // We only need one test to make sure that we're using the global consumes + describe('#validateResponse', function () { + // We only test that Operation#validateResponse handles missing responses because the testing of the remainder + // is in test-response.js. + describe('should return an error for undefined response', function () { + it('undefined value but no default', function () { + var results = swaggerApiRelativePaths.getOperation('/pet', 'post').validateResponse(); - it('should handle global level consumes', function (done) { - var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); + assert.deepEqual(results.warnings, []); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_RESPONSE_CODE', + message: 'This operation does not have a defined \'default\' response code', + path: [] + } + ]); + }); - cSwaggerDoc.consumes = cSwaggerDoc.paths['/pet'].post.consumes; + it('provided value', function () { + var results = swaggerApiRelativePaths.getOperation('/pet/{petId}', 'post').validateResponse({ + statusCode: 201 + }); - delete cSwaggerDoc.paths['/pet'].post.consumes; + assert.deepEqual(results.warnings, []); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_RESPONSE_CODE', + message: 'This operation does not have a defined \'201\' or \'default\' response code', + path: [] + } + ]); + }); + }); - Sway.create({ - definition: cSwaggerDoc - }) + it('should return the \'default\' response when validating an undefined response', function () { + var results = swaggerApiRelativePaths.getOperation('/user', 'post').validateResponse({ + statusCode: 201 + }); + + assert.deepEqual(results.errors, []); + assert.deepEqual(results.warnings, []); + }); + }); + }); + + context('swaggerApi', function () { + var swaggerApi; + + before(function (done) { + helpers.getSwaggerApi(function (api) { + swaggerApi = api; + + done(); + }); + }); + + it('should handle composite parameters', function () { + var method = 'post'; + var path = '/pet/{petId}'; + var operation = swaggerApi.getOperation(path, method); + var pathDef = swaggerApi.definitionFullyResolved.paths['/pet/{petId}']; + + assert.equal(operation.pathObject.path, path); + assert.equal(operation.method, method); + assert.equal(operation.ptr, '#/paths/~1pet~1{petId}/' + method); + + _.each(operation.definition, function (val, key) { + assert.deepEqual(val, pathDef[method][key]); + }); + + assert.equal(operation.parameterObjects.length, 3); + }); + + it('should handle explicit parameters', function () { + var method = 'post'; + var path = '/pet/{petId}/uploadImage'; + var operation = swaggerApi.getOperation(path, method); + var pathDef = swaggerApi.definitionRemotesResolved.paths[path]; + var pathDefFullyResolved = swaggerApi.definitionFullyResolved.paths[path]; + + assert.equal(operation.pathObject.path, path); + assert.equal(operation.method, method); + assert.equal(operation.ptr, '#/paths/~1pet~1{petId}~1uploadImage/post'); + + _.each(operation.definition, function (val, key) { + if (key === 'security') { + assert.deepEqual(val, [ + { + 'petstore_auth': [ + 'read:pets', + 'write:pets' + ] + } + ]); + } else { + assert.deepEqual(val, pathDef[method][key]); + } + }); + + _.each(operation.definitionFullyResolved, function (val, key) { + if (key === 'security') { + assert.deepEqual(val, [ + { + 'petstore_auth': [ + 'read:pets', + 'write:pets' + ] + } + ]); + } else { + assert.deepEqual(val, pathDefFullyResolved[method][key]); + } + }); + }); + + it('should handle explicit parameters', function () { + assert.deepEqual(swaggerApi.getOperation('/user/{username}', 'get').security, [ + { + 'api_key': [] + } + ]); + }); + + function validateRegExps (api, basePath) { + var createPet = api.getOperation('/pet', 'post'); + var updatePet = api.getOperation('/pet/{petId}', 'post'); + + // Make sure they are of the proper type + assert.ok(createPet.pathObject.regexp instanceof RegExp); + assert.ok(updatePet.pathObject.regexp instanceof RegExp); + + // Make sure they have the proper keys + assert.equal(0, createPet.pathObject.regexp.keys.length); + assert.equal(1, updatePet.pathObject.regexp.keys.length); + assert.equal('petId', updatePet.pathObject.regexp.keys[0].name); + + // Make sure they match the expected URLs + assert.ok(_.isArray(createPet.pathObject.regexp.exec(basePath + '/pet'))); + assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets'))); + assert.ok(_.isArray(updatePet.pathObject.regexp.exec(basePath + '/pet/1'))); + assert.ok(!_.isArray(createPet.pathObject.regexp.exec(basePath + '/pets/1'))); + } + + it('should create proper regexp (with basePath)', function () { + validateRegExps(swaggerApi, swaggerApi.basePath); + }); + + it('should create proper regexp (with basePath ending in slash)', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); + + cSwagger.basePath = '/'; + + Sway.create({definition: cSwagger}) + .then(function (api) { + validateRegExps(api, ''); + }) + .then(done, done); + }); + + it('should create proper regexp (without basePath)', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); + + delete cSwagger.basePath; + + Sway.create({definition: cSwagger}) + .then(function (api) { + validateRegExps(api, ''); + }) + .then(done, done); + }); + + describe('#getParameter', function () { + it('should return the proper response', function (done) { + var cSwagger = _.cloneDeep(helpers.swaggerDoc); + + cSwagger.paths['/pet/{petId}'].get.parameters = [ + { + description: 'This is a duplicate name but different location', + name: 'petId', + in: 'query', + type: 'string' + } + ]; + + Sway.create({definition: cSwagger}) .then(function (api) { - var operation = api.getOperation('/pet', 'post'); + var operation = api.getOperation('/pet/{petId}', 'get'); + + assert.ok(_.isUndefined(operation.getParameter())); + assert.ok(_.isUndefined(operation.getParameter('missing'))); + assert.ok(_.isUndefined(operation.getParameter('petId', 'header'))); + assert.deepEqual(operation.getParameter('petId', 'path').definition, + cSwagger.paths['/pet/{petId}'].parameters[0]); + assert.deepEqual(operation.getParameter('petId', 'query').definition, + cSwagger.paths['/pet/{petId}'].get.parameters[0]); + }) + .then(done, done); + }); + }); + + // More vigorous testing of the Parameter object itself and the parameter composition are done elsewhere + describe('#getParameters', function () { + it('should return the proper parameter objects', function () { + var operation = swaggerApi.getOperation('/pet/{petId}', 'post'); + + assert.deepEqual(operation.getParameters(), operation.parameterObjects); + }); + }); + + describe('#getSecurity', function () { + it('should return the proper parameter objects', function () { + var op1 = swaggerApi.getOperation('/pet/{petId}', 'post'); + var op2 = swaggerApi.getOperation('/store/inventory', 'get'); + + assert.notDeepEqual(op1.getSecurity, op1.security); + assert.deepEqual(op1.getSecurity(), swaggerApi.definition.security); + + assert.deepEqual(op2.getSecurity(), op2.security); + }); + }); + + describe('#validateRequest', function () { + describe('validate Content-Type', function () { + var baseRequest = { + url: '/pet', + body: { + name: 'Test Pet', + photoUrls: [] + } + }; + + describe('operation level consumes', function () { + var operation; + + before(function () { + operation = swaggerApi.getOperation('/pet', 'post'); + }); + + it('should return an error for an unsupported value', function () { var request = _.cloneDeep(baseRequest); var results; @@ -295,175 +673,237 @@ describe('Operation', function () { results = operation.validateRequest(request); + assert.equal(results.warnings.length, 0); + assert.equal(results.errors.length, 1); + }); + + it('should handle an undefined value (defaults to application/octet-stream)', function () { + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = {}; + + results = operation.validateRequest(request); + assert.equal(results.warnings.length, 0); assert.deepEqual(results.errors, [ { code: 'INVALID_CONTENT_TYPE', - message: 'Invalid Content-Type (application/x-yaml). ' + - 'These are supported: application/json, application/xml', + message: 'Invalid Content-Type (application/octet-stream). ' + + 'These are supported: application/json, application/xml', path: [] } ]); - }) - .then(done, done); - }); - - it('should handle mime-type parameters (exact match)', function (done) { - var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); - var mimeType = 'application/x-yaml; charset=utf-8'; + }); - cSwaggerDoc.paths['/pet'].post.consumes.push(mimeType); - - Sway.create({ - definition: cSwaggerDoc - }) - .then(function (api) { + it('should not return an error for a supported value', function () { var request = _.cloneDeep(baseRequest); var results; request.headers = { - 'content-type': mimeType + 'content-type': 'application/json' }; - results = api.getOperation('/pet', 'post').validateRequest(request); + results = operation.validateRequest(request); assert.equal(results.warnings.length, 0); assert.equal(results.errors.length, 0); - }) - .then(done, done); - }); - }); + }); + }); + + // We only need one test to make sure that we're using the global consumes - describe('validate parameters', function () { - // We do not need to exhaustively test parameter validation since we're basically just relying on - // ParameterValue's validation and which is heavily tested elsewhere. - - it('should return an error for invalid non-primitive parameters', function () { - var operation = swaggerApi.getOperation('/pet', 'post'); - var results = operation.validateRequest({ - url: '/v2/pet', - headers: { - 'content-type': 'application/json' - }, - body: {}, - files: {} + it('should handle global level consumes', function (done) { + var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); + + cSwaggerDoc.consumes = cSwaggerDoc.paths['/pet'].post.consumes; + + delete cSwaggerDoc.paths['/pet'].post.consumes; + + Sway.create({ + definition: cSwaggerDoc + }) + .then(function (api) { + var operation = api.getOperation('/pet', 'post'); + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = { + 'content-type': 'application/x-yaml' + }; + + results = operation.validateRequest(request); + + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_CONTENT_TYPE', + message: 'Invalid Content-Type (application/x-yaml). ' + + 'These are supported: application/json, application/xml', + path: [] + } + ]); + }) + .then(done, done); }); - assert.equal(results.warnings.length, 0); - assert.deepEqual(results.errors, [ - { - code: 'INVALID_REQUEST_PARAMETER', - errors: [ - { - code: 'OBJECT_MISSING_REQUIRED_PROPERTY', - message: 'Missing required property: photoUrls', - params: ['photoUrls'], - path: [] - }, - { - code: 'OBJECT_MISSING_REQUIRED_PROPERTY', - message: 'Missing required property: name', - params: ['name'], - path: [] - } - ], - in: 'body', - message: 'Invalid parameter (body): Value failed JSON Schema validation', - name: 'body', - path: ['paths', '/pet', 'post', 'parameters', '0'] - } - ]); + it('should handle mime-type parameters (exact match)', function (done) { + var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc); + var mimeType = 'application/x-yaml; charset=utf-8'; + + cSwaggerDoc.paths['/pet'].post.consumes.push(mimeType); + + Sway.create({ + definition: cSwaggerDoc + }) + .then(function (api) { + var request = _.cloneDeep(baseRequest); + var results; + + request.headers = { + 'content-type': mimeType + }; + + results = api.getOperation('/pet', 'post').validateRequest(request); + + assert.equal(results.warnings.length, 0); + assert.equal(results.errors.length, 0); + }) + .then(done, done); + }); }); - it('should return an error for invalid primitive parameters', function () { - var operation = swaggerApi.getOperation('/pet/{petId}/uploadImage', 'post'); - var results = operation.validateRequest({ - url: '/v2/pet/notANumber/uploadImage', - headers: { - 'content-type': 'multipart/form-data' - }, - body: {}, - files: {} + describe('validate parameters', function () { + // We do not need to exhaustively test parameter validation since we're basically just relying on + // ParameterValue's validation and which is heavily tested elsewhere. + + it('should return an error for invalid non-primitive parameters', function () { + var operation = swaggerApi.getOperation('/pet', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet', + headers: { + 'content-type': 'application/json' + }, + body: {}, + files: {} + }); + + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_REQUEST_PARAMETER', + errors: [ + { + code: 'OBJECT_MISSING_REQUIRED_PROPERTY', + message: 'Missing required property: photoUrls', + params: ['photoUrls'], + path: [] + }, + { + code: 'OBJECT_MISSING_REQUIRED_PROPERTY', + message: 'Missing required property: name', + params: ['name'], + path: [] + } + ], + in: 'body', + message: 'Invalid parameter (body): Value failed JSON Schema validation', + name: 'body', + path: ['paths', '/pet', 'post', 'parameters', '0'] + } + ]); }); - assert.equal(results.warnings.length, 0); - assert.deepEqual(results.errors, [ - { - code: 'INVALID_REQUEST_PARAMETER', - errors: [ - { - code: 'INVALID_TYPE', - message: 'Expected type integer but found type string', - path: [] - } - ], - in: 'path', - message: 'Invalid parameter (petId): Expected type integer but found type string', - name: 'petId', - path: [] - } - ]); - }); + it('should return an error for invalid primitive parameters', function () { + var operation = swaggerApi.getOperation('/pet/{petId}/uploadImage', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet/notANumber/uploadImage', + headers: { + 'content-type': 'multipart/form-data' + }, + body: {}, + files: {} + }); - it('should not return an error for valid parameters', function () { - var operation = swaggerApi.getOperation('/pet/{petId}', 'post'); - var results = operation.validateRequest({ - url: '/v2/pet/1', - headers: { - 'content-type': 'application/x-www-form-urlencoded' - }, - body: { - name: 'New Pet', - status: 'available' - } + assert.equal(results.warnings.length, 0); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_REQUEST_PARAMETER', + errors: [ + { + code: 'INVALID_TYPE', + message: 'Expected type integer but found type string', + path: [] + } + ], + in: 'path', + message: 'Invalid parameter (petId): Expected type integer but found type string', + name: 'petId', + path: [] + } + ]); }); - assert.equal(results.errors.length, 0); - assert.equal(results.warnings.length, 0); + it('should not return an error for valid parameters', function () { + var operation = swaggerApi.getOperation('/pet/{petId}', 'post'); + var results = operation.validateRequest({ + url: '/v2/pet/1', + headers: { + 'content-type': 'application/x-www-form-urlencoded' + }, + body: { + name: 'New Pet', + status: 'available' + } + }); + + assert.equal(results.errors.length, 0); + assert.equal(results.warnings.length, 0); + }); }); }); - }); - describe('#validateResponse', function () { - // We only test that Operation#validateResponse handles missing responses because the testing of the remainder - // is in test-response.js. - describe('should return an error for undefined response', function () { - it('undefined value but no default', function () { - var results = swaggerApi.getOperation('/pet', 'post').validateResponse(); + describe('#validateResponse', function () { + // We only test that Operation#validateResponse handles missing responses because the testing of the remainder + // is in test-response.js. + describe('should return an error for undefined response', function () { + it('undefined value but no default', function () { + var results = swaggerApi.getOperation('/pet', 'post').validateResponse(); - assert.deepEqual(results.warnings, []); - assert.deepEqual(results.errors, [ - { - code: 'INVALID_RESPONSE_CODE', - message: 'This operation does not have a defined \'default\' response code', - path: [] - } - ]); + assert.deepEqual(results.warnings, []); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_RESPONSE_CODE', + message: 'This operation does not have a defined \'default\' response code', + path: [] + } + ]); + }); + + it('provided value', function () { + var results = swaggerApi.getOperation('/pet/{petId}', 'post').validateResponse({ + statusCode: 201 + }); + + assert.deepEqual(results.warnings, []); + assert.deepEqual(results.errors, [ + { + code: 'INVALID_RESPONSE_CODE', + message: 'This operation does not have a defined \'201\' or \'default\' response code', + path: [] + } + ]); + }); }); - it('provided value', function () { - var results = swaggerApi.getOperation('/pet/{petId}', 'post').validateResponse({ + it('should return the \'default\' response when validating an undefined response', function () { + var results = swaggerApi.getOperation('/user', 'post').validateResponse({ statusCode: 201 }); + assert.deepEqual(results.errors, []); assert.deepEqual(results.warnings, []); - assert.deepEqual(results.errors, [ - { - code: 'INVALID_RESPONSE_CODE', - message: 'This operation does not have a defined \'201\' or \'default\' response code', - path: [] - } - ]); }); }); - - it('should return the \'default\' response when validating an undefined response', function () { - var results = swaggerApi.getOperation('/user', 'post').validateResponse({ - statusCode: 201 - }); - - assert.deepEqual(results.errors, []); - assert.deepEqual(results.warnings, []); - }); }); });