Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support server variables #222

Merged
merged 16 commits into from
Mar 6, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
openapi: 3.0.0
info:
title: Example OpenApi 3 spec
description: Defines servers using server variables
version: 0.1.0
servers:
- url: /{variableInPath}
description: server url with 1 server variable in the path
variables:
variableInPath:
default: defaultValueOfVariableInPath
enum:
- enumValueOfVariableInPath
- url: /{firstVariableInPath}/{secondVariableInPath}
description: server url with multiple server variables in the path
variables:
firstVariableInPath:
default: defaultValueOfFirstVariableInPath
secondVariableInPath:
default: defaultValueOfSecondVariableInPath
- url: https://{hostVariable}.com:{portVariable}/
description: server url with server variables only before the path
variables:
hostVariable:
default: defaultValueOfHostVariable
portVariable:
default: '1234'
- url: https://{hostVariable}.com:{portVariable}/{variableInDifferentPath}
description: server url with server variables before and after the path
variables:
hostVariable:
default: defaultValueOfHostVariable
portVariable:
default: '1234'
variableInDifferentPath:
default: defaultValueOfVariableInDifferentPath
paths:
/endpointPath:
get:
responses:
200:
description: Response body should be a string
content:
application/json:
schema:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -394,4 +394,143 @@ describe('Using OpenAPI 3 specs that define servers differently', () => {
});
});
});

describe('spec defines servers using server variables', () => {
before(() => {
const pathToApiSpec = path.join(
dirContainingApiSpec,
'withServerVariables.yml',
);
chai.use(chaiResponseValidator(pathToApiSpec));
});

describe('res.req.path matches a server with a server variable in the path (matches the default value)', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).to.satisfyApiSpec;
});

it('fails when using .not', () => {
const assertion = () => expect(res).to.not.satisfyApiSpec;
expect(assertion).to.throw(AssertionError, '');
});
});

describe('res.req.path matches a server with a server variable in the path (matches a non-default value)', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/enumValueOfVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).to.satisfyApiSpec;
});

it('fails when using .not', () => {
const assertion = () => expect(res).to.not.satisfyApiSpec;
expect(assertion).to.throw(AssertionError, '');
});
});

describe('res.req.path matches a server with multiple server variables in the path', () => {
const res = {
status: 200,
req: {
method: 'GET',
path:
'/defaultValueOfFirstVariableInPath/defaultValueOfSecondVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).to.satisfyApiSpec;
});

it('fails when using .not', () => {
const assertion = () => expect(res).to.not.satisfyApiSpec;
expect(assertion).to.throw(AssertionError, '');
});
});

describe('res.req.path matches a server with server variables only before the path', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).to.satisfyApiSpec;
});

it('fails when using .not', () => {
const assertion = () => expect(res).to.not.satisfyApiSpec;
expect(assertion).to.throw(AssertionError, '');
});
});

describe('res.req.path matches a server with server variables before and after the path', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInDifferentPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).to.satisfyApiSpec;
});

it('fails when using .not', () => {
const assertion = () => expect(res).to.not.satisfyApiSpec;
expect(assertion).to.throw(AssertionError, '');
});
});

describe('res.req.path matches a server with server variables, but matches no endpoint paths', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInPath/nonExistentEndpointPath',
},
body: 'valid body (string)',
};

it('fails', () => {
const assertion = () => expect(res).to.satisfyApiSpec;
expect(assertion).to.throw(
AssertionError,
`'/defaultValueOfVariableInPath/nonExistentEndpointPath' matches servers ${str(
[
'/defaultValueOfVariableInPath',
'https://{hostVariable}.com:{portVariable}/',
],
)} but no <server/endpointPath> combinations`,
);
});

it('passes when using .not', () => {
expect(res).to.not.satisfyApiSpec;
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,143 @@ describe('Using OpenAPI 3 specs that define servers differently', () => {
});
});
});

describe('spec defines servers using server variables', () => {
beforeAll(() => {
const pathToApiSpec = path.join(
dirContainingApiSpec,
'withServerVariables.yml',
);
jestOpenAPI(pathToApiSpec);
});

describe('res.req.path matches a server with a server variable in the path (matches the default value)', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).toSatisfyApiSpec();
});

