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

Commit

Permalink
Multiple request data objects; Matching config status codes to swagge…
Browse files Browse the repository at this point in the history
…r spec (#167)

* Made possible to specify multiple request data object for a response as described here: #161
Made config status codes match to status codes in response, so that endpoints with different success codes can be tested

Some ESLint rules don't pass for generated multiple request data scripts, so had to disable them.

* Filter out optional query parameters with no value provided in request data

* Fix for requestParameters not cleared for multiple request data objects

* Updated documentation
  • Loading branch information
sprootshift authored and noahdietz committed May 5, 2018
1 parent be8a957 commit fff9782
Show file tree
Hide file tree
Showing 16 changed files with 734 additions and 24 deletions.
46 changes: 32 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var tests = stt.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`).
* **`pathName`** *required*: List of path names available in your Swagger API spec used to generate tests for. Empty array leads to **all paths**.
* **`statusCodes`** *optional* Array with status codes to generate tests for. Useful for generating only happy-flow tests. Excluding this param will generate tests for all responses.
* **`statusCodes`** *optional* Array with status codes to generate tests for. Useful for generating only happy-flow tests. Excluding this param will generate tests for all responses. A test is generated for the status code only when the status code is listed in the Swagger API spec.
* **`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. If set to `-1`, descriptions will not be truncated. Defaults to `80`.
* **`pathParams`** *optional*: Object containing the values of a specific path parameters.
Expand All @@ -68,7 +68,7 @@ The mock data needs to have the following structure:
{
'/endpoint': {
operation: {
'responseCode': [{ body: {}, description:'some description of the data']
'responseCode': [{ body: {}, description:'some description of the data'}]
}
}
}
Expand All @@ -81,7 +81,7 @@ The mock data needs to have the following structure:
{
'/pet/{name}': {
get: {
'200': [{ name: 'spot', description:'some description of the data']
'200': [{ name: 'spot', description:'some description of the data'}]
}
}
}
Expand All @@ -96,7 +96,7 @@ This will make a request to `/pet?name=spot` assuming that your swagger API has
{
'/pet': {
get: {
'200': [{ name: 'spot', description:'some description of the data']
'200': [{ name: 'spot', description:'some description of the data'}]
}
}
}
Expand All @@ -111,33 +111,49 @@ This will add an HTTP header `X-Token` set to `waestrydtufj` assuming that your
{
'/pet': {
get: {
'200': [{ 'X-Token': 'waestrydtufj', description:'some description of the data']
'200': [{ 'X-Token': 'waestrydtufj', description:'some description of the data'}]
}
}
}

```

so, for example this could be:
Multiple objects within a status code request data array are supported. So, for example this could be:

```javascript
{
'/pet': {
post: {
'200': [{
body: {
'200': [
{
body: {
id: 1,
otherProperty: 'some property that is a string'
},
description: 'the description for this data'
}]
},
{
body: {
id: 2,
otherProperty: 'another value of that property'
},
description: 'the description for another data'
}
]
},
get: {
'200': [ {
guid: 'some_string_to_place_in_path',
anotherPathParam: 100,
description: 'valid path or query parameters'
}]
'200': [
{
guid: 'some_string_to_place_in_path',
anotherPathParam: 100,
description: 'valid path or query parameters'
},
{
guid: 'some_other_string_to_place_in_path',
anotherPathParam: 200,
description: 'another valid path or query parameters'
}
]
}
}
}
Expand All @@ -147,6 +163,8 @@ Note: for get-requests matching data will be transferred to the pathParams. So s

Every mockData item in the `responseCode` array will be used to generate a test. The description will be added to the "it" function for reference.

Parameters explicitly marked as `required: false` in your Swagger API spec, will only be set if there is a matching value in requestData object. Required parameters and parameters without explicitly set `required` flag in Swagger API spec will be set to either a matching value in requestData object or 'DATA GOES HERE' string.

## License

[MIT](/LICENSE)
58 changes: 54 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function getData(swagger, apiPath, operation, response, config, info) {
});
}

if (grandProperty.responses[response].hasOwnProperty('schema')) {
if (grandProperty.responses[response] && grandProperty.responses[response].hasOwnProperty('schema')) {
data.noSchema = false;
data.schema = grandProperty.responses[response].schema;
data.schema = JSON.stringify(data.schema, null, 2);
Expand Down Expand Up @@ -223,6 +223,49 @@ function getData(swagger, apiPath, operation, response, config, info) {
return data;
}

/**
* Populate path params from request data array
* @private
* @param {json} data Generated Data
* @param {json} config configuration for testGen
* @param {int} idx Index of request for response
* @returns {json} return all the properties information
*/
function setPathParamsFromArray(data, config, idx) {
// only write parameters if they are not already defined in config
if (data.requestData === undefined || config.pathParams) {
return data;
}
// if we have requestData, fill the path params accordingly
var mockParameters = {};

data.pathParameters.forEach(function(parameter) {
// find the mock data for this parameter name
mockParameters[parameter.name] = data.requestData.filter(function(mock) {
return mock.hasOwnProperty(parameter.name);
})[idx][parameter.name];
});
data.pathParams = mockParameters;
return data;
}

/**
* Filter out optional query parameters with no value provided in request data
* @private
* @param {json} data Generated Data
* @returns {json} return all the properties information
*/
function filterOutOptionalQueryParams(data) {
data.queryParameters = data.queryParameters.filter(function(queryParam) {
// Let's be conservative and treat params without explicit required field as not-optional
var optional = queryParam.required !== undefined && !queryParam.required;
var dataProvided = data.requestParameters.hasOwnProperty(queryParam.name);

return !optional || dataProvided;
});
return data;
}

/**
* Builds a unit test stubs for the response code of a apiPath's operation
* @private
Expand Down Expand Up @@ -265,16 +308,21 @@ function testGenResponse(swagger, apiPath, operation, response, config, consume,
if (data.requestData && data.requestData.length > 0) {
result = '';
for (var i = 0; i < data.requestData.length; i++) {
data.requestParameters = {};
data = setPathParamsFromArray(data, config, i);
data.request = JSON.stringify(data.requestData[i].body);

for (var key in data.requestData[i]) {
if (['body', 'description'].indexOf(key) === -1) {
data.requestParameters[key] = data.requestData[i][key];
}
}

data.requestMessage = data.requestData[i].description.replace(/'/g, "\\'"); // eslint-disable-line quotes
result += templateFn(data);

var filteredData = _.cloneDeep(data);

filteredData = filterOutOptionalQueryParams(filteredData);
result += templateFn(filteredData);
}
} else {
result = templateFn(data);
Expand Down Expand Up @@ -333,7 +381,9 @@ function testGenOperation(swagger, apiPath, operation, config, info) {
if (config.statusCodes) {
responses = {};
config.statusCodes.forEach(function(code) {
responses[code] = swagger.paths[apiPath][operation].responses[code];
if (swagger.paths[apiPath][operation].responses[code]) {
responses[code] = swagger.paths[apiPath][operation].responses[code];
}
});
}

Expand Down
11 changes: 11 additions & 0 deletions test/request-data/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extends: ../../.eslintrc
env:
mocha: true
rules:
indent: false
no-unused-expressions: false
valid-jsdoc: 0;
quotes: [0]
key-spacing: false
quote-props: false
comma-spacing: false
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ describe('/products', function() {

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
});
it('should respond with 200 OK and some description', function(done) {
request({
url: 'https://api.uber.com/products',
json: true,
method: 'POST',
headers: {
'Content-Type': 'application/vnd:something+json'
},
body: {"id":1,"name":"product"}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,42 @@ describe('/products/{id}', function() {

expect(res.statusCode).to.equal(200);

expect(validator.validate(body, schema)).to.be.true;
done();
});
});
it('should respond with 200 Return product and some other description', function(done) {
/*eslint-disable*/
var schema = {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string"
}
}
};

/*eslint-enable*/
request({
url: 'https://api.uber.com/products/3',
json: true,
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(validator.validate(body, schema)).to.be.true;
done();
});
Expand All @@ -110,6 +146,25 @@ describe('/products/{id}', function() {

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
});
it('should respond with 200 OK and some other description', function(done) {
request({
url: 'https://api.uber.com/products/3',
json: true,
method: 'PUT',
headers: {
'Content-Type': 'application/vnd:something+Json'
},
body: {}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
Expand Down
22 changes: 22 additions & 0 deletions test/request-data/compare/request/expect/qs1-user-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ describe('/user', function() {
done();
});
});
it('should respond with 200 OK and some other description', function(done) {
request({
url: 'https://api.uber.com/v1/user',
json: true,
qs: {
longitude: 20
},
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: {"my-id":3}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
});

it('should respond with 400 NOT OK', function(done) {
request({
Expand Down
21 changes: 21 additions & 0 deletions test/request-data/compare/request/expect/qs2-user-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ describe('/user', function() {
done();
});
});
it('should respond with 200 OK and some other description', function(done) {
request({
url: 'https://api.uber.com/user',
json: true,
qs: {
longitude: 20
},
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
});

it('should respond with 400 NOT OK', function(done) {
request({
Expand Down
21 changes: 21 additions & 0 deletions test/request-data/compare/request/expect/qs3-user-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ describe('/user', function() {
done();
});
});
it('should respond with 200 OK and some other description', function(done) {
request({
url: 'https://api.uber.com/user',
json: true,
qs: {
name: 'Garfunkel'
},
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
},
function(error, res, body) {
if (error) return done(error);

expect(res.statusCode).to.equal(200);

expect(body).to.equal(null); // non-json response or no schema
done();
});
});

it('should respond with 400 NOT OK', function(done) {
request({
Expand Down
Loading

0 comments on commit fff9782

Please sign in to comment.