Skip to content

Commit

Permalink
Merge pull request #155 from apiaryio/150-refactor
Browse files Browse the repository at this point in the history
Modularize validation
  • Loading branch information
artem-zakharchenko authored May 20, 2019
2 parents 577f9b5 + 20ec986 commit c03a0f9
Show file tree
Hide file tree
Showing 29 changed files with 1,884 additions and 17 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
node: true
},
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'consistent-return': 'off',
'class-methods-use-this': 'off',
'no-plusplus': 'off',
Expand Down
9 changes: 6 additions & 3 deletions lib/gavel.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
const { HttpRequest, ExpectedHttpRequest } = require('./model/http-request');
const { HttpResponse, ExpectedHttpResponse } = require('./model/http-response');

const { validate, isValid, isValidatable } = require('./validate');
const { isValid, isValidatable } = require('./validate');
const validate = require('./next/validate');

module.exports = {
// next
validate,

// legacy
HttpRequest,
HttpResponse,
ExpectedHttpRequest,
ExpectedHttpResponse,
validate,
isValid,
isValidatable
};
348 changes: 348 additions & 0 deletions lib/next/test/integration/validateMessage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
const { assert } = require('chai');
const { validateMessage } = require('../../validateMessage');

describe('validateMessage', () => {
describe('with matching requests', () => {
const request = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: '{ "foo": "bar" }'
};
const result = validateMessage(request, request);

it('returns validation result object', () => {
assert.isObject(result);
});

it('contains all validatable keys', () => {
assert.hasAllKeys(result, ['headers', 'body']);
});

describe('headers', () => {
it('has "HeadersJsonExample" validator', () => {
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
});

it('has "application/vnd.apiary.http-headers+json" real headers type', () => {
assert.propertyVal(
result.headers,
'realType',
'application/vnd.apiary.http-headers+json'
);
});

it('has "application/vnd.apiary.http-headers+json" expected headers type', () => {
assert.propertyVal(
result.headers,
'expectedType',
'application/vnd.apiary.http-headers+json'
);
});

it('has no errors', () => {
assert.lengthOf(result.headers.results, 0);
});
});

describe('body', () => {
it('has "JsonExample" validator', () => {
assert.propertyVal(result.body, 'validator', 'JsonExample');
});

it('has "application/json" real body type', () => {
assert.propertyVal(result.body, 'realType', 'application/json');
});

it('has "application/json" expected body type', () => {
assert.propertyVal(result.body, 'expectedType', 'application/json');
});

it('has no errors', () => {
assert.lengthOf(result.body.results, 0);
});
});
});

describe('with non-matching requests', () => {
const result = validateMessage(
{
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: '{ "lookHere": "foo" }'
},
{
method: 'PUT',
headers: '',
body: '2'
}
);

it('returns validation result object', () => {
assert.isObject(result);
});

it('contains all validatable keys', () => {
assert.hasAllKeys(result, ['headers', 'body']);
});

describe('method', () => {
// See https://github.com/apiaryio/gavel.js/issues/158
it.skip('compares methods');
});

describe('headers', () => {
it('has "HeadersJsonExample" validator', () => {
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
});

it('has "application/vnd.apiary.http-headers+json" as real type', () => {
assert.propertyVal(
result.headers,
'realType',
'application/vnd.apiary.http-headers+json'
);
});

it('has "application/vnd.apiary.http-headers+json" expected type', () => {
assert.propertyVal(
result.headers,
'expectedType',
'application/vnd.apiary.http-headers+json'
);
});

it('has no errors', () => {
assert.lengthOf(result.headers.results, 0);
});
});

describe('body', () => {
it('has "JsonExample" validator', () => {
assert.propertyVal(result.body, 'validator', 'JsonExample');
});

it('has "application/json" real type', () => {
assert.propertyVal(result.body, 'realType', 'application/json');
});

it('has "application/json" expected type', () => {
assert.propertyVal(result.body, 'expectedType', 'application/json');
});

describe('produces an error', () => {
it('exactly one error', () => {
assert.lengthOf(result.body.results, 1);
});

it('has "error" severity', () => {
assert.propertyVal(result.body.results[0], 'severity', 'error');
});

it('has explanatory message', () => {
assert.propertyVal(
result.body.results[0],
'message',
`At '' Invalid type: object (expected integer)`
);
});
});
});
});

describe('with matching responses', () => {
const response = {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: '{ "foo": "bar" }'
};
const result = validateMessage(response, response);

it('returns validation result object', () => {
assert.isObject(result);
});

it('contains all validatable keys', () => {
assert.hasAllKeys(result, ['statusCode', 'headers', 'body']);
});

describe('statusCode', () => {
it('has "TextDiff" validator', () => {
assert.propertyVal(result.statusCode, 'validator', 'TextDiff');
});

it('has "text/vnd.apiary.status-code" real type', () => {
assert.propertyVal(
result.statusCode,
'realType',
'text/vnd.apiary.status-code'
);
});

it('has "text/vnd.apiary.status-code" expected type', () => {
assert.propertyVal(
result.statusCode,
'expectedType',
'text/vnd.apiary.status-code'
);
});

it('has no errors', () => {
assert.lengthOf(result.statusCode.results, 0);
});
});

describe('headers', () => {
it('has "HeadersJsonExample" validator', () => {
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
});

it('has "application/vnd.apiary.http-headers+json" real type', () => {
assert.propertyVal(
result.headers,
'realType',
'application/vnd.apiary.http-headers+json'
);
});

it('has "application/vnd.apiary.http-headers+json" expected type', () => {
assert.propertyVal(
result.headers,
'expectedType',
'application/vnd.apiary.http-headers+json'
);
});

it('has no errors', () => {
assert.lengthOf(result.headers.results, 0);
});
});

describe('body', () => {
it('has "JsonExample" validator', () => {
assert.propertyVal(result.body, 'validator', 'JsonExample');
});

it('has "application/json" real type', () => {
assert.propertyVal(result.body, 'realType', 'application/json');
});

it('has "application/json" expected type', () => {
assert.propertyVal(result.body, 'expectedType', 'application/json');
});

it('has no errors', () => {
assert.lengthOf(result.body.results, 0);
});
});
});

describe('with non-matching responses', () => {
const realResponse = {
statusCode: 400,
headers: {
'Content-Type': 'application/json'
}
};
const expectedResponse = {
statusCode: 200,
headers: {
'Accept-Language': 'en-US'
}
};
const result = validateMessage(realResponse, expectedResponse);

it('returns validation result object', () => {
assert.isObject(result);
});

it('contains all validatable keys', () => {
assert.hasAllKeys(result, ['statusCode', 'headers']);
});

describe('statusCode', () => {
it('has "TextDiff" validator', () => {
assert.propertyVal(result.statusCode, 'validator', 'TextDiff');
});

it('has "text/vnd.apiary.status-code" real type', () => {
assert.propertyVal(
result.statusCode,
'realType',
'text/vnd.apiary.status-code'
);
});

it('has "text/vnd.apiary.status-code" expected type', () => {
assert.propertyVal(
result.statusCode,
'expectedType',
'text/vnd.apiary.status-code'
);
});

describe('produces an error', () => {
it('exactly one error', () => {
assert.lengthOf(result.statusCode.results, 1);
});

it('has "error" severity', () => {
assert.propertyVal(result.statusCode.results[0], 'severity', 'error');
});

it('has explanatory message', () => {
assert.propertyVal(
result.statusCode.results[0],
'message',
'Real and expected data does not match.'
);
});
});
});

describe('headers', () => {
it('has "HeadersJsonExample" validator', () => {
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
});

it('has "application/vnd.apiary.http-headers+json" real type', () => {
assert.propertyVal(
result.headers,
'realType',
'application/vnd.apiary.http-headers+json'
);
});

it('has "application/vnd.apiary.http-headers+json" real type', () => {
assert.propertyVal(
result.headers,
'realType',
'application/vnd.apiary.http-headers+json'
);
});

describe('produces an error', () => {
it('exactly one error', () => {
assert.lengthOf(result.headers.results, 1);
});

it('has "error" severity', () => {
assert.propertyVal(result.headers.results[0], 'severity', 'error');
});

it('includes missing header in the message', () => {
assert.propertyVal(
result.headers.results[0],
'message',
`At '/accept-language' Missing required property: accept-language`
);
});
});
});
});
});
Loading

0 comments on commit c03a0f9

Please sign in to comment.