diff --git a/README.md b/README.md index 4f3ec99..213e54c 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ apiBenchmark.measure(service, routes, function(err, results){ (String, default 'get'): Http verb. #### route - (String): the route to benchmark + (String): the route to benchmark. In case of function (that has to return an string) it will be evaulated for each request. #### headers (Object): the headers to send. In case of function (that has to return an object) it will be evaulated for each request. diff --git a/lib/request-agent.js b/lib/request-agent.js index 596410d..525a901 100644 --- a/lib/request-agent.js +++ b/lib/request-agent.js @@ -1,6 +1,7 @@ 'use strict'; var _ = require('underscore'); +var url = require('url'); module.exports = function(agent){ this.agent = agent; @@ -12,13 +13,16 @@ module.exports = function(agent){ this.make = function(options, callback){ var data = evalIfFunction(options.data) || {}, query = evalIfFunction(options.query) || {}, + route = evalIfFunction(options.route) || '', headers = evalIfFunction(options.headers) || {}, method = options.method === 'delete' ? 'del' : options.method, - request = this.agent[method](options.url); + service = options.service || '', + requestUrl = url.resolve(service, route), + request = this.agent[method](requestUrl); if(!_.isEmpty(data)) { request.send(data); - } + } if(!_.isEmpty(query)) { request.query(query); diff --git a/lib/request-handler.js b/lib/request-handler.js index 75cfec4..a0673f2 100644 --- a/lib/request-handler.js +++ b/lib/request-handler.js @@ -3,6 +3,7 @@ var format = require('./format'); var settings = require('./settings'); var _ = require('underscore'); +var url = require('url'); module.exports = { make: function(req, suite, suiteName, requestAgent, callback){ @@ -34,9 +35,9 @@ module.exports = { } }); }, - setup: function(suiteName, suiteHref, suite, requestAgent){ + setup: function(suiteName, service, suite, requestAgent){ var self = this, - req = _.extend(_.clone(suite.endpoint), { url: suiteHref }), + req = _.extend(_.clone(suite.endpoint), { service: service }), suiteOptions = { expectedStatusCode: suite.endpoint.expectedStatusCode, maxMean: suite.endpoint.maxMean, @@ -45,6 +46,10 @@ module.exports = { }, suiteRequest = {}; + if(!!suite.endpoint.route){ + suiteRequest.route = _.isFunction(suite.endpoint.route) ? 'Dynamic route on ' + service : url.resolve(service, suite.endpoint.route); + } + if(!!suite.endpoint.headers){ suiteRequest.headers = _.isFunction(suite.endpoint.headers) ? 'Dynamic headers' : suite.endpoint.headers; } @@ -57,7 +62,7 @@ module.exports = { suiteRequest.query = _.isFunction(suite.endpoint.query) ? 'Dynamic query' : suite.endpoint.query; } - suite.runner.add(suiteName, suiteHref, suiteOptions, suiteRequest, function(done){ + suite.runner.add(suiteName, suiteRequest.route, suiteOptions, suiteRequest, function(done){ self.make(req, suite, suiteName, requestAgent, done); }); } diff --git a/lib/suites-manager.js b/lib/suites-manager.js index 3068cda..6030fcb 100644 --- a/lib/suites-manager.js +++ b/lib/suites-manager.js @@ -7,7 +7,7 @@ var ResultsHandler = require('./results-handler'); var Runner = require('./runner'); var sanitise = require('./sanitise'); var settings = require('./settings'); -var url = require('url'); + var validator = require('./validator'); var _ = require('underscore'); @@ -45,11 +45,10 @@ module.exports = function(agent, debugHelper){ _.forEach(this.services, function(service, serviceName){ _.forEach(this.routes, function(route){ - var routeHref = url.resolve(service, route.endpoint.route), - routeName = serviceName + '/' + route.name, + var routeName = serviceName + '/' + route.name, self = this; - requestHandler.setup(routeName, routeHref, route, self.requestAgent); + requestHandler.setup(routeName, service, route, self.requestAgent); }, this); }, this); diff --git a/templates/report.html b/templates/report.html index de8b969..c5de509 100644 --- a/templates/report.html +++ b/templates/report.html @@ -224,9 +224,15 @@ '
  • 99% Percentile: ' + fixNumber(routeData.stats.p99, 6) + '
  • ' + '
  • 99.9% Percentile: ' + fixNumber(routeData.stats.p999, 6) + '
  • ' + '
    ' + - '
    Request: ' + routeData.options.method.toUpperCase() + - ' ' + - routeData.href + '
    Concurrency level: ' + routeData.options.concurrencyLevel + '
    '; + '
    Request: ' + routeData.options.method.toUpperCase() + ' '; + + if (routeData.href.indexOf('Dynamic route') > -1) { + template += routeData.href; + } else { + template += '' + routeData.href + ''; + } + + template += '
    Concurrency level: ' + routeData.options.concurrencyLevel + '
    '; if(routeData.options.delay) template += 'Delay between bench cycles: ' + routeData.options.delay + '
    '; diff --git a/test/fixtures/test-agent.js b/test/fixtures/test-agent.js index 1b32916..6745b94 100644 --- a/test/fixtures/test-agent.js +++ b/test/fixtures/test-agent.js @@ -1,20 +1,23 @@ 'use strict'; -module.exports.FakeAgent = function(){ +module.exports.FakeAgent = function(){ this.end = function(callback){ callback(null, { + url: this.url, data: this.data, headers: this.headers, query: this.queryData }); }; - this.get = function(request){ + this.get = function(url){ + this.url = url; return this; }; - this.post = function(request){ + this.post = function(url){ + this.url = url; return this; }; diff --git a/test/unit/request-agent.js b/test/unit/request-agent.js index 82a36f2..72cb32a 100644 --- a/test/unit/request-agent.js +++ b/test/unit/request-agent.js @@ -18,6 +18,7 @@ describe('requestAgent.make function', function(){ it('should correctly handle null data', function(done){ requestAgent.make({ + service: 'http://service/', route: '/post', method: 'post', data: null @@ -29,6 +30,7 @@ describe('requestAgent.make function', function(){ it('should correctly handle undefined data', function(done){ requestAgent.make({ + service: 'http://service/', route: '/post', method: 'post', data: undefined @@ -38,6 +40,30 @@ describe('requestAgent.make function', function(){ }); }); + it('should correctly handle route as a function', function(done){ + + var i = 0; + + var routeFunc = function(){ + i++; + return '/endpoint'+i; + }; + + var request = { + service: 'http://service/', + route: routeFunc, + method: 'post' + }; + + requestAgent.make(request, function(err, fakeResults){ + fakeResults.url.should.be.eql('http://service/endpoint1'); + requestAgent.make(request, function(err, fakeResults){ + fakeResults.url.should.be.eql('http://service/endpoint2'); + done(); + }); + }); + }); + it('should correctly handle data as a function', function(done){ var i = 0; @@ -48,6 +74,7 @@ describe('requestAgent.make function', function(){ }; var request = { + service: 'http://service/', route: '/post', method: 'post', data: dataFunc @@ -72,6 +99,7 @@ describe('requestAgent.make function', function(){ }; var request = { + service: 'http://service/', route: '/get', method: 'get', query: queryFunc @@ -88,6 +116,7 @@ describe('requestAgent.make function', function(){ it('should correctly handle cookies', function(done){ requestAgent.make({ + service: 'http://service/', route: '/get', method: 'get', headers: { @@ -101,6 +130,7 @@ describe('requestAgent.make function', function(){ it('should correctly handle headers', function(done){ requestAgent.make({ + service: 'http://service/', route: '/get', method: 'get', headers: { @@ -124,6 +154,7 @@ describe('requestAgent.make function', function(){ }; var request = { + service: 'http://service/', route: '/get', method: 'get', headers: headersFunc diff --git a/test/unit/request-handler.js b/test/unit/request-handler.js index 1e3815a..b13b906 100644 --- a/test/unit/request-handler.js +++ b/test/unit/request-handler.js @@ -17,7 +17,8 @@ describe('requestHandler.setup function', function(){ var suiteObj = { endpoint: { - aProperty: 'value' + aProperty: 'value', + route: 'someRoute' }, runner: { add: function(suiteName, suiteHref, suiteOptions, suiteRequest, callback){ @@ -26,11 +27,47 @@ describe('requestHandler.setup function', function(){ } }; - requestHandler.setup('suiteName', 'suiteHref', suiteObj, fakeAgent); + requestHandler.setup('suiteName', 'serviceRoot', suiteObj, fakeAgent); var req = fakeAgentStack[0][0]; - req.url.should.be.eql('suiteHref'); + req.route.should.be.eql('someRoute'); + req.service.should.be.eql('serviceRoot'); + req.aProperty.should.be.eql('value'); + + done(); + }); + + it('should properly handle dynamic routes', function(done){ + + var fakeAgentStack = []; + + var fakeAgent = { + make: function(){ + fakeAgentStack.push(arguments); + } + }; + + var suiteObj = { + endpoint: { + aProperty: 'value', + route: function () { + return 'someDynamicRoute'; + } + }, + runner: { + add: function(suiteName, suiteHref, suiteOptions, suiteRequest, callback){ + suiteRequest.route.should.be.eql('Dynamic route on serviceRoot'); + callback(); + } + } + }; + + requestHandler.setup('suiteName', 'serviceRoot', suiteObj, fakeAgent); + + var req = fakeAgentStack[0][0]; + req.route.should.be.a.Function(); + req.service.should.be.eql('serviceRoot'); req.aProperty.should.be.eql('value'); done();