Skip to content

Commit

Permalink
follow response $refs
Browse files Browse the repository at this point in the history
  • Loading branch information
cdimascio committed Jun 9, 2020
1 parent ff76d38 commit fb7abfb
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 32 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
"compile": "rm -rf dist/ && tsc",
"compile:windows": "rmdir dist /s /q & tsc",
"test": "TS_NODE_FILES=true mocha -r source-map-support/register -r ts-node/register --files --recursive test/**/*.spec.ts",
"test:debug": "TS_NODE_FILES=true mocha -r source-map-support/register -r ts-node/register --inspect-brk --files --recursive test/**/*.spec.ts",
"test:windows": "set TS_NODE_FILES=true & mocha -r source-map-support/register -r ts-node/register --files --recursive test/**/*.spec.ts",
"test:coverage": "TS_NODE_FILES=true nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts",
"test:coverage:windows": "set TS_NODE_FILES=true & nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts",
"coveralls": "cat coverage/lcov.info | coveralls -v",
"codacy": "cat coverage/lcov.info | codacy-coverage",
"test:reset": "rm -rf node_modules && npm i && npm run compile && npm t"

},
"repository": {
"url": "https://github.com/cdimascio/express-openapi-validator"
Expand Down
4 changes: 2 additions & 2 deletions src/framework/ajv/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function createAjv(
ajv.removeKeyword('readOnly');
ajv.addKeyword('readOnly', {
modifying: true,
compile: sch => {
compile: (sch) => {
if (sch) {
return function validate(data, path, obj, propName) {
const isValid = !(sch === true && data != null);
Expand All @@ -65,7 +65,7 @@ function createAjv(
ajv.removeKeyword('writeOnly');
ajv.addKeyword('writeOnly', {
modifying: true,
compile: sch => {
compile: (sch) => {
if (sch) {
return function validate(data, path, obj, propName) {
const isValid = !(sch === true && data != null);
Expand Down
9 changes: 8 additions & 1 deletion src/middlewares/openapi.response.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,14 @@ export class ResponseValidator {
};

const responseSchemas = {};
for (const [name, response] of <any[]>Object.entries(responses)) {
for (const [name, resp] of <any[]>Object.entries(responses)) {
let tmpResponse = resp;
if (tmpResponse.$ref) {
// resolve response
const id = tmpResponse.$ref.replace(/^.+\//i, '');
tmpResponse = this.spec.components?.responses?.[id];
}
const response = tmpResponse;
const types = validationTypes(response);

for (const mediaTypeToValidate of types) {
Expand Down
20 changes: 17 additions & 3 deletions test/resources/response.validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ info:
servers:
- url: /v1
paths:
/ref_response_body:
get:
operationId: refResponseBody
responses:
'200':
$ref: '#/components/responses/ResponseBody'
/empty_response:
description: get empty response
summary: get empty response
get:
description: get empty response
operationId: get empty resp
operationId: getEmptyResponse
parameters:
- name: mode
in: query
Expand Down Expand Up @@ -146,8 +152,8 @@ paths:
post:
description: Get todos
operationId: getTodos
security:
- bearerAuth: []
# security:
# - bearerAuth: []
responses:
'200':
description: no additional props
Expand All @@ -157,6 +163,14 @@ paths:
$ref: '#/components/schemas/NoAdditionalProps'

components:
responses:
ResponseBody:
description: Sample response
content:
application/json:
schema:
$ref: '#/components/schemas/Human'

schemas:
Object:
type: object
Expand Down
48 changes: 22 additions & 26 deletions test/response.validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ describe(packageJson.name, () => {
validateResponses: true,
},
3005,
app => {
(app) => {
app.get(`${app.basePath}/ref_response_body`, (req, res) => {
return res.json({ id: 213, name: 'name', kids: [] });
});
app.get(`${app.basePath}/empty_response`, (req, res) => {
return res.end();
});
Expand Down Expand Up @@ -61,6 +64,7 @@ describe(packageJson.name, () => {
res.json(req.body);
});
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status ?? 500).json({
message: err.message,
code: err.status ?? 500,
Expand All @@ -75,27 +79,31 @@ describe(packageJson.name, () => {
app.server.close();
});

it('should return 200 on valid responses 200 $ref', async () =>
request(app)
.get(`${app.basePath}/ref_response_body`)
.expect(200)
.then((r: any) => {
expect(r.body.id).to.be.a('number').that.equals(213);
}));

it('should fail if response field has a value of incorrect type', async () =>
request(app)
.get(`${app.basePath}/pets?mode=bad_type`)
.expect(500)
.then((r: any) => {
expect(r.body.message).to.contain('should be integer');
expect(r.body)
.to.have.property('code')
.that.equals(500);
expect(r.body).to.have.property('code').that.equals(500);
}));

// TODO add test for allOf - when allOf is used an array value passes when object is expected
// TODO add test for allOf - when allOf is used an array value passes when object is expected
it('should fail if response is array when expecting object', async () =>
request(app)
.get(`${app.basePath}/object`)
.expect(500)
.then((r: any) => {
expect(r.body.message).to.contain('should be object');
expect(r.body)
.to.have.property('code')
.that.equals(500);
expect(r.body).to.have.property('code').that.equals(500);
}));

// TODO fix me - fails for response and request validation what allOf is in use
Expand All @@ -106,9 +114,7 @@ describe(packageJson.name, () => {
.expect(400)
.then((r: any) => {
expect(r.body.message).to.contain('should be object');
expect(r.body)
.to.have.property('code')
.that.equals(500);
expect(r.body).to.have.property('code').that.equals(500);
}));

it('should fail if response is response is empty object', async () =>
Expand All @@ -118,9 +124,7 @@ describe(packageJson.name, () => {
.then((r: any) => {
console.log(r.body);
expect(r.body.message).to.contain('should be array');
expect(r.body)
.to.have.property('code')
.that.equals(500);
expect(r.body).to.have.property('code').that.equals(500);
}));

it('should fail if response is response is empty', async () =>
Expand All @@ -129,15 +133,11 @@ describe(packageJson.name, () => {
.expect(500)
.then((r: any) => {
expect(r.body.message).to.contain('body required');
expect(r.body)
.to.have.property('code')
.that.equals(500);
expect(r.body).to.have.property('code').that.equals(500);
}));

it('should return 200 for endpoints that return empty response', async () =>
request(app)
.get(`${app.basePath}/empty_response`)
.expect(200));
request(app).get(`${app.basePath}/empty_response`).expect(200));

it('should fail if additional properties are provided when set false', async () =>
request(app)
Expand Down Expand Up @@ -184,9 +184,7 @@ describe(packageJson.name, () => {
.get(`${app.basePath}/pets?mode=check_null`)
.expect(200)
.then((r: any) => {
expect(r.body)
.is.an('array')
.with.length(3);
expect(r.body).is.an('array').with.length(3);
expect(r.body[0].bought_at).equal(null);
expect(r.body[1].bought_at).equal(today.toISOString());
expect(r.body[2].bought_at).to.be.undefined;
Expand All @@ -197,8 +195,6 @@ describe(packageJson.name, () => {
.get(`${app.basePath}/users`)
.expect(200)
.then((r: any) => {
expect(r.body)
.is.an('array')
.with.length(3);
expect(r.body).is.an('array').with.length(3);
}));
});

0 comments on commit fb7abfb

Please sign in to comment.