@vida-global/core 1.3.3 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/logger/index.js +4 -1
- package/lib/server/README.md +30 -8
- package/lib/server/errors.js +2 -0
- package/lib/server/serverController.js +113 -34
- package/package.json +2 -2
- package/test/server/serverController.test.js +66 -72
package/lib/logger/index.js
CHANGED
package/lib/server/README.md
CHANGED
|
@@ -118,9 +118,34 @@ Additional options can be passed to `toApiResponse` by explicitly calling `rende
|
|
|
118
118
|
await this.render(response, {includeTitle: true});
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
### Other Status Codes
|
|
122
|
+
There is no need to set `statusCode` directly as there are helper methods for all statuses.
|
|
123
|
+
```
|
|
124
|
+
201 - this.renderCreationSuccessful
|
|
125
|
+
202 - this.renderAccepted
|
|
126
|
+
204 - this.renderNoConent
|
|
127
|
+
301 - this.renderMovedPermanently
|
|
128
|
+
302 - this.renderFound
|
|
129
|
+
304 - this.renderNotModified
|
|
130
|
+
307 - this.renderTemporaryRedirect
|
|
131
|
+
308 - this.renderPermanentRedirect
|
|
132
|
+
401 - this.renderUnauthorizedResponse
|
|
133
|
+
402 - this.renderPaymentRequired
|
|
134
|
+
403 - this.renderForbiddenResponse
|
|
135
|
+
404 - this.renderNotFoundResponse
|
|
136
|
+
405 - this.renderMethodNotAllowed
|
|
137
|
+
408 - this.renderRequestTimeout
|
|
138
|
+
409 - this.renderConflictResponse
|
|
139
|
+
410 - this.renderGone
|
|
140
|
+
413 - this.renderContentTooLarge
|
|
141
|
+
415 - this.renderUnsupportedMediaType
|
|
142
|
+
422 - this.renderUnprocessableContent
|
|
143
|
+
423 - this.renderLocked
|
|
144
|
+
429 - this.renderTooManyRequests
|
|
145
|
+
```
|
|
146
|
+
|
|
121
147
|
|
|
122
148
|
## Error handling
|
|
123
|
-
There is no need to set `statusCode` directly as there are helper methods for all statuses.
|
|
124
149
|
HTTP 400
|
|
125
150
|
```
|
|
126
151
|
await this.renderErrors("Something bad happened", {password: ['must be > 8 characters', 'must include numbers']})
|
|
@@ -134,19 +159,16 @@ await this.renderErrors({password: ['must be > 8 characters', 'must include numb
|
|
|
134
159
|
```
|
|
135
160
|
HTTP 401
|
|
136
161
|
```
|
|
137
|
-
await this.
|
|
162
|
+
await this.renderUnauthorized("Nope")
|
|
138
163
|
// renders {data: {message: "Nope"}, status: "unauthorized"}
|
|
139
164
|
```
|
|
140
165
|
HTTP 403
|
|
141
166
|
```
|
|
142
|
-
await this.
|
|
167
|
+
await this.renderForbidden("You shall not pass")
|
|
143
168
|
// renders {data: {message: "You shall not pass"}, status: "forbidden"}
|
|
144
169
|
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await this.renderNotFoundResponse("Whoops")
|
|
148
|
-
// renders {data: {message: "Whoops"}, status: "not found"}
|
|
149
|
-
```
|
|
170
|
+
* _Other statuses detailed above._
|
|
171
|
+
|
|
150
172
|
Throwing any of `this.Errors.Authorization(msg)`, `this.Errors.Forbidden(msg)`, `this.Errors.NotFound(msg)`, `this.Errors.Validation(msg, errors)` from anywhere in the code will trigger the corresponding error handler. All other errors will generate a 500 error.
|
|
151
173
|
|
|
152
174
|
|
package/lib/server/errors.js
CHANGED
|
@@ -7,6 +7,7 @@ class ServerError extends Error {
|
|
|
7
7
|
get message() { return this.#message; };
|
|
8
8
|
}
|
|
9
9
|
class AuthorizationError extends ServerError {}
|
|
10
|
+
class ConflictError extends ServerError {}
|
|
10
11
|
class ForbiddenError extends ServerError {}
|
|
11
12
|
class NotFoundError extends ServerError {}
|
|
12
13
|
class ValidationError extends ServerError {
|
|
@@ -21,6 +22,7 @@ class ValidationError extends ServerError {
|
|
|
21
22
|
|
|
22
23
|
module.exports = {
|
|
23
24
|
AuthorizationError,
|
|
25
|
+
ConflictError,
|
|
24
26
|
ForbiddenError,
|
|
25
27
|
NotFoundError,
|
|
26
28
|
ServerError,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { logger } = require('../logger');
|
|
2
2
|
const { camelize, singularize } = require('inflection');
|
|
3
3
|
const{ AuthorizationError,
|
|
4
|
+
ConflictError,
|
|
4
5
|
ForbiddenError,
|
|
5
6
|
NotFoundError,
|
|
6
7
|
ServerError,
|
|
@@ -28,6 +29,7 @@ class VidaServerController {
|
|
|
28
29
|
this.#response = response;
|
|
29
30
|
|
|
30
31
|
this.#applyCallbacks();
|
|
32
|
+
this.#setupStatusRenderers();
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
|
|
@@ -121,13 +123,16 @@ class VidaServerController {
|
|
|
121
123
|
|
|
122
124
|
async #handleError(err) {
|
|
123
125
|
if (err instanceof AuthorizationError) {
|
|
124
|
-
await this.
|
|
126
|
+
await this.renderUnauthorized(err.message);
|
|
127
|
+
|
|
128
|
+
} else if (err instanceof ConflictError) {
|
|
129
|
+
await this.renderConflict(err.message);
|
|
125
130
|
|
|
126
131
|
} else if (err instanceof ForbiddenError) {
|
|
127
|
-
await this.
|
|
132
|
+
await this.renderForbidden(err.message);
|
|
128
133
|
|
|
129
134
|
} else if (err instanceof NotFoundError) {
|
|
130
|
-
await this.
|
|
135
|
+
await this.renderNotFound(err.message);
|
|
131
136
|
|
|
132
137
|
} else if (err instanceof ValidationError) {
|
|
133
138
|
await this.renderErrors(err.message, err.fields);
|
|
@@ -140,7 +145,7 @@ class VidaServerController {
|
|
|
140
145
|
} else {
|
|
141
146
|
message = err.message;
|
|
142
147
|
}
|
|
143
|
-
await this.
|
|
148
|
+
await this.renderServerError(message);
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -186,33 +191,14 @@ class VidaServerController {
|
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
|
|
189
|
-
async
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
async renderForbiddenResponse(message=null) {
|
|
195
|
-
return await this.#renderError(403, message);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
async renderNotFoundResponse(message=null) {
|
|
200
|
-
return await this.#renderError(404, message);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
async renderServerErrorResponse(message=null) {
|
|
194
|
+
async renderServerError(message=null) {
|
|
195
|
+
if (process.env.NODE_ENV != 'development') {
|
|
196
|
+
message = null;
|
|
197
|
+
}
|
|
205
198
|
return await this.#renderError(500, message);
|
|
206
199
|
}
|
|
207
200
|
|
|
208
201
|
|
|
209
|
-
async #renderError(statusCode, message) {
|
|
210
|
-
this.statusCode = statusCode;
|
|
211
|
-
const body = {errors: {message: message || null}};
|
|
212
|
-
return await this.render(body);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
202
|
formatJSONBody(body, errors, options) {
|
|
217
203
|
if (options.standardize === false) return body;
|
|
218
204
|
|
|
@@ -253,26 +239,118 @@ class VidaServerController {
|
|
|
253
239
|
switch(this.statusCode) {
|
|
254
240
|
case 200:
|
|
255
241
|
return 'ok';
|
|
242
|
+
case 201:
|
|
243
|
+
return 'created';
|
|
244
|
+
case 202:
|
|
245
|
+
return 'accepted';
|
|
246
|
+
case 204:
|
|
247
|
+
return 'no content';
|
|
248
|
+
case 301:
|
|
249
|
+
return 'moved permanently';
|
|
250
|
+
case 302:
|
|
251
|
+
return 'found';
|
|
252
|
+
case 304:
|
|
253
|
+
return 'not modified';
|
|
254
|
+
case 307:
|
|
255
|
+
return 'temporary redirect';
|
|
256
|
+
case 308:
|
|
257
|
+
return 'permanent redirect';
|
|
256
258
|
case 400:
|
|
257
259
|
return 'bad request'
|
|
258
260
|
case 401:
|
|
259
261
|
return 'unauthorized';
|
|
262
|
+
case 402:
|
|
263
|
+
return 'payment required';
|
|
260
264
|
case 403:
|
|
261
265
|
return 'forbidden';
|
|
262
266
|
case 404:
|
|
263
267
|
return 'not found';
|
|
268
|
+
case 405:
|
|
269
|
+
return 'method not allowed';
|
|
270
|
+
case 408:
|
|
271
|
+
return 'request timeout';
|
|
272
|
+
case 409:
|
|
273
|
+
return 'conflict';
|
|
274
|
+
case 410:
|
|
275
|
+
return 'gone';
|
|
276
|
+
case 413:
|
|
277
|
+
return 'content too large';
|
|
278
|
+
case 415:
|
|
279
|
+
return 'unsupported media type';
|
|
280
|
+
case 422:
|
|
281
|
+
return 'unprocessable content';
|
|
282
|
+
case 423:
|
|
283
|
+
return 'locked';
|
|
284
|
+
case 429:
|
|
285
|
+
return 'too many requests';
|
|
264
286
|
case 500:
|
|
265
287
|
return 'server error';
|
|
266
288
|
}
|
|
267
289
|
}
|
|
268
290
|
|
|
269
291
|
|
|
292
|
+
#setupStatusRenderers() {
|
|
293
|
+
const errorStatuses = {
|
|
294
|
+
Unauthorized: 401,
|
|
295
|
+
PaymentRequired: 402,
|
|
296
|
+
Forbidden: 403,
|
|
297
|
+
NotFound: 404,
|
|
298
|
+
MethodNotAllowed: 405,
|
|
299
|
+
RequestTimeout: 408,
|
|
300
|
+
Conflict: 409,
|
|
301
|
+
Gone: 410,
|
|
302
|
+
ContentTooLarge: 413,
|
|
303
|
+
UnsupportedMediaType: 415,
|
|
304
|
+
UnprocessableContent: 422,
|
|
305
|
+
Locked: 423,
|
|
306
|
+
TooManyRequests: 429,
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const successStatuses = {
|
|
310
|
+
CreationSuccessful: 201,
|
|
311
|
+
Accepted: 202,
|
|
312
|
+
NoConent: 204,
|
|
313
|
+
MovedPermanently: 301,
|
|
314
|
+
Found: 302,
|
|
315
|
+
NotModified: 304,
|
|
316
|
+
TemporaryRedirect: 307,
|
|
317
|
+
PermanentRedirect: 308,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
for (const [method, statusCode] of Object.entries(errorStatuses)) {
|
|
321
|
+
this[`render${method}`] = async function(message=null) {
|
|
322
|
+
return await this.#renderError(statusCode, message);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
for (const [method, statusCode] of Object.entries(successStatuses)) {
|
|
327
|
+
this[`render${method}`] = async function(message=null) {
|
|
328
|
+
return await this.#renderSuccess(statusCode, message);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
async #renderError(statusCode, message) {
|
|
335
|
+
this.statusCode = statusCode;
|
|
336
|
+
const body = {errors: {message: message || null}};
|
|
337
|
+
return await this.render(body);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
async #renderSuccess(statusCode, body) {
|
|
342
|
+
this.statusCode = statusCode;
|
|
343
|
+
return await this.render(body);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
270
347
|
/***********************************************************************************************
|
|
271
348
|
* HELPERS
|
|
272
349
|
***********************************************************************************************/
|
|
273
350
|
static autoLoadHelpers() {
|
|
274
|
-
const { InstanceMethods, Accessors } = this._autoLoadModule(this.autoLoadHelperPath);
|
|
351
|
+
const { StaticMethods, InstanceMethods, Accessors } = this._autoLoadModule(this.autoLoadHelperPath);
|
|
275
352
|
|
|
353
|
+
if (StaticMethods) Object.assign(this, StaticMethods);
|
|
276
354
|
if (InstanceMethods) Object.assign(this.prototype, InstanceMethods);
|
|
277
355
|
|
|
278
356
|
if (Accessors) {
|
|
@@ -385,10 +463,10 @@ class VidaServerController {
|
|
|
385
463
|
/***********************************************************************************************
|
|
386
464
|
* VALIDATIONS
|
|
387
465
|
***********************************************************************************************/
|
|
388
|
-
validateParameters(validations) {
|
|
466
|
+
async validateParameters(validations) {
|
|
389
467
|
const errors = {};
|
|
390
468
|
for (const [parameterName, parameterValidations] of Object.entries(validations)) {
|
|
391
|
-
const parameterErrors = this.validateParameter(this.params[parameterName], parameterValidations);
|
|
469
|
+
const parameterErrors = await this.validateParameter(this.params[parameterName], parameterValidations);
|
|
392
470
|
if (parameterErrors.length) errors[parameterName] = parameterErrors;
|
|
393
471
|
}
|
|
394
472
|
|
|
@@ -396,7 +474,7 @@ class VidaServerController {
|
|
|
396
474
|
}
|
|
397
475
|
|
|
398
476
|
|
|
399
|
-
validateParameter(value, parameterValidations) {
|
|
477
|
+
async validateParameter(value, parameterValidations) {
|
|
400
478
|
const errors = [];
|
|
401
479
|
|
|
402
480
|
if (parameterValidations?.optional && value === undefined) return errors;
|
|
@@ -404,7 +482,7 @@ class VidaServerController {
|
|
|
404
482
|
for (const [validationType, validationOptions] of Object.entries(parameterValidations)) {
|
|
405
483
|
const validationMethod = `validate${camelize(validationType)}`;
|
|
406
484
|
if (!this[validationMethod]) continue;
|
|
407
|
-
const error = this[validationMethod](value, validationOptions);
|
|
485
|
+
const error = await this[validationMethod](value, validationOptions);
|
|
408
486
|
if (error) errors.push(error);
|
|
409
487
|
}
|
|
410
488
|
|
|
@@ -457,8 +535,8 @@ class VidaServerController {
|
|
|
457
535
|
}
|
|
458
536
|
|
|
459
537
|
|
|
460
|
-
validateFunction(value, fnc) {
|
|
461
|
-
const error = fnc(value);
|
|
538
|
+
async validateFunction(value, fnc) {
|
|
539
|
+
const error = await fnc.call(this, value);
|
|
462
540
|
if (error) return error;
|
|
463
541
|
}
|
|
464
542
|
|
|
@@ -601,6 +679,7 @@ class VidaServerController {
|
|
|
601
679
|
|
|
602
680
|
VidaServerController.prototype.Errors = {
|
|
603
681
|
Authorization: AuthorizationError,
|
|
682
|
+
Conflict: ConflictError,
|
|
604
683
|
Forbidden: ForbiddenError,
|
|
605
684
|
NotFound: NotFoundError,
|
|
606
685
|
Server: ServerError,
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vida-global/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Core libraries for supporting Vida development",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
9
|
+
"test": "LOG_LEVEL=test NODE_NO_WARNINGS=1 node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
10
10
|
"release": "node node_modules/@vida-global/release/scripts/release.js",
|
|
11
11
|
"develop": "node node_modules/@vida-global/release/scripts/release.js develop"
|
|
12
12
|
},
|
|
@@ -155,8 +155,8 @@ describe('VidaServerController', () => {
|
|
|
155
155
|
[paramName2]: {[validationType1]: options2}
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
-
it ('runs validation checkers based on inputs', () => {
|
|
159
|
-
controller.validateParameters(validations);
|
|
158
|
+
it ('runs validation checkers based on inputs', async () => {
|
|
159
|
+
await controller.validateParameters(validations);
|
|
160
160
|
expect(validator1).toHaveBeenCalledTimes(2);
|
|
161
161
|
expect(validator1).toHaveBeenCalledWith(value1, options1);
|
|
162
162
|
expect(validator1).toHaveBeenCalledWith(value2, options2);
|
|
@@ -165,52 +165,44 @@ describe('VidaServerController', () => {
|
|
|
165
165
|
expect(validator2).toHaveBeenCalledWith(value1, options2);
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
-
it ('throws a `ValidationError` when there\'s an error', () => {
|
|
168
|
+
it ('throws a `ValidationError` when there\'s an error', async () => {
|
|
169
169
|
validator1.mockImplementation(() => error1);
|
|
170
|
-
expect(()
|
|
171
|
-
controller.validateParameters(validations);
|
|
172
|
-
}).toThrow(Errors.ValidationError);
|
|
170
|
+
await expect(controller.validateParameters(validations)).rejects.toThrow();
|
|
173
171
|
});
|
|
174
172
|
|
|
175
|
-
it ('throws a `ValidationError` when there are multiple errors', () => {
|
|
173
|
+
it ('throws a `ValidationError` when there are multiple errors', async () => {
|
|
176
174
|
validator1.mockImplementation(() => error1);
|
|
177
175
|
validator2.mockImplementation(() => error2);
|
|
178
176
|
|
|
179
|
-
expect(()
|
|
180
|
-
controller.validateParameters(validations);
|
|
181
|
-
}).toThrow(Errors.ValidationError);
|
|
177
|
+
await expect(controller.validateParameters(validations)).rejects.toThrow(Errors.ValidationError);
|
|
182
178
|
});
|
|
183
179
|
|
|
184
|
-
it ('sets nothing when there is no error', () => {
|
|
185
|
-
controller.validateParameters(validations);
|
|
180
|
+
it ('sets nothing when there is no error', async () => {
|
|
181
|
+
await controller.validateParameters(validations);
|
|
186
182
|
expect(controller.render).toHaveBeenCalledTimes(0);
|
|
187
183
|
expect(response.statusCode).toEqual(200);
|
|
188
184
|
});
|
|
189
185
|
|
|
190
|
-
it ('ignores details where there is no validator', () => {
|
|
186
|
+
it ('ignores details where there is no validator', async () => {
|
|
191
187
|
const validations = {
|
|
192
188
|
[paramName2]: {[validationType1]: options2, 'foo': true}
|
|
193
189
|
};
|
|
194
|
-
controller.validateParameters(validations);
|
|
190
|
+
await controller.validateParameters(validations);
|
|
195
191
|
|
|
196
192
|
expect(validator1).toHaveBeenCalledTimes(1);
|
|
197
193
|
expect(validator1).toHaveBeenCalledWith(value2, options2);
|
|
198
194
|
});
|
|
199
195
|
|
|
200
|
-
it ('does not error when optional parameters are not included', () => {
|
|
196
|
+
it ('does not error when optional parameters are not included', async () => {
|
|
201
197
|
const validations = {foo: {isString: true, optional: true}};
|
|
202
|
-
expect(()
|
|
203
|
-
controller.validateParameters(validations);
|
|
204
|
-
}).not.toThrow();
|
|
198
|
+
expect(controller.validateParameters(validations)).resolves.not.toThrow();
|
|
205
199
|
});
|
|
206
200
|
|
|
207
|
-
it ('does error when optional parameters are invalid', () => {
|
|
201
|
+
it ('does error when optional parameters are invalid', async () => {
|
|
208
202
|
const validations = {foo: {isString: {length: {gte: 2}}, optional: true}};
|
|
209
203
|
params.foo = '';
|
|
210
204
|
|
|
211
|
-
expect(()
|
|
212
|
-
controller.validateParameters(validations);
|
|
213
|
-
}).toThrow(Errors.ValidationError);
|
|
205
|
+
await expect(controller.validateParameters(validations)).rejects.toThrow(Errors.ValidationError);
|
|
214
206
|
|
|
215
207
|
delete params.foo;
|
|
216
208
|
});
|
|
@@ -458,13 +450,13 @@ describe('VidaServerController', () => {
|
|
|
458
450
|
const error = TestHelpers.Faker.Text.randomString();
|
|
459
451
|
const validator = (value) => value == correctValue ? undefined : error;
|
|
460
452
|
|
|
461
|
-
it ('returns undefiend when passed a valid value', () => {
|
|
462
|
-
const result = controller.validateFunction(correctValue, validator);
|
|
453
|
+
it ('returns undefiend when passed a valid value', async () => {
|
|
454
|
+
const result = await controller.validateFunction(correctValue, validator);
|
|
463
455
|
expect(result).toBe(undefined);
|
|
464
456
|
});
|
|
465
457
|
|
|
466
|
-
it ('returns an error when the function returns an error', () => {
|
|
467
|
-
const result = controller.validateFunction(Math.random(), validator);
|
|
458
|
+
it ('returns an error when the function returns an error', async () => {
|
|
459
|
+
const result = await controller.validateFunction(Math.random(), validator);
|
|
468
460
|
expect(result).toBe(error);
|
|
469
461
|
});
|
|
470
462
|
});
|
|
@@ -472,10 +464,11 @@ describe('VidaServerController', () => {
|
|
|
472
464
|
|
|
473
465
|
describe('#performRequest', () => {
|
|
474
466
|
const errorHandlers = [
|
|
475
|
-
['
|
|
476
|
-
['
|
|
477
|
-
['
|
|
478
|
-
['
|
|
467
|
+
['renderUnauthorized', 'AuthorizationError'],
|
|
468
|
+
['renderConflict', 'ConflictError'],
|
|
469
|
+
['renderForbidden', 'ForbiddenError'],
|
|
470
|
+
['renderNotFound', 'NotFoundError'],
|
|
471
|
+
['renderErrors', 'ValidationError'],
|
|
479
472
|
]
|
|
480
473
|
|
|
481
474
|
it.each(errorHandlers)('calls %s when %s is thrown', async (handlerName, errorClsName) => {
|
|
@@ -505,7 +498,7 @@ describe('VidaServerController', () => {
|
|
|
505
498
|
await controller.performRequest('testAction');
|
|
506
499
|
|
|
507
500
|
expect(controller.statusCode).toEqual(500);
|
|
508
|
-
expect(controller.render).toHaveBeenCalledWith({errors: {
|
|
501
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
509
502
|
});
|
|
510
503
|
});
|
|
511
504
|
|
|
@@ -519,7 +512,6 @@ describe('VidaServerController', () => {
|
|
|
519
512
|
controller.render = jest.fn();
|
|
520
513
|
});
|
|
521
514
|
|
|
522
|
-
|
|
523
515
|
describe('#renderErrors', () => {
|
|
524
516
|
it ('returns a 400 response', async () => {
|
|
525
517
|
await controller.renderErrors();
|
|
@@ -576,64 +568,66 @@ describe('VidaServerController', () => {
|
|
|
576
568
|
});
|
|
577
569
|
});
|
|
578
570
|
|
|
579
|
-
|
|
580
|
-
describe('#renderUnauthorizedResponse', () => {
|
|
581
|
-
it ('returns a 401 response', async () => {
|
|
582
|
-
await controller.renderUnauthorizedResponse();
|
|
583
|
-
expect(response.statusCode).toBe(401);
|
|
584
|
-
})
|
|
585
|
-
|
|
586
|
-
it ('includes a blank message by default', async () => {
|
|
587
|
-
await controller.renderUnauthorizedResponse();
|
|
588
|
-
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
589
|
-
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
590
|
-
})
|
|
591
571
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
572
|
+
const successHandlers = [
|
|
573
|
+
['renderCreationSuccessful', 201],
|
|
574
|
+
['renderAccepted', 202],
|
|
575
|
+
['renderNoConent', 204],
|
|
576
|
+
['renderMovedPermanently', 301],
|
|
577
|
+
['renderFound', 302],
|
|
578
|
+
['renderNotModified', 304],
|
|
579
|
+
['renderTemporaryRedirect', 307],
|
|
580
|
+
['renderPermanentRedirect', 308],
|
|
581
|
+
]
|
|
582
|
+
describe.each(successHandlers)('#%s', (handlerName, statusCode) => {
|
|
583
|
+
it (`returns a ${statusCode} response`, async () => {
|
|
584
|
+
await (controller[handlerName]());
|
|
585
|
+
expect(response.statusCode).toBe(statusCode);
|
|
605
586
|
})
|
|
606
587
|
|
|
607
|
-
it ('includes
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
588
|
+
it ('includes the provided body', async () => {
|
|
589
|
+
const key = TestHelpers.Faker.Text.randomString();
|
|
590
|
+
const value = TestHelpers.Faker.Text.randomString();
|
|
591
|
+
const body = {[key]: value};
|
|
592
|
+
await (controller[handlerName](body));
|
|
612
593
|
|
|
613
|
-
it ('includes a provided message', async () => {
|
|
614
|
-
const msg = TestHelpers.Faker.Text.randomString();
|
|
615
|
-
await controller.renderForbiddenResponse(msg);
|
|
616
594
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
617
|
-
expect(controller.render).toHaveBeenCalledWith(
|
|
595
|
+
expect(controller.render).toHaveBeenCalledWith(body);
|
|
618
596
|
});
|
|
619
597
|
});
|
|
620
598
|
|
|
599
|
+
|
|
600
|
+
const errorHandlers = [
|
|
601
|
+
['renderUnauthorized', 401],
|
|
602
|
+
['renderPaymentRequired', 402],
|
|
603
|
+
['renderForbidden', 403],
|
|
604
|
+
['renderNotFound', 404],
|
|
605
|
+
['renderMethodNotAllowed', 405],
|
|
606
|
+
['renderRequestTimeout', 408],
|
|
607
|
+
['renderConflict', 409],
|
|
608
|
+
['renderGone', 410],
|
|
609
|
+
['renderContentTooLarge', 413],
|
|
610
|
+
['renderUnsupportedMediaType', 415],
|
|
611
|
+
['renderUnprocessableContent', 422],
|
|
612
|
+
['renderLocked', 423],
|
|
613
|
+
['renderTooManyRequests', 429],
|
|
614
|
+
]
|
|
621
615
|
|
|
622
|
-
describe('
|
|
623
|
-
it (
|
|
624
|
-
await controller
|
|
625
|
-
expect(response.statusCode).toBe(
|
|
616
|
+
describe.each(errorHandlers)('#%s', (handlerName, statusCode) => {
|
|
617
|
+
it (`returns a ${statusCode} response`, async () => {
|
|
618
|
+
await (controller[handlerName]());
|
|
619
|
+
expect(response.statusCode).toBe(statusCode);
|
|
626
620
|
})
|
|
627
621
|
|
|
628
622
|
it ('includes a blank message by default', async () => {
|
|
629
|
-
await controller
|
|
623
|
+
await controller[handlerName]();
|
|
630
624
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
631
625
|
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
632
626
|
})
|
|
633
627
|
|
|
634
628
|
it ('includes a provided message', async () => {
|
|
635
629
|
const msg = TestHelpers.Faker.Text.randomString();
|
|
636
|
-
await controller
|
|
630
|
+
await controller[handlerName](msg);
|
|
637
631
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
638
632
|
expect(controller.render).toHaveBeenCalledWith({errors: {message: msg}});
|
|
639
633
|
});
|