Skip to content
This repository has been archived by the owner on May 15, 2023. It is now read-only.

Commit

Permalink
Merge pull request #79 from apigee-127/loadTest
Browse files Browse the repository at this point in the history
add the load testing feature into module
  • Loading branch information
mohsen1 committed Aug 13, 2015
2 parents 127bff8 + b4d7ee7 commit a03ed71
Show file tree
Hide file tree
Showing 38 changed files with 4,994 additions and 31 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var config = {
assertionFormat: 'should',
testModule: 'supertest',
pathNames: ['/user', '/user/{id}'],
loadTest: [{pathName:'/user', operation:'get', load:{requests: 1000, concurrent: 100}}, { /* ... */ }],
maxLen: 80
};

Expand All @@ -36,6 +37,7 @@ testGen(swagger, config);
* **`assertionFormat`** *required*: One of `should`, `expect` or `assert`. Choose which assertion method should be used in output test code.
* **`testModule`** *required*: One of `supertest` or `request`. Choose between direct API calls (`request`) vs. programatic access to your API (`supertest`).
* **`pathNames`** *required*: List of path names available in your Swagger API spec used to generate tests for. Empty array leads to **all paths**.
* **`loadTest`** *optional*: List of objects info in your Swagger API spec used to generate stress tests. If specify, pathName & operation are **required**. Optional fields requests defaults to `1000`, concurrent defaults to `100`.
* **`maxLen`** *optional*: Maximum line length. Defaults to `80`.

#### Return value
Expand Down
34 changes: 31 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,30 @@ function getData(swagger, path, operation, response, config, info) {
queryApiKey: null,
headerApiKey: null,
headerSecurity: null,
path: ''
path: '',
isLoadTest: false,
loadName: '',
requests: 0,
concurrent: 0
};

