Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 8630403

Browse files
test: adds granular unit tests for "validateBody"
1 parent 9c50d2f commit 8630403

10 files changed

+320
-8
lines changed

lib/api/test/unit/validateBody.test.js renamed to lib/api/test/unit/units/validateBody.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { assert } = require('chai');
2-
const validateBody = require('../../units/validateBody');
2+
const { validateBody } = require('../../../units/validateBody');
33

44
describe('validateBody', () => {
55
describe('when given unsupported body type', () => {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const { assert } = require('chai');
2+
const mediaTyper = require('media-typer');
3+
const { getBodySchemaType } = require('../../../../units/validateBody');
4+
5+
describe('getBodySchemaType', () => {
6+
describe('when given non-string schema', () => {
7+
// ...
8+
});
9+
10+
describe('when given json schema', () => {
11+
describe('that contains object', () => {
12+
const res = getBodySchemaType('{ "foo": "bar" }');
13+
14+
it('returns no errors', () => {
15+
assert.isNull(res[0]);
16+
});
17+
18+
it('returns "application/schema+json" media type', () => {
19+
assert.deepEqual(res[1], mediaTyper.parse('application/schema+json'));
20+
});
21+
});
22+
23+
describe('that contains array', () => {
24+
const res = getBodySchemaType('[1, 2, 3]');
25+
26+
it('returns no errors', () => {
27+
assert.isNull(res[0]);
28+
});
29+
30+
it('returns "application/schema+json" media type', () => {
31+
assert.deepEqual(res[1], mediaTyper.parse('application/schema+json'));
32+
});
33+
});
34+
});
35+
36+
describe('when given non-json schema', () => {
37+
const res = getBodySchemaType('foo');
38+
39+
it('returns parsing error', () => {
40+
assert.match(
41+
res[0],
42+
/^Can't validate. Expected body JSON Schema is not a parseable JSON:/
43+
);
44+
});
45+
46+
it('returns no media type', () => [assert.isNull(res[1])]);
47+
});
48+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const { assert } = require('chai');
2+
const mediaTyper = require('media-typer');
3+
const { getBodyType } = require('../../../../units/validateBody');
4+
5+
const jsonTypes = ['application/json', 'application/schema+json'];
6+
const nonJsonTypes = ['text/plain'];
7+
8+
describe('getBodyType', () => {
9+
describe('when given json-like content type', () => {
10+
jsonTypes.forEach((jsonType) => {
11+
describe(`when given "${jsonType}" content type`, () => {
12+
describe('and parsable json body', () => {
13+
const res = getBodyType('{ "foo": "bar" }', jsonType);
14+
15+
it('returns no errors', () => {
16+
assert.isNull(res[0]);
17+
});
18+
19+
it(`returns "${jsonType}" media type`, () => {
20+
assert.deepEqual(res[1], mediaTyper.parse(jsonType));
21+
});
22+
});
23+
24+
describe('and non-parsable json body', () => {
25+
const res = getBodyType('abc', jsonType);
26+
27+
it('returns parsing error', () => {
28+
assert.match(
29+
res[0],
30+
new RegExp(
31+
/* eslint-disable no-useless-escape */
32+
`^Real body 'Content-Type' header is \'${jsonType.replace(
33+
/(\/|\+)/g,
34+
'\\$1'
35+
)}\' but body is not a parseable JSON:`
36+
/* eslint-enable no-useless-escape */
37+
)
38+
);
39+
});
40+
41+
it('uses "text/plain" as fallback media type', () => {
42+
assert.deepEqual(res[1], mediaTyper.parse('text/plain'));
43+
});
44+
});
45+
});
46+
});
47+
});
48+
49+
describe('when given non-json content type', () => {
50+
nonJsonTypes.forEach((nonJsonType) => {
51+
describe(`when given "${nonJsonType}" content type`, () => {
52+
describe('and parsable json body', () => {
53+
const res = getBodyType('{ "foo": "bar" }', nonJsonType);
54+
55+
it('returns no errors', () => {
56+
assert.isNull(res[0]);
57+
});
58+
59+
it('coerces to "application/json" media type', () => {
60+
assert.deepEqual(res[1], mediaTyper.parse('application/json'));
61+
});
62+
});
63+
64+
describe('and a non-json body', () => {
65+
const res = getBodyType('abc', nonJsonType);
66+
67+
it('returns no errors', () => {
68+
assert.isNull(res[0]);
69+
});
70+
71+
it(`returns "${nonJsonType}" media type`, () => {
72+
assert.deepEqual(res[1], mediaTyper.parse(nonJsonType));
73+
});
74+
});
75+
});
76+
});
77+
});
78+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
const { assert } = require('chai');
2+
const mediaTyper = require('media-typer');
3+
const { getBodyValidator } = require('../../../../units/validateBody');
4+
5+
const getMediaTypes = (real, expected) => {
6+
return [real, expected].map(mediaTyper.parse);
7+
};
8+
9+
describe('getBodyValidator', () => {
10+
const knownContentTypes = [
11+
/* Known content types */
12+
{
13+
contentTypes: ['application/json', 'application/json'],
14+
expectedValidator: 'JsonExample'
15+
},
16+
{
17+
contentTypes: ['application/json', 'application/schema+json'],
18+
expectedValidator: 'JsonSchema'
19+
},
20+
{
21+
contentTypes: ['text/plain', 'text/plain'],
22+
expectedValidator: 'TextDiff'
23+
}
24+
];
25+
26+
const unknownContentTypes = [
27+
{
28+
contentTypes: ['application/xml', 'application/xml']
29+
},
30+
{
31+
contentTypes: ['text/html', 'application/xml']
32+
}
33+
];
34+
35+
describe('when given known media type', () => {
36+
knownContentTypes.forEach(({ contentTypes, expectedValidator }) => {
37+
const [realContentType, expectedContentType] = contentTypes;
38+
const [real, expected] = getMediaTypes(
39+
realContentType,
40+
expectedContentType
41+
);
42+
43+
describe(`${realContentType} + ${expectedContentType}`, () => {
44+
const [error, validator] = getBodyValidator(real, expected);
45+
46+
it('returns no error', () => {
47+
assert.isNull(error);
48+
});
49+
50+
it(`returns "${expectedValidator}" validator`, () => {
51+
assert.equal(validator.name, expectedValidator);
52+
});
53+
});
54+
});
55+
});
56+
57+
describe('when given unknown media type', () => {
58+
unknownContentTypes.forEach(({ contentTypes }) => {
59+
const [realContentType, expectedContentType] = contentTypes;
60+
const [real, expected] = getMediaTypes(
61+
realContentType,
62+
expectedContentType
63+
);
64+
65+
describe(`${realContentType} + ${expectedContentType}`, () => {
66+
const [error, validator] = getBodyValidator(real, expected);
67+
68+
it('has explanatory error', () => {
69+
assert.equal(
70+
error,
71+
`Can't validate real media type '${realContentType}' against expected media type '${expectedContentType}'.`
72+
);
73+
});
74+
75+
it('returns null validator', () => {
76+
assert.isNull(validator);
77+
});
78+
});
79+
});
80+
});
81+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const { assert } = require('chai');
2+
const mediaTyper = require('media-typer');
3+
const { isJson } = require('../../../../units/validateBody');
4+
5+
describe('isJson', () => {
6+
describe('returns true', () => {
7+
it('when given valid json media type', () => {
8+
assert.isTrue(isJson(mediaTyper.parse('application/json')));
9+
});
10+
11+
it('when given json-compatible media type', () => {
12+
assert.isTrue(isJson(mediaTyper.parse('application/schema+json')));
13+
});
14+
});
15+
16+
describe('returns false', () => {
17+
it('when given non-json media type', () => {
18+
assert.isFalse(isJson(mediaTyper.parse('text/plain')));
19+
});
20+
21+
it('when given rubbish', () => {
22+
assert.isFalse(isJson('abc'));
23+
});
24+
25+
it('when given null', () => {
26+
assert.isFalse(isJson(null));
27+
});
28+
});
29+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const { assert } = require('chai');
2+
const { isJsonContentType } = require('../../../../units/validateBody');
3+
4+
describe('isJsonContentType', () => {
5+
describe('returns true', () => {
6+
const jsonTypes = ['application/json', 'application/schema+json'];
7+
8+
jsonTypes.forEach((contentType) => {
9+
it(`when given ${contentType}`, () => {
10+
assert.isTrue(isJsonContentType(contentType));
11+
});
12+
});
13+
});
14+
15+
describe('returns false', () => {
16+
const nonJsonTypes = ['application/xml', 'text/plain'];
17+
18+
nonJsonTypes.forEach((contentType) => {
19+
it(`when given ${contentType}`, () => {
20+
assert.isFalse(isJsonContentType(contentType));
21+
});
22+
});
23+
24+
it('when given rubbish', () => {
25+
assert.isFalse(isJsonContentType('foo'));
26+
});
27+
});
28+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const { assert } = require('chai');
2+
const mediaTyper = require('media-typer');
3+
const { isJsonSchema } = require('../../../../units/validateBody');
4+
5+
describe('isJsonSchema', () => {
6+
describe('returns true', () => {
7+
it('when given a json schema media type', () => {
8+
assert.isTrue(isJsonSchema(mediaTyper.parse('application/schema+json')));
9+
});
10+
});
11+
12+
describe('returns false', () => {
13+
it('when given json media type', () => {
14+
assert.isFalse(isJsonSchema(mediaTyper.parse('application/json')));
15+
});
16+
17+
it('when given rubbish', () => {
18+
assert.isFalse(isJsonSchema('abc'));
19+
});
20+
21+
it('when given null', () => {
22+
assert.isFalse(isJsonSchema(null));
23+
});
24+
});
25+
});

lib/api/test/unit/validateHeaders.test.js renamed to lib/api/test/unit/units/validateHeaders.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { assert } = require('chai');
2-
const validateHeaders = require('../../units/validateHeaders');
2+
const validateHeaders = require('../../../units/validateHeaders');
33

44
describe('validateHeaders', () => {
55
describe('given matching headers', () => {

lib/api/test/unit/validateStatusCode.test.js renamed to lib/api/test/unit/units/validateStatusCode.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { assert } = require('chai');
2-
const validateStatusCode = require('../../units/validateStatusCode');
2+
const validateStatusCode = require('../../../units/validateStatusCode');
33

44
describe('validateStatusCode', () => {
55
describe('given matching status codes', () => {

lib/api/units/validateBody.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@ function isPlainText(mediaType) {
1111
}
1212

1313
function isJson(mediaType) {
14+
if (!mediaType) {
15+
return false;
16+
}
17+
1418
return (
1519
(mediaType.type === 'application' && mediaType.subtype === 'json') ||
1620
mediaType.suffix === 'json'
1721
);
1822
}
1923

2024
function isJsonSchema(mediaType) {
25+
if (!mediaType) {
26+
return false;
27+
}
28+
2129
return (
2230
mediaType.type === 'application' &&
2331
mediaType.subtype === 'schema' &&
@@ -105,10 +113,8 @@ function getBodySchemaType(bodySchema) {
105113
}
106114

107115
try {
108-
const parsed = jph.parse(bodySchema);
109-
if (typeof parsed === 'object') {
110-
return [null, jsonSchemaType];
111-
}
116+
jph.parse(bodySchema);
117+
return [null, jsonSchemaType];
112118
} catch (exception) {
113119
const error = `Can't validate. Expected body JSON Schema is not a parseable JSON:\n${
114120
exception.message
@@ -151,6 +157,13 @@ function getBodyValidator(realType, expectedType) {
151157
const error = `Can't validate real media type '${mediaTyper.format(
152158
realType
153159
)}' against expected media type '${mediaTyper.format(expectedType)}'.`;
160+
161+
/**
162+
* Shouldn't we fallback to "TextDiff" validator on unknown content types?
163+
* Unless content type is invalid, it can be at least text diff'ed.
164+
* Considering that the input of this function is an already parsed content type,
165+
* it's impossible for it to be invalid.
166+
*/
154167
return [error, null];
155168
}
156169

@@ -232,4 +245,14 @@ function validateBody(real, expected) {
232245
};
233246
}
234247

235-
module.exports = validateBody;
248+
module.exports = {
249+
validateBody,
250+
251+
isJson,
252+
isJsonSchema,
253+
isJsonContentType,
254+
parseContentType,
255+
getBodyType,
256+
getBodySchemaType,
257+
getBodyValidator
258+
};

0 commit comments

Comments
 (0)