UNPKG

seahorse

Version:

Configurable mock REST API testing tool written in javascript

1,203 lines (1,118 loc) 37.9 kB
/*! seahorse - v0.2.6 - 2016-06-18 */ (function(global){ 'use strict'; var fs = require('fs'); var Throttle = require('throttle'); var util = require('util'); var BASE_RATE = 2 * 1000 * 1000 / 8; // 2MBps = 2Mbps/8 var utils = { _debug : false, _logs : false, _cors : true, _proxy : false, _th : null, _bps : BASE_RATE, _getExtension: function(filename) { var i = filename.lastIndexOf('.'); return (i < 0) ? '' : filename.substr(i); }, _setRate: function(newrate) { if (this._th) this._th.setBps(newrate); this._bps = parseInt(newrate); }, _getRate: function() { return this._bps; }, _sendfile: function(filename, res) { var stream = fs.createReadStream(filename); var self = this; this._th = new Throttle(this._bps); stream.on('open', function() { if( utils._debug ) { util.log('[info] open ' + filename); } // automaticaly pipes readable stream to res (= writeableStream), data are then transfered // with a limited rate through the throttle stream.pipe(self._th).pipe(res); }); stream.on('data', function(chunk) { if( utils._debug ) { util.log('[info] read a chunk :' + chunk.length + ' bytes'); } }); stream.on('close', function(chunk) { if( utils._debug ) { util.log('closed ' + filename); } }); stream.on('error', function(err) { console.log('error from reading stream ' + err.toString()); res.end(err); }); }, _allowCrossDomain: function(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); res.header('Seahorse-version', '0.1.0'); // intercept OPTIONS method if ('OPTIONS' == req.method) { res.send(200); } else { next(); } } }; global.utils = utils; })(this); (function(global, utils){ 'use strict'; var util = require('util'), path = require('path'), fs = require('fs'); var routes = { config: [], getFilesizeInBytes: function(filename) { var stats = fs.statSync(filename); var fileSizeInBytes = stats.size; return fileSizeInBytes; }, checkConfig: function(config) { try { var json = JSON.stringify(config); if( false === (config instanceof Array) ) { throw "object is not an array"; } config.forEach(function(element, index, array) { if( (typeof element.httpRequest === 'undefined') || (typeof element.httpResponse === 'undefined')){ throw "["+index+"] httpResponse or httpRequest missing"; } if ((typeof element.httpRequest.method === 'undefined') || (typeof element.httpRequest.path === 'undefined')) { throw "["+index+"] method or path is missing in httpRequest key"; } if (typeof element.httpResponse.statusCode === 'undefined') { throw "["+index+"] statusCode is missing in httpResponse key"; } if ( ( element.httpRequest.method.toUpperCase() === 'GET' ) && ( element.httpResponse.statusCode != 404 ) && ( typeof element.httpResponse.body === 'undefined' ) && ( typeof element.httpResponse.file === 'undefined' ) && ( typeof element.httpResponse.static === 'undefined' ) ) { throw "["+index+"] body, file or static are missing in httpResponse key (GET "+element.httpRequest.path+")"; } }); } catch(e) { if( utils._debug ) { util.log("[server] input file is not a valid json config file, " + e); } return false; } return true; }, setConfig: function(config, app) { if(this.checkConfig(config)) { this.config = config; return true; } return false; }, updateConfig: function(config, app) { var that = this; if(this.checkConfig(config)) { config.forEach(function(element1, index1, array) { var match = that.config.every(function(element2, index2, array){ if( ( element2.httpRequest.path === element1.httpRequest.path ) && ( element2.httpRequest.method === element1.httpRequest.method ) && ( element2.httpRequest.query === element1.httpRequest.query ) ) { // update the current config element with these new properties // todo: check this method is correct (element2 will keep a reference to element1) element2.httpResponse = element1.httpResponse; return false; } else { return true; } }); if( match === true ) { // add this element to the current config that.config.push(element1); } }); return true; } return false; }, getConfig: function() { return this.config; }, _matchPath: function(element, req) { if ( element.httpRequest.method.toUpperCase() !== req.method.toUpperCase() ) { return false; } if (/^regexp:/.test(element.httpRequest.path)) { var r = new RegExp(element.httpRequest.path.split(":")[1]); if( r.test(req.params[0]) !== true ) { return false; } } else { // remove trailing slash if( element.httpRequest.path.replace(/\/$/, '') !== req.params[0].replace(/\/$/, '') ) { return false; } } return true; }, all: function(req, res) { var matchingResponse = {}; var that = this; // is there at least one element in confg that match with request ? var match = this.config.some(function(element, index, array) { if( that._matchPath(element, req) ) { if( typeof element.httpRequest.query !== 'undefined' ) { // do all query parameters match with config ? for (var queryKey in element.httpRequest.query ) { if( typeof req.query[queryKey] === 'undefined' ) { return false; } else if( element.httpRequest.query[queryKey].toString() !== req.query[queryKey].toString() ) { return false; } } matchingResponse = element; return true; } matchingResponse = element; return true; } return false; }); if( match ) { setTimeout(function() { // get headers if( typeof matchingResponse.httpResponse.headers !== 'undefined' ) { matchingResponse.httpResponse.headers.forEach(function(element, index, array) { if( element.name && element.value ) { res.setHeader(element.name, element.value); } }); } // set status code if( typeof matchingResponse.httpResponse.statusCode !== 'undefined' ) { res.status(matchingResponse.httpResponse.statusCode); } else { // is this branch really usefull ? res.status(200); } if( typeof matchingResponse.httpResponse.body !== 'undefined') { // res.setHeader("Content-Length", matchingResponse.httpResponse.body.length); res.send(matchingResponse.httpResponse.body); } else { var filename; if( typeof matchingResponse.httpResponse.file !== 'undefined') { filename = matchingResponse.httpResponse.file; } else if( typeof matchingResponse.httpResponse.static !== 'undefined') { filename = matchingResponse.httpResponse.static+req.originalUrl; } if( typeof filename !== 'undefined') { // convert relative path in absolute try { if( path.resolve(filename)!==filename) { filename = path.normalize(process.cwd()+'/'+filename); } res.setHeader("Content-Length", that.getFilesizeInBytes(filename)); utils._sendfile(filename, res); } catch(e) { var notFound="<html>File '+filename+' not found</html>"; res.setHeader("Content-Type", "text/html; charset=UTF-8"); res.setHeader("Content-Length", notFound.length); res.send(notFound, 404); } } else { res.send(""); } } }, (typeof matchingResponse.httpResponse.delay === 'undefined')?1:matchingResponse.httpResponse.delay); } else { if( utils._debug ) { util.log('[warning] request not configured'); } if(req.method.toUpperCase() === 'HEAD') { res.status(500); res.setHeader("Content-Length", 0); res.send(); } else { var notFound="<html>Page not found</html>"; res.setHeader("Content-Type", "text/html; charset=UTF-8"); res.setHeader("Content-Length", notFound.length); res.send(notFound, 404); } } } }; global.routes = routes; })(this, this.utils); (function(global, utils, routes){ 'use strict'; var express = require('express'); var morgan = require('morgan'); var util = require('util'); var proxy = require('express-http-proxy'); var app = express(); var server = { _server: null, start: function(config, port) { if( utils._cors ) app.use(utils._allowCrossDomain); app.use(express.json()); // to support JSON-encoded bodies app.use(express.urlencoded()); // to support URL-encoded bodies if( utils._logs ) app.use(morgan('combined')); // http://127.0.0.1:3000/proxy?originalUrl=http://webapp.local/operator // -> host = webapp.local // -> path = /operator if( utils._proxy ) app.use('/proxy', proxy( function(req){ var url = require('url'); var host = url.parse(req.url.split('originalUrl=')[1]).host; // return (host?host:url.parse(req.headers.referer).host); return (host?host:'192.168.1.6'); },{ forwardPath: function(req, res) { var url = require('url'); return url.parse(url.parse(req.url).query.split('originalUrl=')[1]).path; }, decorateRequest: function(req) { console.log("[proxy] " + req.method + " - " + req.hostname + " - " + req.path); return req; } })); util.log("[server] start seahorse server on port " + port + (utils._logs?" with logs":"") + (utils._proxy?" with proxy activated":"")+ (utils._cors?" (CORS are on)":" (CORS are off)") ); var sseClients = []; app.get("/stream", function(req, res) { req.socket.setTimeout(Number.MAX_VALUE); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // todo, do not add same client multiple times sseClients.push(res); util.log("[SSE] new client registered"); res.write('\n'); }); app.post('/_rate/:bps', function(req, res) { utils._setRate(req.params.bps); res.setHeader("Content-Type", "application/json; charset=utf-8"); res.send(JSON.stringify({bps: utils._getRate()})); }); app.get('/_rate', function(req, res) { res.setHeader("Content-Type", "application/json; charset=utf-8"); res.send(JSON.stringify({bps: utils._getRate()}), 200); }); app.post('/stream/:event_name', function(req, res) { var data = ""; try { data = JSON.stringify(req.body); } catch(e) { data = "ERROR"; } var id = (new Date()).toLocaleTimeString(); sseClients.forEach(function(element, index, array) { element.write('id: ' + id + '\n'); element.write("event: " + req.params.event_name + '\n'); // extra newline is not an error element.write("data: " + data + '\n\n'); }); res.send(200); }); // permanent route for current configuration reading app.get("/_config", function(req, res) { res.setHeader("Content-Type", "application/json; charset=utf-8"); res.send(JSON.stringify(routes.getConfig()), 200); }); // route for setting a new configuration (erase the previous one) app.post("/_config", function(req, res) { var newConfig = req.body; routes.setConfig(newConfig, app); res.setHeader("Content-Type", "application/json; charset=utf-8"); res.send(JSON.stringify(routes.getConfig())); }); // route for updating the current configuration (update the previous one with the new keys) app.put("/_config", function(req, res) { var newConfig = req.body; routes.updateConfig(newConfig, app); res.setHeader("Content-Type", "application/json; charset=utf-8"); res.send(JSON.stringify(routes.getConfig()), 200); }); // set initial config if( typeof config !== 'undefined') { routes.setConfig(config, app); } else { throw "initial config is undefined"; } // mock routes app.all("*", function(req, res) { if( utils._debug ) { util.log('[request] ' + req.method + ' ' + req.originalUrl + '\t('+ ((typeof req.headers['user-agent'] !== 'undefined')?req.headers['user-agent']:"unknown") +')'); util.log('[request] [headers] ' + JSON.stringify(req.headers)); } routes.all(req, res); }); var self = this; process.on( "SIGINT", function() { self.stop(); } ); // start listening this._server = app.listen(port); }, stop: function() { util.log("[server] stop seahorse server"); // todo: ask confirmation if( this._server !== null ) { this._server.close(); } } }; global.server = server; })(this, this.utils, this.routes); 'use strict'; var should = require('should'); var api = require('hippie'); var routes = this.routes; var server = this.server; server.start(null, 3000); 'use strict'; var should = require('should'); var routes = this.routes; describe("[routes.js] ", function(){ var configOk = [ { "httpRequest" : { "method" : "get", "path" : "/foo" }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ], "delay": 1 } } ]; var updateOkSame = [ { "httpRequest" : { "method" : "get", "path" : "/foo" }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ], "delay": 1 } } ]; var updateOkDifferent = [ { "httpRequest" : { "method" : "post", "path" : "/foo" }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ], "delay": 1 } } ]; describe('Test checkConfig method of routes module', function() { describe('with a config file with is not a json', function() { it('should returns false', function() { routes.checkConfig("foo").should.eql(false); }); }); describe('with a config file with an incorrect syntax (no httpRequest)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpRequest; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with an incorrect syntax (no method)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpRequest.method; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with an incorrect syntax (no path)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpRequest.path; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with an incorrect syntax (no httpResponse)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpResponse; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with an incorrect syntax (no statusCode)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpResponse.statusCode; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with an incorrect syntax (no body nor file)', function() { // ugly way for duplicate object, isn't it ? var configKo = JSON.parse( JSON.stringify( configOk ) ); delete configKo[0].httpResponse.body; it('should returns false', function() { routes.checkConfig(configKo).should.eql(false); }); }); describe('with a config file with a file key instead of body', function() { // ugly way for duplicate object, isn't it ? var configOk2 = JSON.parse( JSON.stringify( configOk ) ); delete configOk2[0].httpResponse.body; configOk2[0].httpResponse.file = "http://www.google.com"; it('should returns true', function() { routes.checkConfig(configOk2).should.eql(true); }); }); describe('with a config file with correct syntax', function() { it('should returns true', function() { routes.checkConfig(configOk).should.eql(true); }); }); }); describe('Test setConfig method of routes module', function() { describe('with an incorrect config file', function() { it('should returns false', function(done) { routes.config = {}; routes.setConfig("foo").should.eql(false); done(); }); it('should not modify config property', function(done) { routes.config.should.be.empty; done(); }); }); describe('with an correct config file', function() { it('should returns true', function(done) { routes.config = {}; routes.setConfig(configOk).should.eql(true); done(); }); it('should modify config property', function(done) { routes.config.length.should.eql(1); routes.config[0].should.have.properties('httpRequest', 'httpResponse'); routes.config[0].httpRequest.should.have.properties('method', 'path'); routes.config[0].httpResponse.should.have.properties('statusCode', 'body', 'headers', 'delay'); done(); }); }); }); describe('Test getConfig method of routes module', function() { it('should returns the current config', function(done) { routes.config = JSON.parse( JSON.stringify( configOk ) ); routes.getConfig().should.eql(configOk); done(); }); }); describe('Test updateConfig method of routes module', function() { describe('with an incorrect config file', function() { it('should returns false', function(done) { routes.config = JSON.parse( JSON.stringify( configOk ) ); routes.updateConfig("foo").should.eql(false); done(); }); it('should not modify config property', function(done) { routes.config.should.eql(configOk); done(); }); }); describe('with an correct config file but with same method and path', function() { it('should returns true', function(done) { routes.config = JSON.parse( JSON.stringify( configOk ) ); routes.updateConfig(updateOkSame).should.eql(true); done(); }); it('should not modify config', function(done) { routes.config.length.should.eql(1); routes.config.should.eql(configOk); done(); }); }); describe('with an correct config file and diffrent method or path', function() { it('should returns true', function(done) { routes.config = JSON.parse( JSON.stringify( configOk ) ); routes.updateConfig(updateOkDifferent).should.eql(true); done(); }); it('should modify config', function(done) { routes.config.length.should.eql(2); routes.config[0].httpRequest.method.should.eql('get'); routes.config[1].httpRequest.method.should.eql('post'); done(); }); }); }); var staticConfig = [ { "httpRequest" : { "method" : "get", "path" : "regexp:^\/.*\.json$" }, "httpResponse" : { "statusCode" : 200, "static" : ".", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ] } } ]; describe('Test static file serving of routes module', function() { describe('get a resource', function() { beforeEach(function(){ routes.setConfig(staticConfig); }); it('should returns a 200 status code and an empty array', function(done) { api() .json() .base('http://localhost:3000') .get('/specs/empty.json') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody([]) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should returns a 200 status code and an simple array', function(done) { api() .json() .base('http://localhost:3000') .get('/specs/array.json') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody([1, 2, 3]) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should returns a 404 status code for an unknown resource', function(done) { api() .base('http://localhost:3000') .get('/specs/unknown.json') .expectStatus(404) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); }); var fileConfig = [ { "httpRequest" : { "method" : "get", "path" : "/array.json" }, "httpResponse" : { "statusCode" : 200, "file" : "specs/array.json", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ] } } ]; describe('Test direct file serving of routes module', function() { describe('get a resource', function() { beforeEach(function(){ routes.setConfig(fileConfig); }); it('should returns a 200 status code and an simple array', function(done) { api() .json() .base('http://localhost:3000') .get('/array.json') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectHeader('Content-length', '9') .expectBody([1, 2, 3]) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should returns a 404 status code for an unknown resource', function(done) { api() .base('http://localhost:3000') .get('/unknown.json') .expectStatus(404) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); }); }); 'use strict'; describe("[server.js] ", function(){ var config = [ { "httpRequest" : { "method" : "get", "path" : "/foo" }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ], "delay": 1 } } ]; var otherConfig = [ { "httpRequest" : { "method" : "post", "path" : "/bar" }, "httpResponse" : { "statusCode" : 404, "delay": 1 } } ]; describe('Test get /_config server route', function() { beforeEach(function(){ routes.setConfig(config); }); it('should returns config object', function(done) { api() .json() .base('http://localhost:3000') .get('/_config') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(config) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); describe('Test post /_config server route', function() { beforeEach(function(){ routes.setConfig(config); }); it('should returns new config when called with a correct config file', function(done) { api() .json() .base('http://localhost:3000') .post('/_config') .send(otherConfig) .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(otherConfig) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should returns initial config when called with an incorrect config file', function(done) { api() .json() .base('http://localhost:3000') .post('/_config') .send(null) .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(config) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); describe('Test put /_config server route', function() { beforeEach(function(){ routes.setConfig(config); }); it('should returns new config when called with a correct config file', function(done) { api() .json() .base('http://localhost:3000') .put('/_config') .send(otherConfig) .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(config.concat(otherConfig)) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should returns initial config when called with an incorrect config file', function(done) { api() .json() .base('http://localhost:3000') .put('/_config') .send(null) .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(config) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); }); 'use strict'; describe("[seahorse.js] ", function(){ this.timeout(50); var config = [ { "httpRequest" : { "method" : "get", "path" : "/foo/" }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json; charset=utf-8" } ], "delay": 1 } }, { "httpRequest" : { "method" : "post", "path" : "/foo" }, "httpResponse" : { "statusCode" : 201, "delay": 1 } }, { "httpRequest" : { "method" : "put", "path" : "/foo" }, "httpResponse" : { "statusCode" : 202, "delay": 1 } }, { "httpRequest" : { "method" : "delete", "path" : "/foo/" }, "httpResponse" : { "statusCode" : 203, "delay": 3000 } }, { "httpRequest" : { "method" : "get", "path" : "/fooquery", "query": { "Content-Type": "application/json", "key": "value" } }, "httpResponse" : { "statusCode" : 200, "body" : "{\"key\": \"value\"}", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/json" } ], "delay": 1 } }, { "httpRequest" : { "method" : "get", "path" : "/fooquery", "query": { "Content-Type": "application/html" } }, "httpResponse" : { "statusCode" : 200, "body" : "<html>Hello</html>", "headers" : [ { "name": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "name": "Content-Type", "value": "application/html" } ], "delay": 1 } } ]; beforeEach(function(){ routes.setConfig(config); }); describe('Test get /_config server route', function() { it('should returns config object', function(done) { api() .json() .base('http://localhost:3000') .get('/_config') .expectStatus(200) .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody(config) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); describe('Test status code', function() { it('should be 404 when request get /foobar', function(done) { api() .base('http://localhost:3000') .get('/foobar') .expectStatus(404) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 200 when request get /foo', function(done) { api() .json() .base('http://localhost:3000') .get('/foo') .expectStatus(200) .expectHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody({"key": "value"}) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 200 when request get /foo/', function(done) { api() .json() .base('http://localhost:3000') .get('/foo/') .expectStatus(200) .expectHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody({"key": "value"}) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 201 when request post /foo', function(done) { api() .json() .base('http://localhost:3000') .post('/foo') .expectStatus(201) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 201 when request post /foo/', function(done) { api() .json() .base('http://localhost:3000') .post('/foo/') .expectStatus(201) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 202 when request put /foo', function(done) { api() .json() .base('http://localhost:3000') .put('/foo') .expectStatus(202) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 202 when request put /foo/', function(done) { api() .json() .base('http://localhost:3000') .put('/foo/') .expectStatus(202) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 203 when request delete /foo', function(done) { this.timeout(5000); api() .json() .url('http://localhost:3000/foo') .method('DELETE') .expectStatus(203) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 203 when request delete /foo/', function(done) { this.timeout(5000); api() .json() .url('http://localhost:3000/foo/') .method('DELETE') .expectStatus(203) .end(function(err, res, body) { if (err) throw err; done(); }); }); }); describe('Test queryparameters', function() { it('should be 404 when request get /fooquery without parameters', function(done) { api() .base('http://localhost:3000') .get('/fooquery') .expectStatus(404) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 404 when request get /fooquery without all parameters', function(done) { api() .base('http://localhost:3000') .get('/fooquery?key=value') .expectStatus(404) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 200 when request get /fooquery with all parameters', function(done) { api() .json() .base('http://localhost:3000') .get('/fooquery?key=value&Content-Type=application/json') .expectStatus(200) .expectHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') .expectHeader('Content-Type', 'application/json; charset=utf-8') .expectBody({"key": "value"}) .end(function(err, res, body) { if (err) throw err; done(); }); }); it('should be 200 when request get /fooquery with all parameters', function(done) { api() .base('http://localhost:3000') .get('/fooquery?Content-Type=application/html') .expectStatus(200) .expectHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') .expectHeader('Content-Type', 'application/html; charset=utf-8') .expectBody("<html>Hello</html>") .end(function(err, res, body) { if (err) throw err; done(); }); }); }); });