// cope with loadTest info
if (info.loadTest != null) {
_.forEach(info.loadTest, function(loadTestParam) {
if (loadTestParam.pathName === path
&& loadTestParam.operation === operation) {
data.loadName = path.replace(/\//g, '_') +
'_' + operation + '_load_test';
info.importArete = true;
data.isLoadTest = true;
data.requests = loadTestParam.load.requests !== undefined ?
loadTestParam.load.requests : 1000;
data.concurrent = loadTestParam.load.concurrent !== undefined ?
loadTestParam.load.concurrent : 100;
}
});
}

// deal with the security properties
if (info.security && info.security.length !== 0) {
Object.keys(info.security[0]).forEach(function(element) {
Expand Down Expand Up @@ -336,11 +357,17 @@ function testGenPath(swagger, path, config) {
var info = {
importValidator: false,
importEnv: false,
importArete: false,
consumes: [],
produces: [],
security: []
security: [],
loadTest: null
};

if (config.loadTest) {
info.loadTest = config.loadTest;
}

source = read(join(__dirname, '/templates/outerDescribe.handlebars'), 'utf8');
outerDescribeFn = handlebars.compile(source, {noEscape: true});

Expand All @@ -361,7 +388,8 @@ function testGenPath(swagger, path, config) {
host: (swagger.host !== undefined ? swagger.host : 'localhost:10010'),
tests: result,
importValidator: info.importValidator,
importEnv: info.importEnv
importEnv: info.importEnv,
importArete: info.importArete
};

if (!allDeprecated) {
Expand Down
6 changes: 5 additions & 1 deletion templates/outerDescribe.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ chai.should();
{{#is assertion 'assert'}}
var assert = chai.assert;
{{/is}}
{{#if importArete}}
var arete = require('arete');
{{/if}}
{{#if importEnv}}

require('dotenv').load();{{/if}}
require('dotenv').load();
{{/if}}

describe('{{description}}', function() {
{{#each tests}}
Expand Down
4 changes: 1 addition & 3 deletions templates/request/delete/delete.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
method: 'DELETE',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
'Custom-Header': { {{#each headerParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}
}{{/if}}{{#if headerApiKey}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
}
Expand Down
66 changes: 63 additions & 3 deletions templates/request/get/get.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
method: 'GET',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
'Custom-Header': { {{#each headerParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}
}{{/if}}{{#if headerApiKey}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
}
Expand Down Expand Up @@ -72,3 +70,65 @@
done();
});
});
{{#if isLoadTest}}
it('load tests with {{length description}}', function(done) {
arete.loadTest({
name: '{{loadName}}',
requests: {{requests}},
concurrentRequests: {{concurrent}},
targetFunction: function(callback) {
request({
url: '{{pathify path}}',
{{#ifCond queryParameters queryApiKey}}
qs: {
{{#if queryApiKey}}{{queryApiKey.type}}: process.env.{{queryApiKey.name}}{{#if queryParameters}},
{{/if}}{{/if}}{{#if queryParameters}}{{#each queryParameters}}{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}
},
{{/ifCond}}
method: 'GET',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
}
},
function(error, res, body) {
callback(error, body);
});
},
printResponses: false, // true or false
printReport: true, // true or false
printSteps: true, // true or false
callback: function(error, report) {
if (error) return done(error);

{{#is assertion 'expect'}}
expect(report.successfulResponses.length).
to.equal(report.results.length);
expect(report.averageResponseTimeInternal).
to.be.lessThan('TIME DATA HERE');
expect(report.timeElapsed).
to.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'should'}}
report.successfulResponses.length.
should.equal(report.results.length);
(report.averageResponseTimeInternal).
should.be.lessThan('TIME DATA HERE');
(report.timeElapsed).
should.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'assert'}}
assert.equal(report.successfulResponses.length,
report.results.length);
assert.isBelow(report.averageResponseTimeInternal,
'TIME DATA HERE');
assert.isBelow(report.timeElapsed,
'TIME DATA HERE');
{{/is}}
done();
}
});
});
{{/if}}
4 changes: 1 addition & 3 deletions templates/request/head/head.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
method: 'HEAD',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
'Custom-Header': { {{#each headerParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}
}{{/if}}{{#if headerApiKey}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
}
Expand Down
93 changes: 90 additions & 3 deletions templates/request/patch/patch.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
{{/ifCond}} method: 'PATCH',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
'Custom-Header': { {{#each headerParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}
}{{/if}}{{#if headerApiKey}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
},
Expand Down Expand Up @@ -98,3 +96,92 @@
done();
});
});
{{#if isLoadTest}}
it('load tests with {{length description}}', function(done) {
arete.loadTest({
name: '{{loadName}}',
requests: {{requests}},
concurrentRequests: {{concurrent}},
targetFunction: function(callback) {
request({
url: '{{pathify path}}',
{{#ifCond queryParameters queryApiKey}}
qs: {
{{#if queryApiKey}}{{queryApiKey.type}}: process.env.{{queryApiKey.name}}{{#if queryParameters}},
{{/if}}{{/if}}{{#if queryParameters}}{{#each queryParameters}}{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}
},
{{/ifCond}}
method: 'PATCH',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
},
{{#is contentType 'application/json'}}
json: {
{{#each bodyParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'application/x-www-form-urlencoded'}}
form: {
{{#each formParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'multipart/form-data'}}
form: {
{{#each formParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'application/xml'}}
body: 'XML STRING GOES HERE'
},
{{/is}}
function(error, res, body) {
callback(error, body);
});
},
printResponses: false, // true or false
printReport: true, // true or false
printSteps: true, // true or false
callback: function(error, report) {
if (error) return done(error);

{{#is assertion 'expect'}}
expect(report.successfulResponses.length).
to.equal(report.results.length);
expect(report.averageResponseTimeInternal).
to.be.lessThan('TIME DATA HERE');
expect(report.timeElapsed).
to.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'should'}}
report.successfulResponses.length.
should.equal(report.results.length);
(report.averageResponseTimeInternal).
should.be.lessThan('TIME DATA HERE');
(report.timeElapsed).
should.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'assert'}}
assert.equal(report.successfulResponses.length,
report.results.length);
assert.isBelow(report.averageResponseTimeInternal,
'TIME DATA HERE');
assert.isBelow(report.timeElapsed,
'TIME DATA HERE');
{{/is}}
done();
}
});
});
{{/if}}
93 changes: 90 additions & 3 deletions templates/request/post/post.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
method: 'POST',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
'Custom-Header': { {{#each headerParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}
}{{/if}}{{#if headerApiKey}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
},
Expand Down Expand Up @@ -99,3 +97,92 @@
done();
});
});
{{#if isLoadTest}}
it('load tests with {{length description}}', function(done) {
arete.loadTest({
name: '{{loadName}}',
requests: {{requests}},
concurrentRequests: {{concurrent}},
targetFunction: function(callback) {
request({
url: '{{pathify path}}',
{{#ifCond queryParameters queryApiKey}}
qs: {
{{#if queryApiKey}}{{queryApiKey.type}}: process.env.{{queryApiKey.name}}{{#if queryParameters}},
{{/if}}{{/if}}{{#if queryParameters}}{{#each queryParameters}}{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}
},
{{/ifCond}}
method: 'POST',
headers: {
'Content-Type': '{{contentType}}'{{#if headerParameters}},
{{#each headerParameters}}'{{this.name}}': 'DATA GOES HERE'{{#unless @last}},{{/unless}}{{/each}}{{/if}}{{#if headerApiKey}},
{{headerApiKey.type}}: process.env.{{headerApiKey.name}}{{/if}}{{#if headerSecurity}},
Authorization: '{{headerSecurity.type}} ' + process.env.{{headerSecurity.name}}{{/if}}
},
{{#is contentType 'application/json'}}
json: {
{{#each bodyParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'application/x-www-form-urlencoded'}}
form: {
{{#each formParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'multipart/form-data'}}
form: {
{{#each formParameters}}
{{this.name}}: 'DATA GOES HERE'{{#unless @last}},{{/unless}}
{{/each}}
}
},
{{/is}}
{{#is contentType 'application/xml'}}
body: 'XML STRING GOES HERE'
},
{{/is}}
function(error, res, body) {
callback(error, body);
});
},
printResponses: false, // true or false
printReport: true, // true or false
printSteps: true, // true or false
callback: function(error, report) {
if (error) return done(error);

{{#is assertion 'expect'}}
expect(report.successfulResponses.length).
to.equal(report.results.length);
expect(report.averageResponseTimeInternal).
to.be.lessThan('TIME DATA HERE');
expect(report.timeElapsed).
to.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'should'}}
report.successfulResponses.length.
should.equal(report.results.length);
(report.averageResponseTimeInternal).
should.be.lessThan('TIME DATA HERE');
(report.timeElapsed).
should.be.lessThan('TIME DATA HERE');
{{/is}}
{{#is assertion 'assert'}}
assert.equal(report.successfulResponses.length,
report.results.length);
assert.isBelow(report.averageResponseTimeInternal,
'TIME DATA HERE');
assert.isBelow(report.timeElapsed,
'TIME DATA HERE');
{{/is}}
done();
}
});
});
{{/if}}
Loading

0 comments on commit a03ed71

Please sign in to comment.