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

Commit c03a0f9

Browse files
Merge pull request #155 from apiaryio/150-refactor
Modularize validation
2 parents 577f9b5 + 20ec986 commit c03a0f9

29 files changed

+1884
-17
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = {
66
node: true
77
},
88
rules: {
9+
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
910
'consistent-return': 'off',
1011
'class-methods-use-this': 'off',
1112
'no-plusplus': 'off',

lib/gavel.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
const { HttpRequest, ExpectedHttpRequest } = require('./model/http-request');
22
const { HttpResponse, ExpectedHttpResponse } = require('./model/http-response');
3-
4-
const { validate, isValid, isValidatable } = require('./validate');
3+
const { isValid, isValidatable } = require('./validate');
4+
const validate = require('./next/validate');
55

66
module.exports = {
7+
// next
8+
validate,
9+
10+
// legacy
711
HttpRequest,
812
HttpResponse,
913
ExpectedHttpRequest,
1014
ExpectedHttpResponse,
11-
validate,
1215
isValid,
1316
isValidatable
1417
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
const { assert } = require('chai');
2+
const { validateMessage } = require('../../validateMessage');
3+
4+
describe('validateMessage', () => {
5+
describe('with matching requests', () => {
6+
const request = {
7+
method: 'POST',
8+
headers: {
9+
'Content-Type': 'application/json'
10+
},
11+
body: '{ "foo": "bar" }'
12+
};
13+
const result = validateMessage(request, request);
14+
15+
it('returns validation result object', () => {
16+
assert.isObject(result);
17+
});
18+
19+
it('contains all validatable keys', () => {
20+
assert.hasAllKeys(result, ['headers', 'body']);
21+
});
22+
23+
describe('headers', () => {
24+
it('has "HeadersJsonExample" validator', () => {
25+
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
26+
});
27+
28+
it('has "application/vnd.apiary.http-headers+json" real headers type', () => {
29+
assert.propertyVal(
30+
result.headers,
31+
'realType',
32+
'application/vnd.apiary.http-headers+json'
33+
);
34+
});
35+
36+
it('has "application/vnd.apiary.http-headers+json" expected headers type', () => {
37+
assert.propertyVal(
38+
result.headers,
39+
'expectedType',
40+
'application/vnd.apiary.http-headers+json'
41+
);
42+
});
43+
44+
it('has no errors', () => {
45+
assert.lengthOf(result.headers.results, 0);
46+
});
47+
});
48+
49+
describe('body', () => {
50+
it('has "JsonExample" validator', () => {
51+
assert.propertyVal(result.body, 'validator', 'JsonExample');
52+
});
53+
54+
it('has "application/json" real body type', () => {
55+
assert.propertyVal(result.body, 'realType', 'application/json');
56+
});
57+
58+
it('has "application/json" expected body type', () => {
59+
assert.propertyVal(result.body, 'expectedType', 'application/json');
60+
});
61+
62+
it('has no errors', () => {
63+
assert.lengthOf(result.body.results, 0);
64+
});
65+
});
66+
});
67+
68+
describe('with non-matching requests', () => {
69+
const result = validateMessage(
70+
{
71+
method: 'POST',
72+
headers: {
73+
'content-type': 'application/json'
74+
},
75+
body: '{ "lookHere": "foo" }'
76+
},
77+
{
78+
method: 'PUT',
79+
headers: '',
80+
body: '2'
81+
}
82+
);
83+
84+
it('returns validation result object', () => {
85+
assert.isObject(result);
86+
});
87+
88+
it('contains all validatable keys', () => {
89+
assert.hasAllKeys(result, ['headers', 'body']);
90+
});
91+
92+
describe('method', () => {
93+
// See https://github.com/apiaryio/gavel.js/issues/158
94+
it.skip('compares methods');
95+
});
96+
97+
describe('headers', () => {
98+
it('has "HeadersJsonExample" validator', () => {
99+
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
100+
});
101+
102+
it('has "application/vnd.apiary.http-headers+json" as real type', () => {
103+
assert.propertyVal(
104+
result.headers,
105+
'realType',
106+
'application/vnd.apiary.http-headers+json'
107+
);
108+
});
109+
110+
it('has "application/vnd.apiary.http-headers+json" expected type', () => {
111+
assert.propertyVal(
112+
result.headers,
113+
'expectedType',
114+
'application/vnd.apiary.http-headers+json'
115+
);
116+
});
117+
118+
it('has no errors', () => {
119+
assert.lengthOf(result.headers.results, 0);
120+
});
121+
});
122+
123+
describe('body', () => {
124+
it('has "JsonExample" validator', () => {
125+
assert.propertyVal(result.body, 'validator', 'JsonExample');
126+
});
127+
128+
it('has "application/json" real type', () => {
129+
assert.propertyVal(result.body, 'realType', 'application/json');
130+
});
131+
132+
it('has "application/json" expected type', () => {
133+
assert.propertyVal(result.body, 'expectedType', 'application/json');
134+
});
135+
136+
describe('produces an error', () => {
137+
it('exactly one error', () => {
138+
assert.lengthOf(result.body.results, 1);
139+
});
140+
141+
it('has "error" severity', () => {
142+
assert.propertyVal(result.body.results[0], 'severity', 'error');
143+
});
144+
145+
it('has explanatory message', () => {
146+
assert.propertyVal(
147+
result.body.results[0],
148+
'message',
149+
`At '' Invalid type: object (expected integer)`
150+
);
151+
});
152+
});
153+
});
154+
});
155+
156+
describe('with matching responses', () => {
157+
const response = {
158+
statusCode: 200,
159+
headers: {
160+
'Content-Type': 'application/json'
161+
},
162+
body: '{ "foo": "bar" }'
163+
};
164+
const result = validateMessage(response, response);
165+
166+
it('returns validation result object', () => {
167+
assert.isObject(result);
168+
});
169+
170+
it('contains all validatable keys', () => {
171+
assert.hasAllKeys(result, ['statusCode', 'headers', 'body']);
172+
});
173+
174+
describe('statusCode', () => {
175+
it('has "TextDiff" validator', () => {
176+
assert.propertyVal(result.statusCode, 'validator', 'TextDiff');
177+
});
178+
179+
it('has "text/vnd.apiary.status-code" real type', () => {
180+
assert.propertyVal(
181+
result.statusCode,
182+
'realType',
183+
'text/vnd.apiary.status-code'
184+
);
185+
});
186+
187+
it('has "text/vnd.apiary.status-code" expected type', () => {
188+
assert.propertyVal(
189+
result.statusCode,
190+
'expectedType',
191+
'text/vnd.apiary.status-code'
192+
);
193+
});
194+
195+
it('has no errors', () => {
196+
assert.lengthOf(result.statusCode.results, 0);
197+
});
198+
});
199+
200+
describe('headers', () => {
201+
it('has "HeadersJsonExample" validator', () => {
202+
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
203+
});
204+
205+
it('has "application/vnd.apiary.http-headers+json" real type', () => {
206+
assert.propertyVal(
207+
result.headers,
208+
'realType',
209+
'application/vnd.apiary.http-headers+json'
210+
);
211+
});
212+
213+
it('has "application/vnd.apiary.http-headers+json" expected type', () => {
214+
assert.propertyVal(
215+
result.headers,
216+
'expectedType',
217+
'application/vnd.apiary.http-headers+json'
218+
);
219+
});
220+
221+
it('has no errors', () => {
222+
assert.lengthOf(result.headers.results, 0);
223+
});
224+
});
225+
226+
describe('body', () => {
227+
it('has "JsonExample" validator', () => {
228+
assert.propertyVal(result.body, 'validator', 'JsonExample');
229+
});
230+
231+
it('has "application/json" real type', () => {
232+
assert.propertyVal(result.body, 'realType', 'application/json');
233+
});
234+
235+
it('has "application/json" expected type', () => {
236+
assert.propertyVal(result.body, 'expectedType', 'application/json');
237+
});
238+
239+
it('has no errors', () => {
240+
assert.lengthOf(result.body.results, 0);
241+
});
242+
});
243+
});
244+
245+
describe('with non-matching responses', () => {
246+
const realResponse = {
247+
statusCode: 400,
248+
headers: {
249+
'Content-Type': 'application/json'
250+
}
251+
};
252+
const expectedResponse = {
253+
statusCode: 200,
254+
headers: {
255+
'Accept-Language': 'en-US'
256+
}
257+
};
258+
const result = validateMessage(realResponse, expectedResponse);
259+
260+
it('returns validation result object', () => {
261+
assert.isObject(result);
262+
});
263+
264+
it('contains all validatable keys', () => {
265+
assert.hasAllKeys(result, ['statusCode', 'headers']);
266+
});
267+
268+
describe('statusCode', () => {
269+
it('has "TextDiff" validator', () => {
270+
assert.propertyVal(result.statusCode, 'validator', 'TextDiff');
271+
});
272+
273+
it('has "text/vnd.apiary.status-code" real type', () => {
274+
assert.propertyVal(
275+
result.statusCode,
276+
'realType',
277+
'text/vnd.apiary.status-code'
278+
);
279+
});
280+
281+
it('has "text/vnd.apiary.status-code" expected type', () => {
282+
assert.propertyVal(
283+
result.statusCode,
284+
'expectedType',
285+
'text/vnd.apiary.status-code'
286+
);
287+
});
288+
289+
describe('produces an error', () => {
290+
it('exactly one error', () => {
291+
assert.lengthOf(result.statusCode.results, 1);
292+
});
293+
294+
it('has "error" severity', () => {
295+
assert.propertyVal(result.statusCode.results[0], 'severity', 'error');
296+
});
297+
298+
it('has explanatory message', () => {
299+
assert.propertyVal(
300+
result.statusCode.results[0],
301+
'message',
302+
'Real and expected data does not match.'
303+
);
304+
});
305+
});
306+
});
307+
308+
describe('headers', () => {
309+
it('has "HeadersJsonExample" validator', () => {
310+
assert.propertyVal(result.headers, 'validator', 'HeadersJsonExample');
311+
});
312+
313+
it('has "application/vnd.apiary.http-headers+json" real type', () => {
314+
assert.propertyVal(
315+
result.headers,
316+
'realType',
317+
'application/vnd.apiary.http-headers+json'
318+
);
319+
});
320+
321+
it('has "application/vnd.apiary.http-headers+json" real type', () => {
322+
assert.propertyVal(
323+
result.headers,
324+
'realType',
325+
'application/vnd.apiary.http-headers+json'
326+
);
327+
});
328+
329+
describe('produces an error', () => {
330+
it('exactly one error', () => {
331+
assert.lengthOf(result.headers.results, 1);
332+
});
333+
334+
it('has "error" severity', () => {
335+
assert.propertyVal(result.headers.results[0], 'severity', 'error');
336+
});
337+
338+
it('includes missing header in the message', () => {
339+
assert.propertyVal(
340+
result.headers.results[0],
341+
'message',
342+
`At '/accept-language' Missing required property: accept-language`
343+
);
344+
});
345+
});
346+
});
347+
});
348+
});

0 commit comments

Comments
 (0)