diff --git a/.gitignore b/.gitignore index 05940ee..222906d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ html-report xunit.xml node_modules npm-debug.log -test/fixtures/package.json .project .idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 59f2871..3d3a6e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,7 @@ -### 1.0.0-rc.4 +### 3.0.0 -* Fixed issue related to [#18](https://github.com/krakenjs/swaggerize-express/issues/18) and [expressjs/body-parser#44](https://github.com/expressjs/body-parser/issues/44). +- Swagger 2.0 compatible. +- `docspath` defaults to `/api-docs`. +- `setUrl` is now `setHost` and is used to set `host` and `port` in the `api`. -### 1.0.0-rc.3 - -* Fixed issue where generator failed with deeper `path`. - -### 1.0.0-rc.2 - -* Improved tests generation. - -### 1.0.0-rc.1 - -* __BREAKING CHANGE__: `options.docs` is `options.docspath` and defaults to `/`. - -### 0.1.0-alpha.6 - -* __BREAKING CHANGE__: Reverted to standard express handlers. -* Removed output validation. -* Middleware capability. - -### 0.1.0-alpha.5 - -* Better tests generator. -* Bug fixes. - -### 0.1.0-alpha.4 - -* Only one of `--handlers` or `--models` is necessary in generator, not both. -* Generator now has a `--tests` switch for generating tests. -* Output validation is opt-in via `outputValidation: true`. - -### 0.1.0-alpha.3 - -* `resourcePath` in swagger document is base `mountpath` for routes. -* Added support for path variables in `handlers` directory names. -* Path parameters are always required if defined. -* Fixed trailing commas in handler definitions created by generator. - -### 0.1.0-alpha.2 - -* Added generator for models and handlers stubs. -* Fixed a bug that was not allowing multiple operations under an API definition. -* Fixed bug caused by `res.send` deprecation. +See `v1.0.0`, `v2.0.0` branches for addition changelogs. diff --git a/QUICKSTART.md b/QUICKSTART.md index 3a888de..8bfd2c0 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -10,47 +10,14 @@ $ mkdir helloworld && cd $_ ### 2. -```bash -$ vim api.json -``` - -Paste the following: - -```json -{ - "swaggerVersion": "1.2", - "apiVersion": "1.0.0", - "basePath": "http://localhost:8000/", - "resourcePath": "/v1/helloworld", - "apis": [ - { - "path": "/hello/{name}", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "getHello", - "parameters": [ - { - "name": "name", - "required": true, - "type": "string", - "paramType": "path" - } - ] - } - ] - } - ] -} -``` +Grab the (Swagger Pet Store example)[https://github.com/wordnik/swagger-spec/blob/master/examples/v2.0/json/petstore.json]. ### 3. ```bash $ npm init $ npm install --save express swaggerize-express -$ node_modules/.bin/swaggerize --api api.json --handlers handlers --models models --tests tests +$ node_modules/.bin/swaggerize --api petstore.json --handlers handlers --models models --tests tests ``` ### 4. @@ -71,15 +38,14 @@ app = express(); var server = http.createServer(app); var swagger = swaggerize({ - api: require('./api.json'), - handlers: './handlers', - outputvalidation: app.settings.env === 'development' + api: require('./petstore.json'), + handlers: './handlers' }); app.use(swagger); server.listen(8000, 'localhost', function () { - swagger.setUrl('http://' + server.address().address + ':' + server.address().port); + swagger.setHost(server.address().address + ':' + server.address().port); }); ``` diff --git a/README.md b/README.md index 36399a1..4741f0a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - **Stability:** `stable` - **Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) -`swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md) +`swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md) and Express. `swaggerize-express` provides the following features: @@ -16,13 +16,13 @@ and Express. - Input model validation. - Models and handlers stubs generator command (`swaggerize`). -### Why "Spec First" +### Why "Design First" There are already a number of modules that help build REST services with express and swagger. However, these modules tend to focus on building the documentation or specification as a side effect of writing the application business logic. -`swaggerize-express` begins with the service specification first. This facilitates writing services that +`swaggerize-express` begins with the service definition first. This facilitates writing services that are easier to design, review, and test. ### Usage @@ -39,11 +39,11 @@ app.use(swaggerize({ Options: -- `api` - a valid Swagger 1.2 document. +- `api` - a valid Swagger 2.0 document. - `docspath` - the path to expose api docs for swagger-ui, etc. Defaults to `/`. - `handlers` - either a directory structure for route handlers or a premade object (see *Handlers Object* below). -The base url for the api can also be updated via the `setUrl` function on the middleware. +The base url for the api can also be updated via the `setHost` function on the middleware. Example: @@ -65,7 +65,7 @@ var swagger = swaggerize({ app.use(swagger); server.listen(port, 'localhost', function () { - swagger.setUrl('http://' + server.address().address + ':' + server.address().port); + swagger.setHost(server.address().address + ':' + server.address().port); }); ``` @@ -73,7 +73,7 @@ Also checkout the [Quick Start Guide](https://github.com/krakenjs/swaggerize-exp ### Mount Path -Api `path` values will be prefixed with the swagger document's `resourcePath` value. +Api `path` values will be prefixed with the swagger document's `basePath` value. ### Handlers Directory @@ -178,4 +178,4 @@ Example: swaggerize --api config/api.json --models resources/models --handlers resources/handlers --tests tests/ ``` -`--api` is required, but only one of `--models` or `--handlers` or `--tests` is required. +`--api` is required, but only one of `--models` or `--handlers` or `--tests` is required. \ No newline at end of file diff --git a/bin/lib/create.js b/bin/lib/create.js index af25876..2581a8b 100644 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -3,7 +3,8 @@ var fs = require('fs'), path = require('path'), lodash = require('lodash'), - mkdirp = require('mkdirp'); + mkdirp = require('mkdirp'), + utils = require('swaggerize-builder/lib/utils'); var modelTemplate, handlerTemplate, testTemplate; @@ -22,6 +23,9 @@ function createModels(models, modelsPath) { if (!fs.existsSync(fileName)) { model = models[modelName]; mkdirp.sync(path.dirname(fileName)); + if (!model.id) { + model.id = modelName; + } fs.writeFileSync(fileName, lodash.template(template, model)); } else { @@ -30,24 +34,24 @@ function createModels(models, modelsPath) { }); } -function createHandlers(apis, handlersPath) { +function createHandlers(paths, handlersPath) { var routes, template; routes = {}; template = fs.readFileSync(handlerTemplate); - apis.forEach(function (api) { + Object.keys(paths).forEach(function (path) { var pathnames, route; route = { - path: api.path, + path: path, pathname: undefined, methods: [] }; pathnames = []; - api.path.split('/').forEach(function (element) { + path.split('/').forEach(function (element) { if (element) { pathnames.push(element); } @@ -55,11 +59,19 @@ function createHandlers(apis, handlersPath) { route.pathname = pathnames.join('/'); - api.operations.forEach(function (operation) { + utils.verbs.forEach(function (verb) { + var operation = paths[path][verb]; + + if (!operation) { + return; + } + route.methods.push({ - method: operation.method.toLowerCase(), - name: operation.nickname, - output: operation.type + method: verb, + name: operation.operationId, + description: operation.description, + parameters: operation.parameters, + produces: operation.produces }); }); @@ -111,13 +123,13 @@ function createTests(api, testsPath, apiPath, handlersPath, modelsPath) { apiPath = path.relative(testsPath, apiPath); handlersPath = path.relative(testsPath, handlersPath); - if (api.models && modelsPath) { + if (api.definitions && modelsPath) { - Object.keys(api.models).forEach(function (key) { + Object.keys(api.definitions).forEach(function (key) { var modelSchema, ModelCtor, options; options = {}; - modelSchema = api.models[key]; + modelSchema = api.definitions[key]; ModelCtor = require(path.join(modelsPath, key.toLowerCase() + '.js')); Object.keys(modelSchema.properties).forEach(function (prop) { @@ -149,19 +161,38 @@ function createTests(api, testsPath, apiPath, handlersPath, modelsPath) { } - resourcePath = api.resourcePath; + resourcePath = api.basePath; + + Object.keys(api.paths).forEach(function (opath) { + var fileName, operations; + + operations = []; + + utils.verbs.forEach(function (verb) { + var operation = {}; - api.apis.forEach(function (api) { - var fileName; + if (!api.paths[opath][verb]) { + return; + } + + Object.keys(api.paths[opath][verb]).forEach(function (key) { + operation[key] = api.paths[opath][verb][key]; + }); + + operation.path = opath; + operation.method = verb; + + operations.push(operation); + }); - fileName = path.join(testsPath, 'test' + api.path.replace(/\//g, '_') + '.js'); + fileName = path.join(testsPath, 'test' + opath.replace(/\//g, '_') + '.js'); if (!fs.existsSync(fileName)) { fs.writeFileSync(fileName, lodash.template(template, { apiPath: apiPath, handlers: handlersPath, resourcePath: resourcePath, - api: api, + operations: operations, models: models })); diff --git a/bin/lib/swaggerize.js b/bin/lib/swaggerize.js index 5f7b013..20aba8f 100644 --- a/bin/lib/swaggerize.js +++ b/bin/lib/swaggerize.js @@ -3,11 +3,11 @@ var fs = require('fs'), path = require('path'), mkdirp = require('mkdirp'), - schema = require('swaggerize-builder/lib/schema'), + tv4 = require('tv4'), create = require('./create'); module.exports = function (options) { - var apiPath, modelsPath, handlersPath, testsPath, validation, api; + var apiPath, modelsPath, handlersPath, testsPath, api; function usage() { console.error('swaggerize --api [[--models ] | [--handlers ] | [--tests ]]'); @@ -30,15 +30,7 @@ module.exports = function (options) { api = require(apiPath); - validation = schema.validate(api); - - if (!validation.valid) { - console.error('%s (at %s)', validation.error, validation.error.dataPath); - if (validation.error.subErrors) { - validation.error.subErrors.forEach(function (subError) { - console.error('%s (at %s)', subError, subError.dataPath); - }); - } + if (validate(api)) { return 1; } @@ -47,7 +39,7 @@ module.exports = function (options) { console.error('tests can not be generated without handlers path.'); return usage(); } - if ((api.models && !modelsPath)) { + if ((api.definitions && !modelsPath)) { console.error('api contains models, so tests can not be generated without handlers and models paths.'); return usage(); } @@ -63,10 +55,29 @@ module.exports = function (options) { } }); - modelsPath && create.models(api.models, modelsPath); - handlersPath && create.handlers(api.apis, handlersPath); + modelsPath && create.models(api.definitions, modelsPath); + handlersPath && create.handlers(api.paths, handlersPath); testsPath && create.tests(api, testsPath, apiPath, handlersPath, modelsPath); return 0; }; + +function validate(api) { + var schema, validator, validation; + + schema = require('swaggerize-builder/lib/schema/swagger-spec/schemas/v2.0/schema.json'); + validator = tv4.freshApi(); + + validation = validator.validateResult(api, schema); + + if (!validation.valid) { + console.error('%s (at %s)', validation.error.message, validation.error.dataPath || '/'); + if (validation.error.subErrors) { + validation.error.subErrors.forEach(function (subError) { + console.error('%s (at %s)', subError, subError.dataPath); + }); + } + return 1; + } +} diff --git a/bin/lib/templates/handler.js b/bin/lib/templates/handler.js index 3ecc837..d55e14c 100644 --- a/bin/lib/templates/handler.js +++ b/bin/lib/templates/handler.js @@ -1,10 +1,17 @@ 'use strict'; -//handlers for <%=path%> +/** + * Operations on <%=path%> + */ module.exports = { <%_.forEach(methods, function (method, i) {%> + /** + * <%=method.description%> + * parameters: <%=method.parameters.map(function (p) { return p.name }).join(', ')%> + * produces: <%=method.produces && method.produces.join(', ')%> + */ <%=method.method%>: function <%=method.name%>(req, res) { - //respond with <%=method.output%> + res.send(500); }<%if (i < methods.length - 1) {%>, <%}%> <%})%> }; diff --git a/bin/lib/templates/test.js b/bin/lib/templates/test.js index 9ae0a33..842b1e6 100644 --- a/bin/lib/templates/test.js +++ b/bin/lib/templates/test.js @@ -8,23 +8,24 @@ var test = require('tape'), test('api', function (t) { var app = express(); - <%_.forEach(api.operations, function (operation) { if (operation.method.toLowerCase() === 'post' || operation.method.toLowerCase() === 'put') { %> + + <%_.forEach(operations, function (operation) { if (operation.method.toLowerCase() === 'post' || operation.method.toLowerCase() === 'put') { %> app.use(require('body-parser')());<%}});%> app.use(swaggerize({ api: require('<%=apiPath%>'), - handlers: path.join(__dirname, '<%=handlers%>'), + handlers: path.join(__dirname, '<%=handlers%>') })); - <%_.forEach(api.operations, function (operation) {%> - t.test('test <%=operation.method%> <%=api.path%>', function (t) { + <%_.forEach(operations, function (operation) {%> + t.test('test <%=operation.method%> <%=operation.path%>', function (t) { <% - var path = api.path; + var path = operation.path; var body; if (operation.parameters && operation.parameters.length) { - operation.parameters.forEach(function (param) { - if (param.paramType === 'path') { - path = api.path.replace(/{([^}]*)}*/, function (p1, p2) { + _.forEach(operation.parameters, function (param) { + if (param.in === 'path') { + path = operation.path.replace(/{([^}]*)}*/, function (p1, p2) { switch (param.type) { case 'integer': case 'float': @@ -33,7 +34,7 @@ test('api', function (t) { case 'byte': return 1; case 'string': - return 'helloworld'; + return 'test'; case 'boolean': return true; default: @@ -41,21 +42,24 @@ test('api', function (t) { } }); } - if (param.paramType === 'body') { - body = models[param.type]; + if (param.in === 'body') { + body = models[param.schema.$ref.slice(param.schema.$ref.lastIndexOf('/') + 1)]; } }); - } - %><%if (operation.method.toLowerCase() === 'post' || operation.method.toLowerCase() === 'put'){%>var body = <%=JSON.stringify(body)%>;<%}%> - t.plan(2); + }%>t.plan(2); + <%if (operation.method.toLowerCase() === 'post' || operation.method.toLowerCase() === 'put'){%> + var body = {<%_.forEach(Object.keys(body), function (k, i) {%> + '<%=k%>': <%=JSON.stringify(body[k])%><%if (i < Object.keys(body).length - 1) {%>, <%}%><%})%> + }; + <%}%> request(app).<%=operation.method.toLowerCase()%>('<%=resourcePath%><%=path%>') .expect(200)<%if (operation.method.toLowerCase() === 'post' || operation.method.toLowerCase() === 'put'){%>.send(body)<%}%> .end(function (err, res) { - t.ok(!err, '<%=operation.method.toLowerCase()%> <%=api.path%> no error.'); - t.strictEqual(res.statusCode, 200, '<%=operation.method.toLowerCase()%> <%=api.path%> 200 status.'); + t.ok(!err, '<%=operation.method.toLowerCase()%> <%=operation.path%> no error.'); + t.strictEqual(res.statusCode, 200, '<%=operation.method.toLowerCase()%> <%=operation.path%> 200 status.'); }); }); <%});%> -}); +}); \ No newline at end of file diff --git a/lib/expressroutes.js b/lib/expressroutes.js index c2e2dff..578bafc 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -11,9 +11,14 @@ var path = require('path'), * @param options */ function expressroutes(router, options) { - var routes = options.routes || []; + var routes, mountpath; - router.get(options.api.resourcePath + utils.prefix(options.docspath || '', '/'), function (req, res) { + routes = options.routes || []; + options.docspath = utils.prefix(options.docspath || '/api-docs', '/'); + options.api.basePath = utils.prefix(options.api.basePath || '/', '/'); + mountpath = utils.unsuffix(options.api.basePath, '/'); + + router.get(mountpath + options.docspath, function (req, res) { res.json(options.api); }); @@ -21,7 +26,7 @@ function expressroutes(router, options) { var args, path, before; path = route.path.replace(/{([^}]+)}/g, ':$1'); - args = [options.api.resourcePath + utils.prefix(path, '/')]; + args = [mountpath + utils.prefix(path, '/')]; before = []; route.validators.forEach(function (validator) { @@ -33,7 +38,7 @@ function expressroutes(router, options) { before.push(function validateInput(req, res, next) { var value, isPath; - switch (parameter.paramType) { + switch (parameter.in) { case 'path': case 'query': isPath = true; @@ -45,16 +50,6 @@ function expressroutes(router, options) { case 'body': case 'form': value = req.body; - if (parameter.type === 'string') { - //If the type is string and we found an empty object, convert to empty string. - //This is a bug in express's body-parser: https://github.com/expressjs/body-parser/issues/44 - if (thing.isObject(value) && !Object.keys(value).length) { - req.body = value = ''; - break; - } - //Coerce to a string since that's the type we expect and Express magic's it for us. - req.body = value = JSON.stringify(value); - } } validate(value, function (error, newvalue) { diff --git a/lib/index.js b/lib/index.js index d900f63..6396ed4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,8 +19,6 @@ function swaggerize(options) { options.routes = builder(options); - options.docspath = options.docspath || '/'; - app = express(); app.once('mount', mount(options)); @@ -30,16 +28,10 @@ function swaggerize(options) { value: options.api }); - Object.defineProperty(app, 'setUrl', { + Object.defineProperty(app, 'setHost', { enumerable: true, value: function (value) { - var basePath = url.parse(options.api.basePath); - - value = url.parse(value); - value.protocol && (basePath.protocol = value.protocol); - value.host && (basePath.host = value.host); - - options.api.basePath = url.format(basePath); + options.api.host = value; } }); diff --git a/package.json b/package.json index 71c7384..c2509c7 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "swaggerize-express", - "version": "1.0.0-rc.4", + "version": "3.0.0-alpha.1", "author": "Trevor Livingston ", - "description": "Spec-first REST services with Swagger and Express.", + "description": "Design-first REST services with Swagger and Express.", "keywords": [ "swagger", + "swagger 2.0", "swagger-node", "swagger-express", "swagger-ui", @@ -29,13 +30,14 @@ "node": "0.10.x" }, "dependencies": { - "swaggerize-builder": "^1.0.0", + "swaggerize-builder": "^2.0.0", "caller": "^0.0.1", "core-util-is": "^1.0.1", "debuglog": "^1.0.1", "lodash": "^2.4.1", "minimist": "^0.2.0", - "mkdirp": "^0.5.0" + "mkdirp": "^0.5.0", + "tv4": "^1.1.0" }, "peerDependencies": { "express": "^4.0.0" diff --git a/test/fixtures/api.json b/test/fixtures/api.json deleted file mode 100644 index 8b2bf72..0000000 --- a/test/fixtures/api.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "swaggerVersion": "1.2", - "apiVersion": "v1", - "basePath": "http://localhost:8000/", - "resourcePath": "/v1/greetings", - "apis": [ - { - "path": "/hello/{subject}", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "getHello", - "parameters": [ - { - "name": "subject", - "description": "The subject to be greeted.", - "required": true, - "type": "string", - "paramType": "path" - } - ] - } - ] - }, - { - "path": "/goodbye/{subject}", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "getGoodbye", - "parameters": [ - { - "name": "subject", - "required": false, - "type": "string", - "paramType": "path" - } - ] - } - ] - }, - { - "path": "/sub/{id}", - "operations": [ - { - "method": "GET", - "type": "integer", - "nickname": "getSub", - "parameters": [ - { - "name": "id", - "required": false, - "type": "integer", - "paramType": "path" - } - ] - }, - { - "method": "HEAD", - "type": "void", - "nickname": "headSub", - "parameters": [ - { - "name": "id", - "required": false, - "type": "integer", - "paramType": "path" - } - ] - } - ] - }, - { - "path": "/sub/{id}/path", - "operations": [ - { - "method": "GET", - "type": "User", - "nickname": "getSubpath", - "parameters": [ - { - "name": "id", - "required": true, - "type": "integer", - "paramType": "path" - } - ] - } - ] - } - ], - "models": { - "User": { - "id": "User", - "required": ["id", "name"], - "properties": { - "name": { - "type": "string" - }, - "id": { - "type": "integer" - } - } - } - } -} diff --git a/test/fixtures/badapi.json b/test/fixtures/badapi.json deleted file mode 100644 index f35f5c6..0000000 --- a/test/fixtures/badapi.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "swaggerVersion": "1.2", - "apiVersion": "v1", - "basePath": "http://localhost:8000/", - "resourcePath": "/v1/collections" -} diff --git a/test/fixtures/collections.json b/test/fixtures/collections.json deleted file mode 100644 index b88616c..0000000 --- a/test/fixtures/collections.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "swaggerVersion": "1.2", - "apiVersion": "v1", - "basePath": "http://localhost:8000/", - "resourcePath": "/v1/collections", - "apis": [ - { - "path": "/stuffs", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "stuffs" - } - ] - }, - { - "path": "/stuffs/{id}", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "stuffsId", - "parameters": [ - { - "name": "id", - "required": true, - "type": "integer", - "paramType": "path" - } - ] - } - ] - }, - { - "path": "/middlewares", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "getMiddlewares" - } - ] - } - ] -} diff --git a/test/fixtures/defs/badapi.json b/test/fixtures/defs/badapi.json new file mode 100644 index 0000000..121b797 --- /dev/null +++ b/test/fixtures/defs/badapi.json @@ -0,0 +1,15 @@ +{ + "swagger": 2.0, + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "name": "Wordnik API Team" + }, + "license": { + "name": "MIT" + } + } +} diff --git a/test/fixtures/defs/pets.json b/test/fixtures/defs/pets.json new file mode 100644 index 0000000..97d778d --- /dev/null +++ b/test/fixtures/defs/pets.json @@ -0,0 +1,205 @@ +{ + "swagger": 2.0, + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "name": "Wordnik API Team" + }, + "license": { + "name": "MIT" + } + }, + "host": "petstore.swagger.wordnik.com/api", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to", + "operationId": "findPets", + "produces": [ + "application/json", + "application/xml", + "text/xml", + "text/html" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv" + }, + { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "pet", + "in": "body", + "description": "Pet to add to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/pets/{id}": { + "get": { + "description": "Returns a user based on a single ID, if the user does not have access to the pet", + "operationId": "findPetById", + "produces": [ + "application/json", + "application/xml", + "text/xml", + "text/html" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to fetch", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "description": "deletes a single pet based on the ID supplied", + "operationId": "deletePet", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "204": { + "description": "pet deleted" + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} diff --git a/test/fixtures/handlers/pets.js b/test/fixtures/handlers/pets.js new file mode 100644 index 0000000..05741cb --- /dev/null +++ b/test/fixtures/handlers/pets.js @@ -0,0 +1,12 @@ +'use strict'; + +var store = require('../lib/store'); + +module.exports = { + get: function (req, res) { + res.json(store.all()); + }, + post: function (req, res) { + res.json(store.get(store.put(req.body))); + } +}; diff --git a/test/fixtures/handlers/pets/{id}.js b/test/fixtures/handlers/pets/{id}.js new file mode 100644 index 0000000..c29ffcd --- /dev/null +++ b/test/fixtures/handlers/pets/{id}.js @@ -0,0 +1,14 @@ +'use strict'; + +var store = require('../../lib/store'); + + +module.exports = { + get: function (req, res) { + res.json(store.get(req.param('id'))); + }, + delete: function (req, res) { + store.delete(req.param('id')); + res.json(store.all()); + } +}; \ No newline at end of file diff --git a/test/fixtures/input.json b/test/fixtures/input.json deleted file mode 100644 index e6a7f48..0000000 --- a/test/fixtures/input.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "swaggerVersion": "1.2", - "apiVersion": "v1", - "basePath": "http://localhost:8000/", - "resourcePath": "/v1", - "apis": [ - { - "path": "/test/{id}", - "operations": [ - { - "method": "GET", - "type": "string", - "nickname": "getTest_id", - "parameters": [ - { - "name": "id", - "description": "the id.", - "required": true, - "type": "integer", - "paramType": "path" - }, - { - "name": "q1", - "description": "query param.", - "required": false, - "type": "string", - "paramType": "query" - } - ] - }, - { - "method": "POST", - "type": "string", - "nickname": "postTest_id", - "parameters": [ - { - "name": "id", - "description": "the id.", - "required": true, - "type": "integer", - "paramType": "path" - }, - { - "name": "body", - "description": "the body.", - "required": true, - "type": "string", - "paramType": "body" - } - ] - }, - { - "method": "PUT", - "type": "string", - "nickname": "putTest_id", - "parameters": [ - { - "name": "id", - "description": "the id.", - "required": true, - "type": "integer", - "paramType": "path" - }, - { - "name": "body", - "description": "the body.", - "required": true, - "type": "string", - "paramType": "form" - } - ] - }, - { - "method": "DELETE", - "type": "string", - "nickname": "deleteTest_id", - "parameters": [ - { - "name": "id", - "description": "the id.", - "required": true, - "type": "integer", - "paramType": "path" - }, - { - "name": "body", - "description": "the body.", - "required": true, - "type": "Form", - "paramType": "body" - } - ] - } - ] - } - ], - "models": { - "Form": { - "id": "Form", - "properties": { - "param1": { - "type": "string" - }, - "param2": { - "type": "string" - } - } - } - } -} diff --git a/test/fixtures/lib/store.js b/test/fixtures/lib/store.js new file mode 100644 index 0000000..5b54f3c --- /dev/null +++ b/test/fixtures/lib/store.js @@ -0,0 +1,19 @@ +'use strict'; + +var store = []; + +module.exports = { + put: function (data) { + store.push(data); + return store.length - 1; + }, + get: function (id) { + return store[id]; + }, + delete: function (id) { + store.splice(id, 1); + }, + all: function () { + return store; + } +}; diff --git a/test/handlers/hello/{subject}.js b/test/handlers/hello/{subject}.js deleted file mode 100644 index c13a836..0000000 --- a/test/handlers/hello/{subject}.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = { - get: function (req, res) { - res.status(200).send('hello'); - } -}; diff --git a/test/handlers/middlewares.js b/test/handlers/middlewares.js deleted file mode 100644 index d499da6..0000000 --- a/test/handlers/middlewares.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = { - get: [ - function m1(req, res, next) { - req.m1 = true; - next(); - }, - function handler(req, res) { - res.status(200).end(); - } - ] -}; diff --git a/test/handlers/stuffs.js b/test/handlers/stuffs.js deleted file mode 100644 index aed3bc4..0000000 --- a/test/handlers/stuffs.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = { - get: function (req, res) { - res.status(200).end(); - } -}; diff --git a/test/handlers/stuffs/{id}.js b/test/handlers/stuffs/{id}.js deleted file mode 100644 index aed3bc4..0000000 --- a/test/handlers/stuffs/{id}.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = { - get: function (req, res) { - res.status(200).end(); - } -}; diff --git a/test/handlers/sub/{id}.js b/test/handlers/sub/{id}.js deleted file mode 100644 index ee678f5..0000000 --- a/test/handlers/sub/{id}.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - get: function (req, res) { - res.status(200).end(); - }, - head: function headSub(req, res) { - res.status(200).end(); - } -}; diff --git a/test/handlers/sub/{id}/path.js b/test/handlers/sub/{id}/path.js deleted file mode 100644 index 1f26e57..0000000 --- a/test/handlers/sub/{id}/path.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - get: function (req, res) { - res.json({ - id: 0, - name: 'Swaggycat' - }); - } -}; diff --git a/test/test-expressroutes.js b/test/test-expressroutes.js index aced579..b277c74 100644 --- a/test/test-expressroutes.js +++ b/test/test-expressroutes.js @@ -15,12 +15,11 @@ test('express routes', function (t) { var stack; expressroutes(app, { - api: require('./fixtures/api.json'), - docspath: '/api-docs', + api: require('./fixtures/defs/pets.json'), routes: [ { method: 'get', - path: '/hello/:subject', + path: '/pets/:id', validators: [], handler: function (req, res) {} } @@ -30,8 +29,8 @@ test('express routes', function (t) { stack = Array.prototype.slice.call(parent._router.stack, 3); t.strictEqual(stack.length, 2, '2 routes added.'); - t.strictEqual(stack[0].route.path, '/v1/greetings/api-docs', 'api-docs added.'); - t.strictEqual(stack[1].route.path, '/v1/greetings/hello/:subject', 'hello added.'); + t.strictEqual(stack[0].route.path, '/api-docs', 'api-docs added.'); + t.strictEqual(stack[1].route.path, '/pets/:id', 'hello added.'); }); app.use(child); @@ -46,8 +45,7 @@ test('express routes', function (t) { var stack; expressroutes(app, { - api: require('./fixtures/api.json'), - docspath: '/api-docs', + api: require('./fixtures/defs/pets.json'), validators: [], routes: [] }); @@ -55,7 +53,7 @@ test('express routes', function (t) { stack = Array.prototype.slice.call(parent._router.stack, 3); t.strictEqual(stack.length, 1, 'only api-docs route added.'); - t.strictEqual(stack[0].route.path, '/v1/greetings/api-docs', 'api-docs added.'); + t.strictEqual(stack[0].route.path, '/api-docs', 'api-docs added.'); }); app.use(child); @@ -70,12 +68,11 @@ test('express routes', function (t) { var stack; expressroutes(app, { - api: require('./fixtures/collections.json'), - docspath: '/api-docs', + api: require('./fixtures/defs/pets.json'), routes: [ { method: 'get', - path: '/middlewares', + path: '/pets', validators: [], handler: [ function m1(req, res, next) {}, @@ -88,9 +85,9 @@ test('express routes', function (t) { stack = Array.prototype.slice.call(parent._router.stack, 3); t.strictEqual(stack.length, 2, '2 routes added.'); - t.strictEqual(stack[1].route.path, '/v1/collections/middlewares', '/middlewares added.'); - t.strictEqual(stack[1].route.stack.length, 2, '/middlewares has middleware.'); - t.strictEqual(stack[1].route.stack[0].name, 'm1', '/middlewares has middleware named m1.'); + t.strictEqual(stack[1].route.path, '/pets', '/pets added.'); + t.strictEqual(stack[1].route.stack.length, 2, '/pets has middleware.'); + t.strictEqual(stack[1].route.stack[0].name, 'm1', '/pets has middleware named m1.'); }); app.use(child); diff --git a/test/test-generator.js b/test/test-generator.js index 7521757..c4aae6f 100644 --- a/test/test-generator.js +++ b/test/test-generator.js @@ -6,32 +6,35 @@ var test = require('tape'), path = require('path'), mkdirp = require('mkdirp'); -test('swaggerize command', function (t) { +function rm(dir) { + var files = fs.readdirSync(dir); - mkdirp.sync(path.resolve('test/temp')); + files && files.forEach(function (file) { + var info = fs.statSync(file = path.join(dir, file)); - t.on('end', function () { - function rm(dir) { - var files = fs.readdirSync(dir); + info.isFile() && fs.unlinkSync(file); + info.isDirectory() && rm(file); + }); - files && files.forEach(function (file) { - var info = fs.statSync(file = path.join(dir, file)); + fs.rmdirSync(dir); +} - info.isFile() && fs.unlinkSync(file); - info.isDirectory() && rm(file); - }); +test('swaggerize command', function (t) { + var tempDir = path.resolve('test/temp'); + + fs.existsSync(tempDir) && rm(tempDir); - fs.rmdirSync(dir); - } + mkdirp.sync(tempDir); - rm(path.resolve('test/temp')); + t.on('end', function () { + rm(tempDir); }); t.test('no handlers or models', function (t) { t.plan(1); var code = swaggerize({ - api: 'test/fixtures/api.json' + api: 'test/fixtures/defs/pets.json' }); t.strictEqual(code, 1, 'error code 1.'); @@ -41,7 +44,7 @@ test('swaggerize command', function (t) { t.plan(1); var code = swaggerize({ - api: 'test/fixtures/api.json', + api: 'test/fixtures/defs/pets.json', tests: '/test/temp/tests' }); @@ -52,7 +55,7 @@ test('swaggerize command', function (t) { t.plan(1); var code = swaggerize({ - api: 'test/fixtures/badapi.json', + api: 'test/fixtures/defs/badapi.json', handlers: 'test/temp/handlers' }); @@ -60,42 +63,39 @@ test('swaggerize command', function (t) { }); t.test('handlers', function (t) { - t.plan(9); + t.plan(5); var code = swaggerize({ - api: 'test/fixtures/api.json', + api: 'test/fixtures/defs/pets.json', handlers: 'test/temp/handlers' }); t.ok(!code, 'no error code.'); t.ok(fs.existsSync(path.resolve('test/temp/handlers')), 'handlers dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/goodbye')), 'goodbye dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/goodbye/{subject}.js')), 'goodbye/{subject}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/hello/{subject}.js')), 'hello/{subject}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/sub')), 'sub dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/sub/{id}.js')), 'sub/{id}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/sub/{id}')), 'sub/{id} dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/handlers/sub/{id}/path.js')), 'sub/{id}/path.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/handlers/pets')), 'pets dir exists'); + t.ok(fs.existsSync(path.resolve('test/temp/handlers/pets/{id}.js')), 'pets/{id}.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/handlers/pets.js')), 'pets.js exists'); }); t.test('models', function (t) { - t.plan(3); + t.plan(4); var code = swaggerize({ - api: 'test/fixtures/api.json', + api: 'test/fixtures/defs/pets.json', models: 'test/temp/models' }); t.ok(!code, 'no error code.'); t.ok(fs.existsSync(path.resolve('test/temp/models')), 'models dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/models/user.js')), 'user.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/models/pet.js')), 'pet.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/models/error.js')), 'error.js exists'); }); t.test('tests', function (t) { - t.plan(6); + t.plan(4); var code = swaggerize({ - api: 'test/fixtures/api.json', + api: 'test/fixtures/defs/pets.json', handlers: 'test/temp/handlers', models: 'test/temp/models', tests: 'test/temp/tests' @@ -103,10 +103,8 @@ test('swaggerize command', function (t) { t.ok(!code, 'no error code.'); t.ok(fs.existsSync(path.resolve('test/temp/tests')), 'tests dir exists'); - t.ok(fs.existsSync(path.resolve('test/temp/tests/test_goodbye_{subject}.js')), 'test_goodbye_{subject}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/tests/test_hello_{subject}.js')), 'test_hello_{subject}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/tests/test_sub_{id}.js')), 'test_sub_{id}.js exists'); - t.ok(fs.existsSync(path.resolve('test/temp/tests/test_sub_{id}_path.js')), 'test_sub_{id}_path.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/tests/test_pets_{id}.js')), 'test_goodbye_{subject}.js exists'); + t.ok(fs.existsSync(path.resolve('test/temp/tests/test_pets.js')), 'test_hello_{subject}.js exists'); }); }); diff --git a/test/test-swaggerize-command.js b/test/test-swaggerize-command.js index b8db6cb..e1b9997 100644 --- a/test/test-swaggerize-command.js +++ b/test/test-swaggerize-command.js @@ -6,14 +6,31 @@ var test = require('tape'), path = require('path'), mkdirp = require('mkdirp'); +function rm(dir) { + var files = fs.readdirSync(dir); + + files && files.forEach(function (file) { + var info = fs.statSync(file = path.join(dir, file)); + + info.isFile() && fs.unlinkSync(file); + info.isDirectory() && rm(file); + }); + + fs.rmdirSync(dir); +} + test('swaggerize command', function (t) { - var cwd, writeStream; + var cwd, writeStream, tempDir; cwd = process.cwd(); - mkdirp.sync(path.resolve('test/temp')); + tempDir = path.resolve('test/temp'); + + fs.existsSync(tempDir) && rm(tempDir); - process.chdir(path.resolve('test/temp')); + mkdirp.sync(tempDir); + + process.chdir(tempDir); writeStream = fs.createWriteStream('package.json'); @@ -21,31 +38,15 @@ test('swaggerize command', function (t) { t.on('end', function () { process.chdir(cwd); - - function rm(dir) { - var files = fs.readdirSync(dir); - - files && files.forEach(function (file) { - var info = fs.statSync(file = path.join(dir, file)); - - info.isFile() && fs.unlinkSync(file); - info.isDirectory() && rm(file); - }); - - fs.rmdirSync(dir); - } - - rm(path.resolve('test/temp')); + rm(tempDir); }); t.test('npm devDependencies', function (t) { t.plan(5); - execFile('../../bin/swaggerize.js', ['--api', '../fixtures/api.json', '--handlers', 'handlers', '--models', 'models', '--tests', 'tests'], function (error, stdout, stderr) { + execFile('../../bin/swaggerize.js', ['--api', '../fixtures/defs/pets.json', '--handlers', 'handlers', '--models', 'models', '--tests', 'tests'], function (error, stdout, stderr) { var pkg; - stdout && console.log(stdout.toString()); - stderr && console.log(stderr.toString()); error && console.error(error); t.ok(!error, 'no error.'); diff --git a/test/test-swaggerize.js b/test/test-swaggerize.js index 20fb6c6..8880478 100644 --- a/test/test-swaggerize.js +++ b/test/test-swaggerize.js @@ -4,15 +4,19 @@ var test = require('tape'), swaggerize = require('../lib'), express = require('express'), bodyParser = require('body-parser'), - request = require('supertest'); + request = require('supertest'), + path = require('path'); -test('swagger valid input/output', function (t) { +test('swaggerize', function (t) { var app = express(); + var swagger = swaggerize({ - api: require('./fixtures/api.json') + api: require('./fixtures/defs/pets.json'), + handlers: path.join(__dirname, 'fixtures/handlers') }); + app.use(bodyParser.json()); app.use(swagger); t.test('api', function (t) { @@ -21,179 +25,114 @@ test('swagger valid input/output', function (t) { t.ok(swagger.hasOwnProperty('_api'), 'has _api property.'); t.ok(swagger._api, '_api is an object.'); - t.ok(swagger.hasOwnProperty('setUrl'), 'has setUrl property.'); - t.strictEqual(typeof swagger.setUrl, 'function', 'setUrl is a function.'); + t.ok(swagger.hasOwnProperty('setHost'), 'has setHost property.'); + t.strictEqual(typeof swagger.setHost, 'function', 'setHost is a function.'); - swagger.setUrl('http://localhost:8080'); + swagger.setHost('localhost:8080'); - t.strictEqual(swagger._api.basePath, 'http://localhost:8080/v1/greetings'); + t.strictEqual(swagger._api.host, 'localhost:8080'); }); t.test('docs', function (t) { t.plan(2); - request(app).get('/v1/greetings/').end(function (error, response) { + request(app).get('/api-docs').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); }); }); - t.test('route', function (t) { - t.plan(2); - - request(app).get('/v1/greetings/hello').end(function (error, response) { - t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 404, '404 required param missing.'); - }); - }); - - - t.test('route', function (t) { + t.test('post /pets', function (t) { t.plan(3); - request(app).get('/v1/greetings/hello/doge').end(function (error, response) { + request(app).post('/pets').send({id: 0, name: 'Cat'}).end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); - t.strictEqual(response.text, 'hello', 'body is correct.'); + t.strictEqual(response.body.name, 'Cat', 'body is correct.'); }); }); - t.test('route', function (t) { - t.plan(2); + t.test('get /pets', function (t) { + t.plan(3); - request(app).get('/v1/greetings/sub/1').end(function (error, response) { + request(app).get('/pets').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); + t.strictEqual(response.body.length, 1, 'body is correct.'); }); }); - t.test('route', function (t) { - t.plan(2); + t.test('get /pets/:id', function (t) { + t.plan(3); - request(app).get('/v1/greetings/sub/1/path').end(function (error, response) { + request(app).get('/pets/0').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); + t.strictEqual(response.body.name, 'Cat', 'body is correct.'); }); }); -}); - -test('input validators', function (t) { - - var app = express(); - - app.use(swaggerize({ - api: require('./fixtures/api.json'), - handlers: { - sub: { - '{id}': { - $get: function (req, res) { - res.send('foobar'); - } - } - }, - goodbye: { - $get: function (req, res) { - res.send('baz'); - } - } - } - })); - - t.test('bad input', function (t) { - t.plan(2); - - request(app).get('/v1/greetings/sub/asdf').end(function (error, response) { - t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 400, '400 status.'); - }); - }); - - t.test('null input not found', function (t) { - t.plan(2); + t.test('delete /pets', function (t) { + t.plan(3); - request(app).get('/v1/greetings/goodbye').end(function (error, response) { + request(app).delete('/pets/0').end(function (error, response) { t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 404, '404 status.'); + t.strictEqual(response.statusCode, 200, '200 status.'); + t.strictEqual(response.body.length, 0, 'body is correct.'); }); }); }); -test('body and query input', function (t) { +test('input validation', function (t) { var app = express(); - app.use(bodyParser()); + app.use(bodyParser.json()); app.use(swaggerize({ - api: require('./fixtures/input.json'), + api: require('./fixtures/defs/pets.json'), handlers: { - test: { + 'pets': { '{id}': { $get: function (req, res) { - res.send('get'); - }, - $post: function (req, res) { - res.send(typeof req.body); - }, - $put: function (req, res) { - res.send(typeof req.body); + }, $delete: function (req, res) { res.send(typeof req.body); } + }, + $get: function (req, res) { + res.json({ + id: 0, + name: 'Cat', + tags: req.param('tags') + }); + }, + $post: function (req, res) { + res.send(typeof req.body); } } } })); t.test('good query', function (t) { - t.plan(2); + t.plan(3); - request(app).get('/v1/test/1?q1=foobar').end(function (error, response) { + request(app).get('/pets?tags=kitty,serious').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); + t.strictEqual(response.body.tags.length, 2, 'query parsed.'); }); }); t.test('missing body', function (t) { t.plan(2); - request(app).post('/v1/test/1').send('').end(function (error, response) { + request(app).post('/pets').send('').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 400, '400 status.'); }); }); - t.test('coerce body', function (t) { - t.plan(3); - - request(app).post('/v1/test/1').send({hello: 'world'}).end(function (error, response) { - t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 200, '200 status.'); - t.strictEqual(response.text, 'string', 'coerced json to string.'); - }); - }); - - t.test('coerce body with form', function (t) { - t.plan(3); - - request(app).put('/v1/test/1').send({param1: 'hello', param2: 'world'}).end(function (error, response) { - t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 200, '200 status.'); - t.strictEqual(response.text, 'string', 'coerced json to string.'); - }); - }); - - t.test('body with form uses model', function (t) { - t.plan(3); - - request(app).delete('/v1/test/1').send({param1: 'hello', param2: 'world'}).end(function (error, response) { - t.ok(!error, 'no error.'); - t.strictEqual(response.statusCode, 200, '200 status.'); - t.strictEqual(response.text, 'object', 'type came across as object.'); - }); - }); - });