it('fails when using .not', () => {
const assertion = () => expect(res).not.toSatisfyApiSpec();
expect(assertion).toThrow(Error, '');
});
});

describe('res.req.path matches a server with a server variable in the path (matches a non-default value)', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/enumValueOfVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).toSatisfyApiSpec();
});

it('fails when using .not', () => {
const assertion = () => expect(res).not.toSatisfyApiSpec();
expect(assertion).toThrow(Error, '');
});
});

describe('res.req.path matches a server with multiple server variables in the path', () => {
const res = {
status: 200,
req: {
method: 'GET',
path:
'/defaultValueOfFirstVariableInPath/defaultValueOfSecondVariableInPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).toSatisfyApiSpec();
});

it('fails when using .not', () => {
const assertion = () => expect(res).not.toSatisfyApiSpec();
expect(assertion).toThrow(Error, '');
});
});

describe('res.req.path matches a server with server variables only beforeAll the path', () => {
rwalle61 marked this conversation as resolved.
Show resolved Hide resolved
const res = {
status: 200,
req: {
method: 'GET',
path: '/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).toSatisfyApiSpec();
});

it('fails when using .not', () => {
const assertion = () => expect(res).not.toSatisfyApiSpec();
expect(assertion).toThrow(Error, '');
});
});

describe('res.req.path matches a server with server variables beforeAll and after the path', () => {
rwalle61 marked this conversation as resolved.
Show resolved Hide resolved
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInDifferentPath/endpointPath',
},
body: 'valid body (string)',
};

it('passes', () => {
expect(res).toSatisfyApiSpec();
});

it('fails when using .not', () => {
const assertion = () => expect(res).not.toSatisfyApiSpec();
expect(assertion).toThrow(Error, '');
});
});

describe('res.req.path matches a server with server variables, but matches no endpoint paths', () => {
const res = {
status: 200,
req: {
method: 'GET',
path: '/defaultValueOfVariableInPath/nonExistentEndpointPath',
},
body: 'valid body (string)',
};

it('fails', () => {
const assertion = () => expect(res).toSatisfyApiSpec();
expect(assertion).toThrow(
Error,
`'/defaultValueOfVariableInPath/nonExistentEndpointPath' matches servers ${str(
[
'/defaultValueOfVariableInPath',
'https://{hostVariable}.com:{portVariable}/',
],
)} but no <server/endpointPath> combinations`,
);
});

it('passes when using .not', () => {
expect(res).not.toSatisfyApiSpec();
});
});
});
});
6 changes: 3 additions & 3 deletions packages/openapi-validator/lib/classes/AbstractOpenApiSpec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const OpenAPIResponseValidator = require('openapi-response-validator').default;

const { extractPathname } = require('../utils');
const { getPathname } = require('../utils/common.utils');
const ValidationError = require('./errors/ValidationError');

class OpenApiSpec {
Expand Down Expand Up @@ -57,7 +57,7 @@ class OpenApiSpec {
}

findOpenApiPathMatchingRequest(actualRequest) {
const actualPathname = extractPathname(actualRequest);
const actualPathname = getPathname(actualRequest);
const openApiPath = this.findOpenApiPathMatchingPathname(actualPathname);
return openApiPath;
}
Expand All @@ -68,7 +68,7 @@ class OpenApiSpec {
* @see OpenAPI3 {@link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject}
*/
findExpectedPathItem(actualRequest) {
const actualPathname = extractPathname(actualRequest);
const actualPathname = getPathname(actualRequest);
const openApiPath = this.findOpenApiPathMatchingPathname(actualPathname);
const pathItemObject = this.getPathItem(openApiPath);
return pathItemObject;
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-validator/lib/classes/AbstractResponse.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { stringify } = require('../utils');
const { stringify } = require('../utils/common.utils');

class AbstractResponse {
constructor(res) {
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-validator/lib/classes/OpenApi2Spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const utils = require('../utils');
const utils = require('../utils/common.utils');
const AbstractOpenApiSpec = require('./AbstractOpenApiSpec');
const ValidationError = require('./errors/ValidationError');

Expand Down