diff --git a/.gitignore b/.gitignore index 7be1880..f56a81c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ html-report xunit.xml node_modules npm-debug.log +package-lock.json .project .idea diff --git a/lib/generators/format.js b/lib/generators/v2/format.js similarity index 100% rename from lib/generators/format.js rename to lib/generators/v2/format.js diff --git a/lib/generators/index.js b/lib/generators/v2/index.js similarity index 100% rename from lib/generators/index.js rename to lib/generators/v2/index.js diff --git a/lib/generators/paramtypes.js b/lib/generators/v2/paramtypes.js similarity index 100% rename from lib/generators/paramtypes.js rename to lib/generators/v2/paramtypes.js diff --git a/lib/generators/v3/format.js b/lib/generators/v3/format.js new file mode 100644 index 0000000..93b70eb --- /dev/null +++ b/lib/generators/v3/format.js @@ -0,0 +1,28 @@ +'use strict'; +const Moment = require('moment'); +const Chance = require('chance').Chance(); +const Randexp = require('randexp').randexp; + +const date = () => Moment().format('YYYY-MM-DD'); +const dataTime = () => Moment().toISOString(); +const url = () => Chance.url(); +const email = () => Chance.email(); +const phone = () => Chance.phone(); +const guid = () => Chance.guid(); +const ipv4 = () => Chance.ip(); +const ipv6 = () => Chance.ipv6(); +const hostname = () => Randexp(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/gm); + +module.exports = { + date, + 'date-time': dataTime, + uri: url, + url, + email, + phone, + uuid: guid, + guid, + ipv4, + ipv6, + hostname +}; diff --git a/lib/generators/v3/index.js b/lib/generators/v3/index.js new file mode 100644 index 0000000..e9720e0 --- /dev/null +++ b/lib/generators/v3/index.js @@ -0,0 +1,239 @@ +'use strict'; +const Chance = require('chance').Chance(); +const Format = require('./format'); +const Randexp = require('randexp').randexp; + +const mock = (node, useExample) => { + let mock; + + // Parameters and responses live inside the schema keyword now + const schema = node.schema; + + if (schema) { + let type = schema.type || findType(schema); + let example = schema.examples || schema.example || schema.default; + /** + * Get the mock generator from the `type` of the schema + */ + if (example && useExample) { + mock = example; + } else { + const generator = Generators[type]; + if (generator) { + mock = generator.call(null, schema, useExample); + } + } + } + return mock; +}; + +const objectMock = ({ properties, additionalProperties }, useExample ) => { + let mockObj = {}; + if (properties) { + Object.keys(properties).forEach(function (key) { + mockObj[key] = mock(properties[key], useExample); + }); + /** + * In the absense of `properties`, check if `additionalProperties` is defined or not. + * (If additionalProperties is an object, that object is a schema that will be used to validate + * any additional properties not listed in properties.) + * + * If present, use this to generate mocks. + */ + } else if (additionalProperties) { + //Create a random property + mockObj[Chance.word()] = mock(additionalProperties, useExample); + } + return mockObj; +}; +/** + * Generates a mock `array` data of `items` + * Supports: `minItems` and `maxItems` + * TODO: Implement `uniqueItems` + */ +const arrayMock = ({ items, minItems, maxItems }, useExample) => { + let min; + let max; + let numItems; + let arr = []; + + if (items) { + //Use the min as the base + min = minItems || 1; + if (maxItems) { + //If min is greater than max, use min as max. + max = (maxItems < min) ? min : maxItems; + } else { + //If max is not defined, use min as max. + max = min; + } + //Find the number of items with min and max boundary parameters. + numItems = Chance.integer({ + min: min, + max: max + }); + for (let i = 0; i < numItems; i++) { + arr.push(mock(items, useExample)); + } + } + return arr; +}; +/** + * Generates a mock `integer` value + * Supports `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum` + * TODO - Validate `minimum` and `maximum` values + */ +const integerMock = schema => { + let opts = {}; + let intmock; + + /** + * If `enum` is defined for the property + */ + if (schema.enum && schema.enum.length > 0) { + return enumMock(schema); + } + + if (Number.isInteger(schema.minimum)) { + opts.min = (schema.exclusiveMinimum) ? schema.minimum + 1 : schema.minimum; + } + if (Number.isInteger(schema.maximum)) { + opts.max = (schema.exclusiveMaximum) ? schema.maximum - 1 : schema.maximum; + } + //Generate a number that is multiple of schema.multipleOf + if (Number.isInteger(schema.multipleOf) && schema.multipleOf > 0) { + //Use the min/muplilier as the min number + //Use default min as 1 if min is not properly set. + opts.min = (Number.isInteger(opts.min)) ? (Math.ceil(opts.min / schema.multipleOf)) : 1; + //Use the max/muplilier as the new max value + //Use a default - min + 10 - if max value is not properly set. + opts.max = (Number.isInteger(opts.max)) ? (Math.floor(opts.max / schema.multipleOf)) : (opts.min + 10); + intmock = Chance.integer(opts); + intmock = intmock * schema.multipleOf; + } else { + intmock = Chance.integer(opts); + } + return intmock; +}; + +/** + * Generates a mock `number` value + * Supports `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum` + * TODO - Validate `minimum` and `maximum` values + */ +const numberMock = schema => { + let opts = {}; + let nummock; + + /** + * If `enum` is defined for the property + */ + if (schema.enum && schema.enum.length > 0) { + return enumMock(schema); + } + + if (Number.isFinite(schema.minimum)) { + opts.min = (schema.exclusiveMinimum) ? schema.minimum + 0.1 : schema.minimum; + } + if (Number.isFinite(schema.maximum)) { + opts.max = (schema.exclusiveMaximum) ? schema.maximum - 0.1 : schema.maximum ; + } + //Generate a number that is multiple of schema.multipleOf + if (Number.isFinite(schema.multipleOf) && schema.multipleOf > 0) { + //Use the min/muplilier as the min number + //Use default min as 1 if min is not properly set + opts.min = (Number.isFinite(opts.min)) ? (Math.ceil(opts.min / schema.multipleOf)) : 1; + //Use the max/muplilier as the new max value + //Use a default - min + 10 - if max value is not properly set. + opts.max = (Number.isFinite(opts.max)) ? (Math.floor(opts.max / schema.multipleOf)) : (opts.min + 10); + + nummock = Chance.integer(opts); + nummock = nummock * schema.multipleOf; + } else { + nummock = Chance.floating(opts); + } + return nummock; +}; + +const booleanMock = schema => { + /** + * If `enum` is defined for the property + */ + if (schema.enum && schema.enum.length > 0) { + return enumMock(schema); + } + return Chance.bool(); +}; +/** + * Geneartes a mock `string` value + * Supports: `minLength`, `maxLength`, `enum`, `date`, and `date-time` + * + */ +const stringMock = schema => { + let mockStr; + let opts = {}; + let minLength = schema.minLength || 1; + let maxLength = schema.maxLength || minLength + 10; + opts.min = minLength; + opts.max = maxLength; + + if (schema.enum && schema.enum.length > 0) { + /** + * If `enum` is defined for the property + */ + mockStr = enumMock(schema); + } else if (schema.pattern) { + /** + * If `pattern` is defined for the property + */ + mockStr = Randexp(schema.pattern); + } else if(Format[schema.format]) { + /** + * If a `format` is defined for the property + */ + mockStr = Format[schema.format].call(null, schema); + } else { + mockStr = Chance.string({ + length: Chance.integer(opts), + alpha: true //Use only alpha characters + }); + } + + return mockStr; +}; + +const enumMock = schema => { + let len = schema.enum.length; + let opts = { + min: 0, + max: len - 1 + }; + return schema.enum[Chance.integer(opts)]; +}; + +const fileMock = () => { + return Chance.file(); +}; + +//Find out the type based on schema props +//(This is not a complete list or full proof solution) +const findType = schema => { + let type = 'object';// Use 'object' as the default type + if (schema.pattern) { + type = 'string'; + } else if (schema.items) { + type = 'array'; + } + return type; +}; + +const Generators = module.exports = { + object: objectMock, + array: arrayMock, + string: stringMock, + integer: integerMock, + number: numberMock, + boolean: booleanMock, + file: fileMock, + mock +}; diff --git a/lib/generators/v3/paramtypes.js b/lib/generators/v3/paramtypes.js new file mode 100644 index 0000000..76afe31 --- /dev/null +++ b/lib/generators/v3/paramtypes.js @@ -0,0 +1,74 @@ +const Generators = require('./index'); + +const defaultStyle = { + query: 'form', + path: 'simple', + header: 'simple', + cookie: 'form' +}; + +const arrayStyles = { + // matrix: val => ';' + val.join(','), + // label: val => '.' + val.join('.'), + // form: val => val.join('\t'), + simple: val => val.join(','), + spaceDelimited: val => val.join('%20'), + pipeDelimited: val => val.join('|'), + // deepObject: val => val.join('|'), +}; + +/** + * TODO : Handle type `file` + */ +const queryMock = param => { + // Describes how the parameter value will be serialized depending on the type of the parameter + // value. Default values (based on value of in): for query - form; for path - simple; for header + // - simple; for cookie - form. + if (typeof param.style !== 'string') { + param.style = defaultStyle[param.in]; + } + + // When this is true, parameter values of type array or object generate separate parameters for + // each value of the array or key-value pair of the map. For other types of parameters this property + // has no effect. When style is form, the default value is true. For all other styles, the default + // value is false. + if (typeof param.explode === 'undefined') { + param.explode = param.style == 'form'; + } + + let value = Generators.mock(param); + + if (param.type === 'array') { + const collector = arrayStyles[param.style] || arrayStyles.simple; + + console.log({collector}) + console.log({value}) + value = collector(value, param.explode); + console.log({value}) + + } + + // TODO Support object collection too https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#style-values + if (param.type === 'object') { + } + + return { + name: param.name, + value: value + }; +}; + +const bodyMock = param => { + return { + name: param.name, + value: Generators.mock(param.schema) + }; +}; + +module.exports = { + query: queryMock, + path: queryMock, + formData: queryMock, + header: queryMock, + body: bodyMock +}; diff --git a/lib/index.js b/lib/index.js index a988b20..1f86942 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,8 @@ const Parser = require('swagger-parser'); -const Generators = require('./generators'); -const ParamTypes = require('./generators/paramtypes'); +const Generators2 = require('./generators/v2'); +const ParamTypes2 = require('./generators/v2/paramtypes'); +const Generators3 = require('./generators/v3'); +const ParamTypes3 = require('./generators/v3/paramtypes'); const Querystring = require('querystring'); const Maybe = require('call-me-maybe'); @@ -32,8 +34,8 @@ SwagMock.prototype.requests = function(options = {}, callback) { }; SwagMock.prototype.mock = function(options = {}) { - return this.swagger.then(api => { + options.version = (api.swagger === '2.0') ? '2.0' : api.openapi; return mockSchema(api, options); }); }; @@ -119,13 +121,13 @@ const mockResponses = (opsObj, options) => { let response = responses[options.response]; if (response) { //Found the response - mockResp = mockResponse(response, options.useExamples); + mockResp = mockResponse(response, options); } else { mockResp = mockResp || {}; Object.keys(responses).forEach(function(responseStr) { let response = responses[responseStr]; if (response) { - mockResp[responseStr] = mockResponse(response, options.useExamples); + mockResp[responseStr] = mockResponse(response, options); } }); } @@ -135,20 +137,39 @@ const mockResponses = (opsObj, options) => { /** * */ -const mockResponse = (response, useExamples) => { +const mockResponse = (response, { version, useExamples }) => { let mockResp; let schema = response.schema; if (schema) { - mockResp = Generators.mock(schema, useExamples); + const generators = appropriateGenerators(version); + mockResp = generators.mock(schema, useExamples); } return mockResp; }; + + +const appropriateGenerators = version => { + if (version.match(version.match(/^3\./))) { + return Generators3; + } else { + return Generators2; + } +} + +const appropriateParamTypes = version => { + if (version.match(version.match(/^3\./))) { + return ParamTypes3; + } else { + return ParamTypes2; + } +} /** * Generate a mock parameter list * */ -const mockParameters = (resolved, opsObj) => { +const mockParameters = (resolved, opsObj, { version }) => { let mockParam = {}; + const paramTypes = appropriateParamTypes(version); //Combine common parameters let parameters = mergeParams(resolved.commonParams, opsObj.parameters); if (parameters.length > 0) { @@ -157,10 +178,10 @@ const mockParameters = (resolved, opsObj) => { // `in` - The location of the parameter. // Possible values are "query", "header", "path", "formData" or "body". let paramType = param.in; - if (ParamTypes[paramType]) { + if (paramTypes[paramType]) { //Found the Mock generator for the param type (AKA `location`, AKA `in`). mockParam[paramType] = mockParam[paramType] || []; - mockParam[paramType].push(ParamTypes[paramType].call(null, param)); + mockParam[paramType].push(paramTypes[paramType].call(null, param)); } }); } diff --git a/package.json b/package.json index 8b13b21..b9f0fdd 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "scripts": { "lint": "eslint lib", - "test": "mocha tests/*.js", + "test": "mocha tests/**/*.js", "cover": "istanbul cover _mocha -- tests/*.js" }, "repository": { @@ -34,7 +34,7 @@ "chance": "^1.0.3", "moment": "^2.13.0", "randexp": "^0.4.2", - "swagger-parser": "^3.4.1" + "swagger-parser": "^5.0" }, "devDependencies": { "eslint": "^2.13.0", diff --git a/tests/fixture/petstore-v3.json b/tests/fixture/petstore-v3.json new file mode 100644 index 0000000..8f38718 --- /dev/null +++ b/tests/fixture/petstore-v3.json @@ -0,0 +1,1127 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1000, + "maximum": 2000 + } + }, + { + "name": "petName", + "in": "query", + "description": "Name of pet to return", + "required": true, + "schema": { + "type": "string", + "pattern": "awesome+ (pet|cat|bird)" + } + }, + { + "name": "petWeight", + "in": "query", + "description": "Weight of pet to return", + "required": false, + "schema": { + "type": "number", + "minimum": 10, + "maximum": 500 + } + }, + { + "name": "bmi", + "in": "query", + "description": "bmi of the pet", + "required": false, + "schema": { + "type": "number", + "minimum": 0, + "maximum": 1, + "multipleOf": 0.2 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [ + + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "name": { + "description": "Updated name of the pet", + "type": "string" + }, + "status": { + "description": "Updated status of the pet", + "type": "string" + } + } + } + } + } + } + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1000, + "maximum": 1010, + "exclusiveMinimum": true, + "exclusiveMaximum": true + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "additionalMetadata": { + "description": "Additional data to pass to server", + "type": "string" + }, + "file": { + "description": "file to upload", + "type": "string", + "format": "binary" + } + } + } + } + } + } + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "security": [ + { + "api_key": [ + + ] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid Order" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "description": "order placed for purchasing the pet", + "required": true + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1, + "maximum": 10, + "multipleOf": 2 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Created user object", + "required": true + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "description": "List of user object", + "required": true + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "maxItems": 4, + "items": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "description": "List of user object", + "required": true + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "responses": { + "default": { + "description": "successful operation" + } + } + }, + "parameters": [ + { + "name": "common", + "in": "query", + "description": "A common parameter", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Updated user object", + "required": true + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v2" + } + ], + "components": { + "requestBodies": { + "Pet": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "description": "Pet object that needs to be added to the store", + "required": true + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "enum": [ + 1, + 3, + 5 + ], + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "multipleOf": 100, + "minimum": 1000, + "exclusiveMinimum": true, + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "minItems": 2, + "maxItems": 5, + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + } + } \ No newline at end of file diff --git a/tests/param_mockgen.js b/tests/v2/param_mockgen.js old mode 100644 new mode 100755 similarity index 93% rename from tests/param_mockgen.js rename to tests/v2/param_mockgen.js index 2d45b1a..75d1f7d --- a/tests/param_mockgen.js +++ b/tests/v2/param_mockgen.js @@ -1,10 +1,10 @@ const Assert = require('assert'); -const Swagmock = require('../lib'); +const Swagmock = require('../../lib'); const Path = require('path'); const Parser = require('swagger-parser'); describe('Parameter Mock generator', () => { - let apiPath = Path.resolve(__dirname, 'fixture/petstore.json'); + let apiPath = Path.resolve(__dirname, '../fixture/petstore.json'); let apiResolver = Parser.validate(apiPath); //Test case of valiadted api use case. let swagmock = Swagmock(apiResolver, { validated: true }); @@ -13,7 +13,7 @@ describe('Parameter Mock generator', () => { path: '/store/order/{orderId}', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -29,13 +29,13 @@ describe('Parameter Mock generator', () => { path: '/pet/findByStatus', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); Assert.ok(params.query, 'Generated query parameter'); Assert.ok(params.query[0].name === 'status', 'generated mock parameter for status'); - Assert.ok(params.query[0].value, 'OK value for status'); + Assert.ok(typeof params.query[0].value[0] === 'string', 'OK value for status'); Assert.ok(params.query[0].separator === 'multi' , 'OK multi separator'); done(); }); @@ -46,7 +46,7 @@ describe('Parameter Mock generator', () => { path: '/pet/{petId}', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -80,7 +80,7 @@ describe('Parameter Mock generator', () => { path: '/pet/{petId}', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -97,7 +97,7 @@ describe('Parameter Mock generator', () => { path: '/pet/{petId}/uploadImage', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -117,7 +117,7 @@ describe('Parameter Mock generator', () => { path: '/store/inventory', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -130,7 +130,7 @@ describe('Parameter Mock generator', () => { path: '/store/order', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -153,7 +153,7 @@ describe('Parameter Mock generator', () => { path: '/user/createWithArray', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); @@ -179,7 +179,7 @@ describe('Parameter Mock generator', () => { path: '/user/logout', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let params = mock.parameters; Assert.ok(params, 'Generated parameters'); diff --git a/tests/params_mockgen.js b/tests/v2/params_mockgen.js old mode 100644 new mode 100755 similarity index 94% rename from tests/params_mockgen.js rename to tests/v2/params_mockgen.js index a4f5208..e908cd3 --- a/tests/params_mockgen.js +++ b/tests/v2/params_mockgen.js @@ -1,9 +1,9 @@ const Assert = require('assert'); -const Swagmock = require('../lib'); +const Swagmock = require('../../lib'); const Path = require('path') describe('Parameters Mock generator', () => { - let apiPath = Path.resolve(__dirname, 'fixture/petstore.json'); + let apiPath = Path.resolve(__dirname, '../fixture/petstore.json'); let swagmock = Swagmock(apiPath); it('should generate parameter mock for path /store/order/{orderId} for all operations', (done) => { let mockgen = swagmock.parameters({ @@ -21,7 +21,7 @@ describe('Parameters Mock generator', () => { Assert.ok(params.path[0].value >= 1 && params.path[0].value <= 10, 'OK value for orderId'); done(); }).catch(err => { - Assert.ok(!err, 'No error'); + if (err) throw err; done(); }); }); @@ -54,7 +54,7 @@ describe('Parameters Mock generator', () => { Assert.ok(testMock.length <= 4, 'body parameter should have maximum 4 items'); done(); }).catch(err => { - Assert.ok(!err, 'No error'); + if (err) throw err; done(); }); }); diff --git a/tests/request_mockgen.js b/tests/v2/request_mockgen.js old mode 100644 new mode 100755 similarity index 91% rename from tests/request_mockgen.js rename to tests/v2/request_mockgen.js index cd805df..01a1054 --- a/tests/request_mockgen.js +++ b/tests/v2/request_mockgen.js @@ -1,16 +1,16 @@ const Assert = require('assert'); -const Swagmock = require('../lib'); +const Swagmock = require('../../lib'); const Path = require('path') describe('Request Mock generator', () => { - let apiPath = Path.resolve(__dirname, 'fixture/petstore.json'); + let apiPath = Path.resolve(__dirname, '../fixture/petstore.json'); let swagmock = Swagmock(apiPath); it('should generate request mock for path /store/order/{orderId}', (done) => { swagmock.requests({ path: '/store/order/{orderId}', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); @@ -25,7 +25,7 @@ describe('Request Mock generator', () => { path: '/pet/findByStatus', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); @@ -39,7 +39,7 @@ describe('Request Mock generator', () => { path: '/pet/{petId}', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated parameters'); @@ -53,7 +53,7 @@ describe('Request Mock generator', () => { path: '/pet/{petId}/uploadImage', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); @@ -68,7 +68,7 @@ describe('Request Mock generator', () => { path: '/store/inventory', operation: 'get' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); @@ -81,7 +81,7 @@ describe('Request Mock generator', () => { path: '/store/order', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); @@ -103,7 +103,7 @@ describe('Request Mock generator', () => { path: '/user/createWithArray', operation: 'post' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let request = mock.request; Assert.ok(request, 'Generated request'); diff --git a/tests/response_mockgen.js b/tests/v2/response_mockgen.js old mode 100644 new mode 100755 similarity index 91% rename from tests/response_mockgen.js rename to tests/v2/response_mockgen.js index 5af1d85..520453f --- a/tests/response_mockgen.js +++ b/tests/v2/response_mockgen.js @@ -1,9 +1,9 @@ const Assert = require('assert'); -const Swagmock = require('../lib'); +const Swagmock = require('../../lib'); const Path = require('path') describe('Response Mock generator', () => { - let apiPath = Path.resolve(__dirname, 'fixture/petstore.json'); + let apiPath = Path.resolve(__dirname, '../fixture/petstore.json'); let swagmock = Swagmock(apiPath); it('should generate response mock for path /store/order/{orderId}', (done) => { @@ -12,7 +12,7 @@ describe('Response Mock generator', () => { operation: 'get', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; Assert.ok(resp, 'Generated response'); @@ -32,7 +32,7 @@ describe('Response Mock generator', () => { operation: 'get', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -52,7 +52,7 @@ describe('Response Mock generator', () => { operation: 'get', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -84,7 +84,7 @@ describe('Response Mock generator', () => { operation: 'post', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -100,7 +100,7 @@ describe('Response Mock generator', () => { operation: 'get', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -116,7 +116,7 @@ describe('Response Mock generator', () => { operation: 'post', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -132,7 +132,7 @@ describe('Response Mock generator', () => { operation: 'get', response: '200' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; @@ -148,7 +148,7 @@ describe('Response Mock generator', () => { operation: 'post', response: '405' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); let resp = mock.responses; diff --git a/tests/responses_mockgen.js b/tests/v2/responses_mockgen.js old mode 100644 new mode 100755 similarity index 94% rename from tests/responses_mockgen.js rename to tests/v2/responses_mockgen.js index 0a1ab17..276396c --- a/tests/responses_mockgen.js +++ b/tests/v2/responses_mockgen.js @@ -1,10 +1,10 @@ const Assert = require('assert'); -const Swagmock = require('../lib'); +const Swagmock = require('../../lib'); const Path = require('path') const Parser = require('swagger-parser'); describe('Responses Mock generator', () => { - let apiPath = Path.resolve(__dirname, 'fixture/petstore.json'); + let apiPath = Path.resolve(__dirname, '../fixture/petstore.json'); let apiResolver = Parser.validate(apiPath); let swagmock = Swagmock(apiResolver, { validated: true }); @@ -29,7 +29,7 @@ describe('Responses Mock generator', () => { Assert.ok(typeof successResp.complete === 'boolean', 'complete is boolean'); done(); }).catch(err => { - Assert.ok(!err, 'No error'); + if (err) throw err; done(); }); }); @@ -52,7 +52,7 @@ describe('Responses Mock generator', () => { Assert.ok(Number.isInteger(pet.id), 'id is integer'); done(); }).catch(err => { - Assert.ok(!err, 'No error'); + if (err) throw err; done(); }); }); @@ -61,7 +61,7 @@ describe('Responses Mock generator', () => { swagmock.responses({ path: '/pet/{petId}' }, (err, mock) => { - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); Assert.ok(mock.get, 'Generated mock for get operation'); @@ -78,7 +78,7 @@ describe('Responses Mock generator', () => { it('should generate response mock for all paths', (done) => { swagmock.responses({}, (err, mock) => { let testMock; - Assert.ok(!err, 'No error'); + if (err) throw err; Assert.ok(mock, 'Generated mock'); Assert.ok(mock['/pet'], 'Generated mock for path /pet'); Assert.ok(mock['/pet/findByStatus'], 'Generated mock for path /pet/findByStatus'); diff --git a/tests/v3/param_mockgen.js b/tests/v3/param_mockgen.js new file mode 100755 index 0000000..9e668d7 --- /dev/null +++ b/tests/v3/param_mockgen.js @@ -0,0 +1,191 @@ +const Assert = require('assert'); +const Swagmock = require('../../lib'); +const Path = require('path'); +const Parser = require('swagger-parser'); + +describe('Parameter Mock generator', () => { + let apiPath = Path.resolve(__dirname, '../fixture/petstore-v3.json'); + let apiResolver = Parser.validate(apiPath); + //Test case of valiadted api use case. + let swagmock = Swagmock(apiResolver, { validated: true }); + it('should generate parameter mock for path /store/order/{orderId}', done => { + swagmock.parameters({ + path: '/store/order/{orderId}', + operation: 'get' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.path, 'Generated path parameter'); + Assert.ok(params.path[0].name === 'orderId', 'generated mock parameter for orderId'); + Assert.ok(params.path[0].value >= 1 && params.path[0].value <= 10, 'OK value for orderId'); + done(); + }); + }); + + it('should generate parameter mock for path /pet/findByStatus', done => { + swagmock.parameters({ + path: '/pet/findByStatus', + operation: 'get' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.query, 'Generated query parameter'); + Assert.ok(params.query[0].name === 'status', 'generated mock parameter for status'); + console.log(params.query[0]); + Assert.ok(typeof params.query[0].value[0] === 'string', 'OK value for status'); + done(); + }); + }); + + it('should generate parameter mock for path /pet/{petId}', done => { + swagmock.parameters({ + path: '/pet/{petId}', + operation: 'get' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.path, 'Generated path parameter'); + Assert.ok(params.path[0].name === 'petId', 'generated mock parameter for petId'); + Assert.ok(Number.isInteger(params.path[0].value), 'OK value for petId'); + //Test the operation level overrides + Assert.ok(params.path[0].value >= 1000 && params.path[0].value <= 2000, 'OK value for petId'); + + Assert.ok(params.query, 'Generated query parameter'); + params.query.forEach(param => { + if (param.name === 'petName') { + Assert.ok(/awesome+ (pet|cat|bird)/.test(param.value), 'OK value for petName'); + } + if (param.name === 'petWeight') { + Assert.ok(Number.isFinite(param.value), 'OK value for petWeight'); + Assert.ok(param.value <= 500 && param.value >= 10, 'OK value for petWeight'); + } + if (param.name === 'bmi') { + Assert.ok(Number.isFinite(param.value), 'OK value for bmi'); + Assert.ok(param.value <= 1 && param.value >= 0, 'OK value for bmi'); + } + }); + + done(); + }); + }); + + it('should generate parameter mock for path /pet/{petId} post - common parameter', (done) => { + swagmock.parameters({ + path: '/pet/{petId}', + operation: 'post' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.path, 'Generated path parameter'); + Assert.ok(params.path[0].name === 'petId', 'generated mock parameter for petId'); + Assert.ok(Number.isInteger(params.path[0].value), 'OK value for petId'); + + done(); + }); + }); + + it('should generate parameter mock for path /pet/{petId}/uploadImage', (done) => { + swagmock.parameters({ + path: '/pet/{petId}/uploadImage', + operation: 'post' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.path, 'Generated path parameter'); + Assert.ok(params.path[0].name === 'petId', 'generated mock parameter for petId'); + Assert.ok(Number.isInteger(params.path[0].value), 'OK value for petId'); + Assert.ok(params.path[0].value > 1000 && params.path[0].value < 1010, 'OK value for petId'); + Assert.ok(params.formData, 'Generated formData parameter'); + Assert.ok(params.formData[0].name === 'additionalMetadata', 'generated mock parameter for additionalMetadata'); + Assert.ok(typeof params.formData[0].value === 'string', 'OK value for additionalMetadata'); + done(); + }); + }); + + it('should generate parameter mock for path /store/inventory', (done) => { + swagmock.parameters({ + path: '/store/inventory', + operation: 'get' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + done(); + }); + }); + + it('should generate parameter mock for path /store/order', (done) => { + swagmock.parameters({ + path: '/store/order', + operation: 'post' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.body, 'Generated body parameter'); + Assert.ok(params.body[0].name === 'body', 'generated mock parameter for body'); + let order = params.body[0].value; + Assert.ok(typeof order === 'object', 'OK value for body'); + Assert.ok(Number.isInteger(order.id), 'order.id is integer'); + Assert.ok(Number.isInteger(order.petId), 'order.petId is integer'); + Assert.ok(Number.isInteger(order.quantity), 'order.quantity is integer'); + Assert.ok(typeof order.shipDate === 'string', 'order.shipDate is string'); + Assert.ok(['placed','approved','delivered'].indexOf(order.status) !== -1, 'order.status is enum'); + Assert.ok(typeof order.complete === 'boolean', 'order.complete is boolean'); + done(); + }); + }); + + it('should generate parameter mock for path /user/createWithArray', (done) => { + swagmock.parameters({ + path: '/user/createWithArray', + operation: 'post' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + + Assert.ok(params.body, 'Generated body parameter'); + Assert.ok(params.body[0].name === 'body', 'generated mock parameter for body'); + let users = params.body[0].value; + Assert.ok(users.length === 1, 'Created a parameter array of users'); + let user = users[0]; + Assert.ok(typeof user === 'object', 'OK value for user parameter'); + Assert.ok(Number.isInteger(user.id), 'user.id is integer'); + Assert.ok(Number.isInteger(user.userStatus), 'user.userStatus is integer'); + Assert.ok(user.userStatus > 1000, 'user.userStatus is greater than 1000'); + Assert.ok(user.userStatus % 100 === 0, 'user.userStatus is multipleOf 100'); + Assert.ok(typeof user.username === 'string', 'user.username is string'); + + done(); + }); + }); + + it('should generate parameter mock for path /user/logout', (done) => { + swagmock.parameters({ + path: '/user/logout', + operation: 'get' + }, (err, mock) => { + if (err) { throw err; } + Assert.ok(mock, 'Generated mock'); + let params = mock.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.query, 'Generated path parameter'); + Assert.ok(params.query[0].name === 'common', 'generated mock parameter for common parameter'); + done(); + }); + }); +}); diff --git a/tests/v3/params_mockgen.js b/tests/v3/params_mockgen.js new file mode 100755 index 0000000..494bdef --- /dev/null +++ b/tests/v3/params_mockgen.js @@ -0,0 +1,61 @@ +const Assert = require('assert'); +const Swagmock = require('../../lib'); +const Path = require('path') + +describe('Parameters Mock generator', () => { + let apiPath = Path.resolve(__dirname, '../fixture/petstore-v3.json'); + let swagmock = Swagmock(apiPath); + it('should generate parameter mock for path /store/order/{orderId} for all operations', (done) => { + let mockgen = swagmock.parameters({ + path: '/store/order/{orderId}' + }); + //Promise test case + mockgen.then(mock => { + Assert.ok(mock, 'Generated mock'); + Assert.ok(mock.get, 'Generated mock for get operation'); + Assert.ok(mock.delete, 'Generated mock for delete operation'); + let params = mock.get.parameters; + Assert.ok(params, 'Generated parameters'); + Assert.ok(params.path, 'Generated path parameter'); + Assert.ok(params.path[0].name === 'orderId', 'generated mock parameter for orderId'); + Assert.ok(params.path[0].value >= 1 && params.path[0].value <= 10, 'OK value for orderId'); + done(); + }).catch(err => { + if (err) throw err; + done(); + }); + }); + + it('should generate parameter mock for all the path', (done) => { + let mockgen = swagmock.parameters({}); + //Promise test case + mockgen.then(mock => { + let testMock; + Assert.ok(mock, 'Generated mock'); + Assert.ok(mock['/pet'], 'Generated mock for path /pet'); + Assert.ok(mock['/pet/findByStatus'], 'Generated mock for path /pet/findByStatus'); + Assert.ok(mock['/pet/findByTags'], 'Generated mock for path /pet/findByTags'); + //Test minItems + testMock = mock['/pet/findByTags'].get.parameters.query[0].value; + Assert.ok(testMock.length >= 2, 'tags parameter should have minimum 2 items'); + Assert.ok(mock['/pet/{petId}'], 'Generated mock for path /pet/{petId}'); + Assert.ok(mock['/pet/{petId}/uploadImage'], 'Generated mock for path /pet/{petId}/uploadImage'); + Assert.ok(mock['/store/inventory'], 'Generated mock for path /store/inventory'); + Assert.ok(mock['/store/order'], 'Generated mock for path /store/order'); + Assert.ok(mock['/store/order/{orderId}'], 'Generated mock for path /store/order/{orderId}'); + Assert.ok(mock['/user'], 'Generated mock for path /user'); + Assert.ok(mock['/user/createWithArray'], 'Generated mock for path /user/createWithArray'); + //Test the default min and max + testMock = mock['/user/createWithArray'].post.parameters.body[0].value; + Assert.ok(testMock.length === 1, 'body parameter should have i item (default)'); + Assert.ok(mock['/user/createWithList'], 'Generated mock for path /user/createWithList'); + //Test maxItems + testMock = mock['/user/createWithList'].post.parameters.body[0].value; + Assert.ok(testMock.length <= 4, 'body parameter should have maximum 4 items'); + done(); + }).catch(err => { + if (err) throw err; + done(); + }); + }); +}); diff --git a/tests/v3/request_mockgen.js b/tests/v3/request_mockgen.js new file mode 100755 index 0000000..dc55df9 --- /dev/null +++ b/tests/v3/request_mockgen.js @@ -0,0 +1,122 @@ +const Assert = require('assert'); +const Swagmock = require('../../lib'); +const Path = require('path') + +describe('Request Mock generator', () => { + let apiPath = Path.resolve(__dirname, '../fixture/petstore-v3.json'); + let swagmock = Swagmock(apiPath); + it('should generate request mock for path /store/order/{orderId}', (done) => { + swagmock.requests({ + path: '/store/order/{orderId}', + operation: 'get' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + Assert.ok(request.pathname, 'Generated pathname request'); + Assert.ok(request.path, 'Generated path request'); + done(); + }); + }); + + it('should generate request mock for path /pet/findByStatus', (done) => { + swagmock.requests({ + path: '/pet/findByStatus', + operation: 'get' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + Assert.ok(request.query, 'Generated query request'); + done(); + }); + }); + + it('should generate request mock for path /pet/{petId}', (done) => { + swagmock.requests({ + path: '/pet/{petId}', + operation: 'get' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated parameters'); + Assert.ok(request.pathname, 'Generated path parameter'); + done(); + }); + }); + + it('should generate request mock for path /pet/{petId}/uploadImage', (done) => { + swagmock.requests({ + path: '/pet/{petId}/uploadImage', + operation: 'post' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + Assert.ok(request.pathname, 'Generated path request'); + Assert.ok(request.formData, 'Generated formData request'); + done(); + }); + }); + + it('should generate request mock for path /store/inventory', (done) => { + swagmock.requests({ + path: '/store/inventory', + operation: 'get' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + done(); + }); + }); + + it('should generate request mock for path /store/order', (done) => { + swagmock.requests({ + path: '/store/order', + operation: 'post' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + Assert.ok(request.body, 'Generated body request'); + let order = request.body; + Assert.ok(typeof order === 'object', 'OK value for body'); + Assert.ok(Number.isInteger(order.id), 'order.id is integer'); + Assert.ok(Number.isInteger(order.petId), 'order.petId is integer'); + Assert.ok(Number.isInteger(order.quantity), 'order.quantity is integer'); + Assert.ok(typeof order.shipDate === 'string', 'order.shipDate is string'); + Assert.ok(['placed','approved','delivered'].indexOf(order.status) !== -1, 'order.status is enum'); + Assert.ok(typeof order.complete === 'boolean', 'order.complete is boolean'); + done(); + }); + }); + + it('should generate request mock for path /user/createWithArray', (done) => { + swagmock.requests({ + path: '/user/createWithArray', + operation: 'post' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let request = mock.request; + Assert.ok(request, 'Generated request'); + Assert.ok(request.body, 'Generated body request'); + let users = request.body; + Assert.ok(users.length === 1, 'Created a request array of users'); + let user = users[0]; + Assert.ok(typeof user === 'object', 'OK value for user request'); + Assert.ok(Number.isInteger(user.id), 'user.id is integer'); + Assert.ok(Number.isInteger(user.userStatus), 'user.userStatus is integer'); + Assert.ok(typeof user.username === 'string', 'user.username is string'); + + done(); + }); + }); +}); diff --git a/tests/v3/response_mockgen.js b/tests/v3/response_mockgen.js new file mode 100755 index 0000000..84fd149 --- /dev/null +++ b/tests/v3/response_mockgen.js @@ -0,0 +1,161 @@ +const Assert = require('assert'); +const Swagmock = require('../../lib'); +const Path = require('path') + +describe('Response Mock generator', () => { + let apiPath = Path.resolve(__dirname, '../fixture/petstore-v3.json'); + let swagmock = Swagmock(apiPath); + + it('should generate response mock for path /store/order/{orderId}', (done) => { + swagmock.responses({ + path: '/store/order/{orderId}', + operation: 'get', + response: '200' + }, (err, mock) => { + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + Assert.ok(Number.isInteger(resp.id), 'id is integer'); + Assert.ok(Number.isInteger(resp.petId), 'petId is integer'); + Assert.ok([ 1, 3, 5 ].indexOf(resp.quantity) != -1, 'quantity is integer enum'); + Assert.ok(typeof resp.shipDate === 'string', 'shipDate is string'); + Assert.ok(['placed','approved','delivered'].indexOf(resp.status) !== -1, 'status is enum'); + Assert.ok(typeof resp.complete === 'boolean', 'complete is boolean'); + done(); + }); + }); + + it('should generate response mock for path /pet/findByStatus', (done) => { + swagmock.responses({ + path: '/pet/findByStatus', + operation: 'get', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + Assert.ok(Array.isArray(resp), 'response is Pet array'); + let pet = resp[0]; + Assert.ok(pet, 'Ok Pet response'); + Assert.ok(Number.isInteger(pet.id), 'id is integer'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /pet/{petId}', (done) => { + swagmock.responses({ + path: '/pet/{petId}', + operation: 'get', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + //TODO add asserts for pending props + done(); + }); + }); + + + it('should use the example "doggie" when generating with examples for path /pet/{petId}', (done) => { + swagmock.responses({ + path: '/pet/{petId}', + operation: 'get', + response: '200', + useExamples: true + }, (err, mock) => { + + let resp = mock.responses; + Assert.equal(resp.name, 'doggie'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /pet/{petId}/uploadImage', (done) => { + swagmock.responses({ + path: '/pet/{petId}/uploadImage', + operation: 'post', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /store/inventory', (done) => { + swagmock.responses({ + path: '/store/inventory', + operation: 'get', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /store/order', (done) => { + swagmock.responses({ + path: '/store/order', + operation: 'post', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /user/login', (done) => { + swagmock.responses({ + path: '/user/login', + operation: 'get', + response: '200' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + //TODO add asserts for pending props + done(); + }); + }); + + it('should generate response mock for path /pet', (done) => { + swagmock.responses({ + path: '/pet', + operation: 'post', + response: '405' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(!resp, 'No response'); + //TODO add asserts for pending props + done(); + }); + }); + +}); diff --git a/tests/v3/responses_mockgen.js b/tests/v3/responses_mockgen.js new file mode 100755 index 0000000..a47ce7a --- /dev/null +++ b/tests/v3/responses_mockgen.js @@ -0,0 +1,100 @@ +const Assert = require('assert'); +const Swagmock = require('../../lib'); +const Path = require('path') +const Parser = require('swagger-parser'); + +describe('Responses Mock generator', () => { + let apiPath = Path.resolve(__dirname, '../fixture/petstore-v3.json'); + let apiResolver = Parser.validate(apiPath); + let swagmock = Swagmock(apiResolver, { validated: true }); + + it('should generate response mock for path /store/order/{orderId}, get operation', (done) => { + let mockgen = swagmock.responses({ + path: '/store/order/{orderId}', + operation: 'get' + }); + mockgen.then(mock => { + Assert.ok(mock, 'Generated mock'); + let resp = mock.responses; + Assert.ok(resp, 'Generated response'); + Assert.ok(resp.hasOwnProperty('200'), 'Generated 200 response'); + Assert.ok(resp.hasOwnProperty('400'), 'Generated 400 response'); + Assert.ok(resp.hasOwnProperty('404'), 'Generated 404 response'); + let successResp = resp['200']; + Assert.ok(Number.isInteger(successResp.id), 'id is integer'); + Assert.ok(Number.isInteger(successResp.petId), 'petId is integer'); + Assert.ok(Number.isInteger(successResp.quantity), 'quantity is integer'); + Assert.ok(typeof successResp.shipDate === 'string', 'shipDate is string'); + Assert.ok(['placed','approved','delivered'].indexOf(successResp.status) !== -1, 'status is enum'); + Assert.ok(typeof successResp.complete === 'boolean', 'complete is boolean'); + done(); + }).catch(err => { + if (err) throw err; + done(); + }); + }); + + it('should generate response mock for path /pet/findByStatus, for all operations', (done) => { + let mockgen = swagmock.responses({ + path: '/pet/findByStatus' + }); + mockgen.then(mock => { + Assert.ok(mock, 'Generated mock'); + Assert.ok(mock.get, 'Generated mock for `get` operation'); + let responses = mock.get.responses; + Assert.ok(responses, 'Generated responses'); + Assert.ok(responses.hasOwnProperty('200'), 'Generated 200 response'); + Assert.ok(responses.hasOwnProperty('400'), 'Generated 400 response'); + let resp = responses['200']; + Assert.ok(Array.isArray(resp), 'response is Pet array'); + let pet = resp[0]; + Assert.ok(pet, 'Ok Pet response'); + Assert.ok(Number.isInteger(pet.id), 'id is integer'); + done(); + }).catch(err => { + if (err) throw err; + done(); + }); + }); + + it('should generate response mock for path /pet/{petId} for all operations', (done) => { + swagmock.responses({ + path: '/pet/{petId}' + }, (err, mock) => { + if (err) throw err; + + Assert.ok(mock, 'Generated mock'); + Assert.ok(mock.get, 'Generated mock for get operation'); + Assert.ok(mock.post, 'Generated mock for post operation'); + Assert.ok(mock.delete, 'Generated mock for delete operation'); + let responses = mock.delete.responses; + Assert.ok(responses, 'Generated response for delete operation'); + Assert.ok(responses.hasOwnProperty('404'), 'Generated 404 response'); + Assert.ok(responses.hasOwnProperty('400'), 'Generated 400 response'); + done(); + }); + }); + + it('should generate response mock for all paths', (done) => { + swagmock.responses({}, (err, mock) => { + let testMock; + if (err) throw err; + Assert.ok(mock, 'Generated mock'); + Assert.ok(mock['/pet'], 'Generated mock for path /pet'); + Assert.ok(mock['/pet/findByStatus'], 'Generated mock for path /pet/findByStatus'); + Assert.ok(mock['/pet/findByTags'], 'Generated mock for path /pet/findByTags'); + Assert.ok(mock['/pet/{petId}'], 'Generated mock for path /pet/{petId}'); + //test the minItems and MaxItems + testMock = mock['/pet/{petId}'].get.responses['200'].tags; + Assert.ok(testMock.length <= 5 && testMock.length >= 2, 'tags response should have min 2 and max 5 items'); + Assert.ok(mock['/pet/{petId}/uploadImage'], 'Generated mock for path /pet/{petId}/uploadImage'); + Assert.ok(mock['/store/inventory'], 'Generated mock for path /store/inventory'); + Assert.ok(mock['/store/order'], 'Generated mock for path /store/order'); + Assert.ok(mock['/store/order/{orderId}'], 'Generated mock for path /store/order/{orderId}'); + Assert.ok(mock['/user'], 'Generated mock for path /user'); + Assert.ok(mock['/user/createWithArray'], 'Generated mock for path /user/createWithArray'); + Assert.ok(mock['/user/createWithList'], 'Generated mock for path /user/createWithList'); + done(); + }); + }); +});