diff --git a/lib/api/test/unit/validateHeaders.test.js b/lib/api/test/unit/validateHeaders.test.js new file mode 100644 index 00000000..8db18509 --- /dev/null +++ b/lib/api/test/unit/validateHeaders.test.js @@ -0,0 +1,125 @@ +const { assert } = require('chai'); +const validateHeaders = require('../../units/validateHeaders'); + +describe('validateHeaders', () => { + describe('given matching headers', () => { + const res = validateHeaders( + { + 'content-type': 'application/json', + connection: 'keep-alive' + }, + { + 'content-type': 'application/json', + connection: 'keep-alive' + } + ); + + it('has proper validator', () => { + assert.propertyVal(res, 'validator', 'HeadersJsonExample'); + }); + + it('has proper real type', () => { + assert.propertyVal( + res, + 'realType', + 'application/vnd.apiary.http-headers+json' + ); + }); + + it('has proper expected type', () => { + assert.propertyVal( + res, + 'expectedType', + 'application/vnd.apiary.http-headers+json' + ); + }); + + it('has no errors', () => { + assert.deepPropertyVal(res, 'results', []); + }); + }); + + describe('given non-matching headers', () => { + const res = validateHeaders( + { + connection: 'keep-alive' + }, + { + 'content-type': 'application/json', + connection: 'keep-alive' + } + ); + + it('has proper validator', () => { + assert.propertyVal(res, 'validator', 'HeadersJsonExample'); + }); + + it('has proper real type', () => { + assert.propertyVal( + res, + 'realType', + 'application/vnd.apiary.http-headers+json' + ); + }); + + it('has proper expected type', () => { + assert.propertyVal( + res, + 'expectedType', + 'application/vnd.apiary.http-headers+json' + ); + }); + + describe('has error', () => { + it('for each missing header', () => { + assert.lengthOf(res.results, 1); + }); + + it('contains pointer to missing header(s)', () => { + assert.propertyVal(res.results[0], 'pointer', '/content-type'); + }); + + it('contains error message', () => { + assert.propertyVal( + res.results[0], + 'message', + `At '/content-type' Missing required property: content-type` + ); + }); + + it('contains proper severity', () => { + assert.propertyVal(res.results[0], 'severity', 'error'); + }); + }); + }); + + describe('given non-json headers', () => { + const res = validateHeaders('foo', 'bar'); + + it('has no validator', () => { + assert.propertyVal(res, 'validator', null); + }); + + it('has no real type', () => { + assert.propertyVal(res, 'realType', null); + }); + + it('has no expected type', () => { + assert.propertyVal(res, 'expectedType', null); + }); + + it('has invalid format error', () => { + assert.lengthOf(res.results, 1); + assert.propertyVal(res.results[0], 'severity', 'error'); + assert.propertyVal( + res.results[0], + 'message', + ` +No validator found for real data media type +"null" +and expected data media type +"null".` + ); + }); + }); +}); diff --git a/lib/api/units/validateHeaders.js b/lib/api/units/validateHeaders.js new file mode 100644 index 00000000..a1d31141 --- /dev/null +++ b/lib/api/units/validateHeaders.js @@ -0,0 +1,67 @@ +const { HeadersJsonExample } = require('../../validators/headers-json-example'); + +const APIARY_JSON_HEADER_TYPE = 'application/vnd.apiary.http-headers+json'; + +function normalizeHeaders(headers) { + return ( + headers instanceof Object && + Object.keys(headers).reduce( + (acc, headerName) => + Object.assign({}, acc, { + [headerName.toLowerCase()]: headers[headerName] + }), + {} + ) + ); +} + +function getHeadersType(headers) { + return headers instanceof Object && !Array.isArray(headers) + ? APIARY_JSON_HEADER_TYPE + : null; +} + +/** + * Validates given real and expected headers. + * @param {Object} realHeaders + * @param {Object} expectedHeaders + */ +function validateHeaders(realHeaders, expectedHeaders) { + const real = normalizeHeaders(realHeaders); + const expected = normalizeHeaders(expectedHeaders); + const realType = getHeadersType(real); + const expectedType = getHeadersType(expected); + const results = []; + + const hasJsonHeaders = + realType === APIARY_JSON_HEADER_TYPE && + expectedType === APIARY_JSON_HEADER_TYPE; + + const validator = hasJsonHeaders + ? new HeadersJsonExample(real, expected) + : null; + const rawData = validator && validator.validate(); + + if (validator) { + results.push(...validator.evaluateOutputToResults()); + } else { + results.push({ + message: ` +No validator found for real data media type +"${JSON.stringify(realType)}" +and expected data media type +"${JSON.stringify(expectedType)}".`, + severity: 'error' + }); + } + + return { + validator: validator && 'HeadersJsonExample', + realType, + expectedType, + rawData, + results + }; +} + +module.exports = validateHeaders;