@vida-global/core 2.1.0 → 2.1.2
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/activeRecord/baseRecord.js +83 -8
- package/lib/activeRecord/db/queryInterface.js +1 -1
- package/lib/http/README.md +4 -4
- package/lib/http/client.js +33 -2
- package/lib/server/controllerMixins/renderer.js +51 -33
- package/lib/server/doc/renderer.md +1 -1
- package/lib/server/serverController.js +2 -1
- package/package.json +1 -1
- package/test/activeRecord/baseRecord.test.js +125 -6
- package/test/activeRecord/db/queryInterface.test.js +4 -2
- package/test/http/client.test.js +34 -0
- package/test/http/helpers/client.js +31 -0
- package/test/server/controllerMixins/callbacks.test.js +6 -5
- package/test/server/controllerMixins/renderer.test.js +74 -14
- package/test/server/helpers/response.js +19 -0
- package/test/server/serverController.test.js +8 -7
|
@@ -33,21 +33,74 @@ class BaseRecord extends Model {
|
|
|
33
33
|
/***********************************************************************************************
|
|
34
34
|
* QUERIES
|
|
35
35
|
***********************************************************************************************/
|
|
36
|
-
static async find(ids) {
|
|
36
|
+
static async find(ids, { useWriteDB=false, ...options }={}) {
|
|
37
37
|
if (this.isCacheable) {
|
|
38
|
-
return await this._cachedFind(
|
|
38
|
+
return await this._cachedFind(ids, options);
|
|
39
39
|
} else if (Array.isArray(ids)) {
|
|
40
40
|
const pk = this.primaryKeyAttribute
|
|
41
|
-
return await this.where({[pk]: ids});
|
|
41
|
+
return await this.where({[pk]: ids}, { useMaster: useWriteDB });
|
|
42
42
|
} else {
|
|
43
|
-
return await this.findByPk(ids);
|
|
43
|
+
return await this.findByPk(ids, { useMaster: useWriteDB });
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
static async where(where, options={}) {
|
|
49
|
-
const
|
|
50
|
-
|
|
49
|
+
const { page, pageSize, ...queryOptions } = options;
|
|
50
|
+
|
|
51
|
+
if (!page && !pageSize) {
|
|
52
|
+
const parameters = {...queryOptions, where };
|
|
53
|
+
return await this.findAll(parameters);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const offset = pageSize * (page - 1);
|
|
57
|
+
const rows = await this.findAll({
|
|
58
|
+
...queryOptions,
|
|
59
|
+
where,
|
|
60
|
+
order: queryOptions.order || [this.defaultSort()],
|
|
61
|
+
limit: pageSize,
|
|
62
|
+
offset,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const totalCount = await this.paginatedTotalCount(rows, where,
|
|
66
|
+
{
|
|
67
|
+
offset,
|
|
68
|
+
pageSize,
|
|
69
|
+
queryOptions,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return { rows, totalCount }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
static async transaction(callback) {
|
|
77
|
+
return await this.connection.queryInterface.transaction(callback);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
static defaultSort() {
|
|
82
|
+
return ['created_at', 'DESC'];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
static async paginatedTotalCount(rows, where, { offset, pageSize, queryOptions }) {
|
|
87
|
+
const isFullPage = rows.length === pageSize
|
|
88
|
+
const isEmptyPageAfterFirst = rows.length === 0 && offset > 0
|
|
89
|
+
|
|
90
|
+
if (isFullPage || isEmptyPageAfterFirst) {
|
|
91
|
+
return await this.count({...queryOptions, where })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return offset + rows.length
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
static async execute(query, { raw=false, ...options }={}) {
|
|
99
|
+
return await this.sequelize.query(query, {
|
|
100
|
+
type: Sequelize.QueryTypes.SELECT,
|
|
101
|
+
...(raw ? {} : { model: this, mapToModel: true }),
|
|
102
|
+
...options
|
|
103
|
+
});
|
|
51
104
|
}
|
|
52
105
|
|
|
53
106
|
|
|
@@ -78,10 +131,16 @@ class BaseRecord extends Model {
|
|
|
78
131
|
updatedAt: 'updated_at',
|
|
79
132
|
}
|
|
80
133
|
|
|
81
|
-
|
|
134
|
+
const hasTimestamps = schema.created_at && schema.updated_at;
|
|
135
|
+
|
|
136
|
+
if (!hasTimestamps) {
|
|
82
137
|
options.timestamps = false;
|
|
83
138
|
}
|
|
84
139
|
|
|
140
|
+
if (hasTimestamps && schema.deleted_at) {
|
|
141
|
+
options.paranoid = true;
|
|
142
|
+
}
|
|
143
|
+
|
|
85
144
|
this.configureOverridenAccessors(schema);
|
|
86
145
|
this.configureVirtualAccessors(schema);
|
|
87
146
|
|
|
@@ -376,17 +435,33 @@ class BaseRecord extends Model {
|
|
|
376
435
|
toApiResponse() {
|
|
377
436
|
const response = {};
|
|
378
437
|
for (const column of this.apiResponseColumns) {
|
|
379
|
-
response[column] = this[column]; // Use overridden getters when available
|
|
438
|
+
response[this.apiResponseKey(column)] = this[column]; // Use overridden getters when available
|
|
380
439
|
}
|
|
381
440
|
return response;
|
|
382
441
|
}
|
|
383
442
|
|
|
384
443
|
|
|
444
|
+
toObject() {
|
|
445
|
+
return this.toApiResponse();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
|
|
385
449
|
get apiResponseColumns() {
|
|
386
450
|
return Object.keys(this.constructor.documentationSchema());
|
|
387
451
|
}
|
|
388
452
|
|
|
389
453
|
|
|
454
|
+
apiResponseKey(column) {
|
|
455
|
+
if (!this.camelizeFieldsForApiResponse) return column;
|
|
456
|
+
return camelize(column, true);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
get camelizeFieldsForApiResponse() {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
390
465
|
/***********************************************************************************************
|
|
391
466
|
* DOCUMENTATION
|
|
392
467
|
***********************************************************************************************/
|
package/lib/http/README.md
CHANGED
|
@@ -25,10 +25,8 @@ class MyApiClient extends HttpClient {
|
|
|
25
25
|
return this.#dev ? 'https://staging.foo.com' : 'https://foo.com';
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
get
|
|
29
|
-
|
|
30
|
-
headers.Authorization = `Bearer ${this.#token}`;
|
|
31
|
-
return headers;
|
|
28
|
+
get bearerToken() {
|
|
29
|
+
return this.#token;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async getTickets({ page, pageSize }) {
|
|
@@ -104,4 +102,6 @@ Subclasses customize behavior through getters on the class:
|
|
|
104
102
|
|
|
105
103
|
- `urlRoot` — base URL prepended to every endpoint.
|
|
106
104
|
- `defaultHeaders` — merged into every request (per-call `headers` override).
|
|
105
|
+
- `bearerToken` — when it returns a value, a `Authorization: Bearer <token>` header is added to `defaultHeaders`.
|
|
106
|
+
- `basicAuthUsername` / `basicAuthPassword` — when both return values, a `Authorization: Basic <base64>` header is added to `defaultHeaders`. `bearerToken` takes precedence if both are set.
|
|
107
107
|
- `logger` — scoped logger used for the `API CALL: ...` debug line emitted on each request (defaults to `logger.http`).
|
package/lib/http/client.js
CHANGED
|
@@ -178,10 +178,41 @@ class HttpClient {
|
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
get defaultHeaders() {
|
|
181
|
-
|
|
181
|
+
const headers = {
|
|
182
182
|
Accept: "application/json",
|
|
183
183
|
"Content-Type": "application/json",
|
|
184
|
-
}
|
|
184
|
+
};
|
|
185
|
+
if (this.authorizationHeader) headers.Authorization = this.authorizationHeader;
|
|
186
|
+
return headers;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
get authorizationHeader() {
|
|
191
|
+
if (this.bearerToken) return `Bearer ${this.bearerToken}`;
|
|
192
|
+
if (this.basicAuthCredentials) return `Basic ${this.basicAuthCredentials}`;
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
get bearerToken() {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
get basicAuthCredentials() {
|
|
203
|
+
if (!this.basicAuthUsername || !this.basicAuthPassword) return null;
|
|
204
|
+
const credentials = `${this.basicAuthUsername}:${this.basicAuthPassword}`;
|
|
205
|
+
return Buffer.from(credentials).toString('base64');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
get basicAuthUsername() {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
get basicAuthPassword() {
|
|
215
|
+
return null;
|
|
185
216
|
}
|
|
186
217
|
|
|
187
218
|
|
|
@@ -51,6 +51,9 @@ function setSseHeaders(controller) {
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
const InstanceMethods = {
|
|
54
|
+
MIME_TYPES: CONTENT_TYPE_BY_EXTENSION,
|
|
55
|
+
|
|
56
|
+
|
|
54
57
|
async render(body, options={}) {
|
|
55
58
|
if (this.rendered) return;
|
|
56
59
|
if (this.isStreaming) return;
|
|
@@ -76,21 +79,21 @@ const InstanceMethods = {
|
|
|
76
79
|
|
|
77
80
|
|
|
78
81
|
renderJSONResponse(body) {
|
|
79
|
-
this.
|
|
82
|
+
if (!this.responseContentType) this.responseContentType = CONTENT_TYPE_BY_EXTENSION.json;
|
|
80
83
|
this._response.json(body);
|
|
81
84
|
},
|
|
82
85
|
|
|
83
86
|
|
|
84
87
|
renderXMLResponse(body) {
|
|
85
|
-
this.
|
|
86
|
-
const doc = create({
|
|
88
|
+
if (!this.responseContentType) this.responseContentType = CONTENT_TYPE_BY_EXTENSION.xml;
|
|
89
|
+
const doc = create({ [this.xmlRootElement]: body });
|
|
87
90
|
const xml = doc.end();
|
|
88
91
|
this._response.send(xml);
|
|
89
92
|
},
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
renderTextResponse(body) {
|
|
93
|
-
this.
|
|
96
|
+
if (!this.responseContentType) this.responseContentType = CONTENT_TYPE_BY_EXTENSION.txt;
|
|
94
97
|
this._response.send(body);
|
|
95
98
|
},
|
|
96
99
|
|
|
@@ -163,7 +166,7 @@ const InstanceMethods = {
|
|
|
163
166
|
|
|
164
167
|
|
|
165
168
|
_serializeDateForResponseBody(date, options) {
|
|
166
|
-
return date.getTime();
|
|
169
|
+
return Math.floor(date.getTime() / 1000);
|
|
167
170
|
},
|
|
168
171
|
|
|
169
172
|
|
|
@@ -186,33 +189,6 @@ const InstanceMethods = {
|
|
|
186
189
|
},
|
|
187
190
|
|
|
188
191
|
|
|
189
|
-
_setupStatusRenderers() {
|
|
190
|
-
const successStatuses = {
|
|
191
|
-
CreationSuccessful: 201,
|
|
192
|
-
Accepted: 202,
|
|
193
|
-
NoConent: 204,
|
|
194
|
-
MovedPermanently: 301,
|
|
195
|
-
Found: 302,
|
|
196
|
-
NotModified: 304,
|
|
197
|
-
TemporaryRedirect: 307,
|
|
198
|
-
PermanentRedirect: 308,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
for (let [type, statusCode] of Object.entries(Errors.Statuses)) {
|
|
202
|
-
if (type == 'Server') type = 'ServerError';
|
|
203
|
-
this[`render${type}`] = async function(message=null, body=null) {
|
|
204
|
-
return await this._renderError(statusCode, message, body);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
for (let [type, statusCode] of Object.entries(successStatuses)) {
|
|
209
|
-
this[`render${type}`] = async function(body=null) {
|
|
210
|
-
return await this._renderSuccess(statusCode, body);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
|
|
216
192
|
async _renderError(statusCode, message, body) {
|
|
217
193
|
this.statusCode = statusCode;
|
|
218
194
|
body = structuredClone(body || {});
|
|
@@ -305,7 +281,37 @@ const InstanceMethods = {
|
|
|
305
281
|
}
|
|
306
282
|
|
|
307
283
|
|
|
284
|
+
const StatusRenderSuccessStatuses = {
|
|
285
|
+
CreationSuccessful: 201,
|
|
286
|
+
Accepted: 202,
|
|
287
|
+
NoContent: 204,
|
|
288
|
+
MovedPermanently: 301,
|
|
289
|
+
Found: 302,
|
|
290
|
+
NotModified: 304,
|
|
291
|
+
TemporaryRedirect: 307,
|
|
292
|
+
PermanentRedirect: 308,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
308
296
|
const StaticMethods = {
|
|
297
|
+
// Define render<Status> helpers on the prototype (not per instance) so subclasses and mixins
|
|
298
|
+
// can override them with a normal method of the same name.
|
|
299
|
+
setupStatusRenderers() {
|
|
300
|
+
for (let [type, statusCode] of Object.entries(Errors.Statuses)) {
|
|
301
|
+
if (type == 'Server') type = 'ServerError';
|
|
302
|
+
this.prototype[`render${type}`] = async function(message=null, body=null) {
|
|
303
|
+
return await this._renderError(statusCode, message, body);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
for (const [type, statusCode] of Object.entries(StatusRenderSuccessStatuses)) {
|
|
308
|
+
this.prototype[`render${type}`] = async function(body=null) {
|
|
309
|
+
return await this._renderSuccess(statusCode, body);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
|
|
309
315
|
formatResponseBody(body, errors, { statusCode, statusText }) {
|
|
310
316
|
const response = {data: body, status: statusText};
|
|
311
317
|
if (errors) response.errors = errors;
|
|
@@ -325,7 +331,6 @@ const Accessors = {
|
|
|
325
331
|
get() {
|
|
326
332
|
const override = `${this.params._format || ''}`.toLowerCase();
|
|
327
333
|
if (override === 'xml' || override === 'json') return override;
|
|
328
|
-
if (/\bxml\b/i.test(this.requestHeaders.accept || '')) return 'xml';
|
|
329
334
|
return 'json';
|
|
330
335
|
}
|
|
331
336
|
},
|
|
@@ -341,6 +346,19 @@ const Accessors = {
|
|
|
341
346
|
},
|
|
342
347
|
|
|
343
348
|
|
|
349
|
+
responseContentType: {
|
|
350
|
+
get() { return this._response.getHeader('content-type') },
|
|
351
|
+
set(type) { return this._response.setHeader('content-type', type) }
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
// Root element wrapped around XML response bodies. Override in a subclass to emit a custom
|
|
356
|
+
// root (e.g. a TwiML `<Response>` for a telecom webhook).
|
|
357
|
+
xmlRootElement: {
|
|
358
|
+
get() { return 'response' }
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
|
|
344
362
|
statusText: {
|
|
345
363
|
get() {
|
|
346
364
|
return STATUS_TEXTS[this.statusCode];
|
|
@@ -47,7 +47,7 @@ Don't set `this.statusCode` directly — use the helper that pairs the status wi
|
|
|
47
47
|
|---|---|
|
|
48
48
|
| 201 Created | `this.renderCreationSuccessful(body)` |
|
|
49
49
|
| 202 Accepted | `this.renderAccepted(body)` |
|
|
50
|
-
| 204 No Content | `this.
|
|
50
|
+
| 204 No Content | `this.renderNoContent(body)` |
|
|
51
51
|
| 301 Moved Permanently | `this.renderMovedPermanently(body)` |
|
|
52
52
|
| 302 Found | `this.renderFound(body)` |
|
|
53
53
|
| 304 Not Modified | `this.renderNotModified(body)` |
|
|
@@ -23,7 +23,6 @@ class VidaServerController {
|
|
|
23
23
|
|
|
24
24
|
this._applyCallbacks();
|
|
25
25
|
this._applyDocumentation();
|
|
26
|
-
this._setupStatusRenderers();
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
|
|
@@ -260,6 +259,8 @@ VidaServerController.loadMixin(RendererMixin);
|
|
|
260
259
|
VidaServerController.loadMixin(RoutingMixin);
|
|
261
260
|
VidaServerController.loadMixin(ValidationMixin);
|
|
262
261
|
|
|
262
|
+
VidaServerController.setupStatusRenderers();
|
|
263
|
+
|
|
263
264
|
|
|
264
265
|
/***************************************************************************************************
|
|
265
266
|
* ERRORS
|
package/package.json
CHANGED
|
@@ -165,8 +165,58 @@ describe('BaseRecord', () => {
|
|
|
165
165
|
sequelize: new Helpers.MockSequelize(),
|
|
166
166
|
tableName: 'users',
|
|
167
167
|
timestamps: false,
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
updatedAt: 'updated_at'
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it ('uses `paranoid: true` when the schema has timestamps and deleted_at', () => {
|
|
173
|
+
importSchema.mockImplementation(() => ({
|
|
174
|
+
created_at: {type: Helpers.dataTypeValue1},
|
|
175
|
+
deleted_at: {type: Helpers.dataTypeValue2},
|
|
176
|
+
updated_at: {type: Helpers.dataTypeValue2},
|
|
177
|
+
}));
|
|
178
|
+
class User extends BaseRecord {}
|
|
179
|
+
User.initialize();
|
|
180
|
+
|
|
181
|
+
expect(Model.init).toHaveBeenCalledWith(
|
|
182
|
+
{
|
|
183
|
+
created_at: {type: Sequelize.DataTypes[Helpers.dataTypeKey1]},
|
|
184
|
+
deleted_at: {type: Sequelize.DataTypes[Helpers.dataTypeKey2]},
|
|
185
|
+
updated_at: {type: Sequelize.DataTypes[Helpers.dataTypeKey2]},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
createdAt: 'created_at',
|
|
189
|
+
deletedAt: 'deleted_at',
|
|
190
|
+
modelName: 'User',
|
|
191
|
+
paranoid: true,
|
|
192
|
+
sequelize: new Helpers.MockSequelize(),
|
|
193
|
+
tableName: 'users',
|
|
194
|
+
updatedAt: 'updated_at'
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it ('does not use `paranoid: true` when the schema has deleted_at but no timestamps', () => {
|
|
200
|
+
importSchema.mockImplementation(() => ({
|
|
201
|
+
deleted_at: {type: Helpers.dataTypeValue2},
|
|
202
|
+
}));
|
|
203
|
+
class User extends BaseRecord {}
|
|
204
|
+
User.initialize();
|
|
205
|
+
|
|
206
|
+
expect(Model.init).toHaveBeenCalledWith(
|
|
207
|
+
{
|
|
208
|
+
deleted_at: {type: Sequelize.DataTypes[Helpers.dataTypeKey2]},
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
createdAt: 'created_at',
|
|
212
|
+
deletedAt: 'deleted_at',
|
|
213
|
+
modelName: 'User',
|
|
214
|
+
sequelize: new Helpers.MockSequelize(),
|
|
215
|
+
tableName: 'users',
|
|
216
|
+
timestamps: false,
|
|
217
|
+
updatedAt: 'updated_at'
|
|
218
|
+
}
|
|
219
|
+
);
|
|
170
220
|
});
|
|
171
221
|
|
|
172
222
|
it ('does not run twice on the same model', () => {
|
|
@@ -382,7 +432,7 @@ describe('BaseRecord', () => {
|
|
|
382
432
|
const id = Math.random();
|
|
383
433
|
await Person.find(id);
|
|
384
434
|
expect(Person.findByPk).toHaveBeenCalledTimes(1);
|
|
385
|
-
expect(Person.findByPk).toHaveBeenCalledWith(id);
|
|
435
|
+
expect(Person.findByPk).toHaveBeenCalledWith(id, {useMaster: false});
|
|
386
436
|
});
|
|
387
437
|
|
|
388
438
|
it ('calls the sequelize `findAll` method when passed multiple value', async () => {
|
|
@@ -390,7 +440,13 @@ describe('BaseRecord', () => {
|
|
|
390
440
|
const id2 = Math.random();
|
|
391
441
|
await Person.find([id1, id2]);
|
|
392
442
|
expect(Person.findAll).toHaveBeenCalledTimes(1);
|
|
393
|
-
expect(Person.findAll).toHaveBeenCalledWith({where: {[pk]: [id1, id2]}});
|
|
443
|
+
expect(Person.findAll).toHaveBeenCalledWith({useMaster: false, where: {[pk]: [id1, id2]}});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it ('forwards useWriteDB to findByPk as useMaster', async () => {
|
|
447
|
+
const id = Math.random();
|
|
448
|
+
await Person.find(id, {useWriteDB: true});
|
|
449
|
+
expect(Person.findByPk).toHaveBeenCalledWith(id, {useMaster: true});
|
|
394
450
|
});
|
|
395
451
|
});
|
|
396
452
|
|
|
@@ -421,6 +477,29 @@ describe('BaseRecord', () => {
|
|
|
421
477
|
});
|
|
422
478
|
|
|
423
479
|
|
|
480
|
+
describe('BaseRecord.transaction', () => {
|
|
481
|
+
it ('delegates to the query interface and returns the result', async () => {
|
|
482
|
+
const expected = {};
|
|
483
|
+
const callback = jest.fn(() => expected);
|
|
484
|
+
const queryInterface = {
|
|
485
|
+
transaction: jest.fn(cb => cb())
|
|
486
|
+
};
|
|
487
|
+
class FakeConnection {
|
|
488
|
+
get queryInterface() { return queryInterface; }
|
|
489
|
+
}
|
|
490
|
+
class User extends BaseRecord {
|
|
491
|
+
static get connectionClass() { return FakeConnection; }
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const result = await User.transaction(callback);
|
|
495
|
+
|
|
496
|
+
expect(queryInterface.transaction).toHaveBeenCalledTimes(1);
|
|
497
|
+
expect(queryInterface.transaction).toHaveBeenCalledWith(callback);
|
|
498
|
+
expect(result).toBe(expected);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
|
|
424
503
|
describe('Caching', () => {
|
|
425
504
|
class Person extends BaseRecord {
|
|
426
505
|
static get isCacheable() { return true; }
|
|
@@ -439,7 +518,7 @@ describe('BaseRecord', () => {
|
|
|
439
518
|
await Person.find(id);
|
|
440
519
|
expect(Person.findByPk).toHaveBeenCalledTimes(0);
|
|
441
520
|
expect(findSpy).toHaveBeenCalledTimes(1);
|
|
442
|
-
expect(findSpy).toHaveBeenCalledWith(id);
|
|
521
|
+
expect(findSpy).toHaveBeenCalledWith(id, {});
|
|
443
522
|
});
|
|
444
523
|
|
|
445
524
|
it ('calls _cachedFind when multiple ids are passed', async () => {
|
|
@@ -448,7 +527,7 @@ describe('BaseRecord', () => {
|
|
|
448
527
|
await Person.find([id1, id2]);
|
|
449
528
|
expect(Person.findByPk).toHaveBeenCalledTimes(0);
|
|
450
529
|
expect(findSpy).toHaveBeenCalledTimes(1);
|
|
451
|
-
expect(findSpy).toHaveBeenCalledWith([id1, id2]);
|
|
530
|
+
expect(findSpy).toHaveBeenCalledWith([id1, id2], {});
|
|
452
531
|
});
|
|
453
532
|
});
|
|
454
533
|
|
|
@@ -842,5 +921,45 @@ describe('BaseRecord', () => {
|
|
|
842
921
|
|
|
843
922
|
expect(record.toApiResponse()).toEqual({ name: 'OVERRIDDEN' });
|
|
844
923
|
});
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
it ('leaves column keys unchanged when camelizeFieldsForApiResponse is disabled', () => {
|
|
927
|
+
const record = buildRecord(
|
|
928
|
+
{ org_id: { type: 'integer' }, external_task_id: { type: 'string' } },
|
|
929
|
+
{ org_id: 7, external_task_id: 'abc' },
|
|
930
|
+
);
|
|
931
|
+
|
|
932
|
+
expect(record.toApiResponse()).toEqual({ org_id: 7, external_task_id: 'abc' });
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
it ('camelizes column keys when camelizeFieldsForApiResponse is enabled', () => {
|
|
937
|
+
const record = buildRecord(
|
|
938
|
+
{ org_id: { type: 'integer' }, external_task_id: { type: 'string' } },
|
|
939
|
+
{ org_id: 7, external_task_id: 'abc' },
|
|
940
|
+
);
|
|
941
|
+
Object.defineProperty(record, 'camelizeFieldsForApiResponse', { get: () => true, configurable: true });
|
|
942
|
+
|
|
943
|
+
expect(record.toApiResponse()).toEqual({ orgId: 7, externalTaskId: 'abc' });
|
|
944
|
+
});
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
describe('#apiResponseKey', () => {
|
|
949
|
+
it ('returns the column unchanged by default', () => {
|
|
950
|
+
class Thing extends BaseRecord {}
|
|
951
|
+
const record = Object.create(Thing.prototype);
|
|
952
|
+
|
|
953
|
+
expect(record.apiResponseKey('external_task_id')).toEqual('external_task_id');
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
it ('camelizes the column when camelizeFieldsForApiResponse is enabled', () => {
|
|
958
|
+
class Thing extends BaseRecord {}
|
|
959
|
+
const record = Object.create(Thing.prototype);
|
|
960
|
+
Object.defineProperty(record, 'camelizeFieldsForApiResponse', { get: () => true, configurable: true });
|
|
961
|
+
|
|
962
|
+
expect(record.apiResponseKey('external_task_id')).toEqual('externalTaskId');
|
|
963
|
+
});
|
|
845
964
|
});
|
|
846
965
|
});
|
|
@@ -29,10 +29,12 @@ describe('QueryInterface', () => {
|
|
|
29
29
|
|
|
30
30
|
describe('QueryInterface#transaction', () => {
|
|
31
31
|
it('delegates transaction callback to sequelize', async () => {
|
|
32
|
-
const
|
|
32
|
+
const expected = {};
|
|
33
|
+
const callback = jest.fn(() => expected);
|
|
33
34
|
|
|
34
|
-
await queryInterface.transaction(callback);
|
|
35
|
+
const result = await queryInterface.transaction(callback);
|
|
35
36
|
|
|
37
|
+
expect(result).toBe(expected);
|
|
36
38
|
expect(connection._sequelize.transaction).toHaveBeenCalledTimes(1);
|
|
37
39
|
expect(connection._sequelize.transaction).toHaveBeenCalledWith(callback);
|
|
38
40
|
});
|
package/test/http/client.test.js
CHANGED
|
@@ -26,6 +26,40 @@ describe('HttpClient', () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
describe('HttpClient#authorizationHeader', () => {
|
|
30
|
+
it('omits the Authorization header by default', async () => {
|
|
31
|
+
const client = new HttpClient();
|
|
32
|
+
|
|
33
|
+
await client.post(Helpers.apiUrl);
|
|
34
|
+
|
|
35
|
+
const callHeaders = fetch.mock.calls[0][1].headers;
|
|
36
|
+
expect(callHeaders.Authorization).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('sets a bearer Authorization header when bearerToken is overridden', async () => {
|
|
40
|
+
const client = new Helpers.ClientWithBearerToken();
|
|
41
|
+
|
|
42
|
+
await client.post(Helpers.apiUrl);
|
|
43
|
+
|
|
44
|
+
Helpers.expectRequest('POST', Helpers.apiUrl, {
|
|
45
|
+
headers: { Authorization: `Bearer ${Helpers.bearerToken}` },
|
|
46
|
+
body: {},
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('sets a basic Authorization header when username and password are overridden', async () => {
|
|
51
|
+
const client = new Helpers.ClientWithBasicAuth();
|
|
52
|
+
|
|
53
|
+
await client.post(Helpers.apiUrl);
|
|
54
|
+
|
|
55
|
+
Helpers.expectRequest('POST', Helpers.apiUrl, {
|
|
56
|
+
headers: { Authorization: `Basic ${Helpers.basicAuthCredentials}` },
|
|
57
|
+
body: {},
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
|
|
29
63
|
const httpMethods = [
|
|
30
64
|
['post', 'POST', { supportsBody: true }],
|
|
31
65
|
['put', 'PUT', { supportsBody: true }],
|
|
@@ -26,6 +26,33 @@ class ClientWithUrlRoot extends HttpClient {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
const bearerToken = TestHelpers.Faker.Text.randomString();
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ClientWithBearerToken extends HttpClient {
|
|
33
|
+
get bearerToken() {
|
|
34
|
+
return bearerToken;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
const basicAuthUsername = TestHelpers.Faker.Text.randomString();
|
|
40
|
+
const basicAuthPassword = TestHelpers.Faker.Text.randomString();
|
|
41
|
+
const basicAuthCredentials = Buffer.from(`${basicAuthUsername}:${basicAuthPassword}`).toString('base64');
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ClientWithBasicAuth extends HttpClient {
|
|
45
|
+
get basicAuthUsername() {
|
|
46
|
+
return basicAuthUsername;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
get basicAuthPassword() {
|
|
51
|
+
return basicAuthPassword;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
29
56
|
/***************************************************************************************************
|
|
30
57
|
* MOCKS
|
|
31
58
|
***************************************************************************************************/
|
|
@@ -80,6 +107,10 @@ function buildExpectedRequest({ method, headers, body }) {
|
|
|
80
107
|
|
|
81
108
|
module.exports = {
|
|
82
109
|
apiUrl,
|
|
110
|
+
basicAuthCredentials,
|
|
111
|
+
bearerToken,
|
|
112
|
+
ClientWithBasicAuth,
|
|
113
|
+
ClientWithBearerToken,
|
|
83
114
|
ClientWithUrlRoot,
|
|
84
115
|
endpoint,
|
|
85
116
|
expectRequest,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { VidaServerController } = require('../../../lib/server/serverController');
|
|
2
|
+
const { buildResponseMock } = require('../helpers/response');
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
describe('VidaServerController', () => {
|
|
@@ -75,7 +76,7 @@ describe('VidaServerController', () => {
|
|
|
75
76
|
namedBefore() { order.push('before2') }
|
|
76
77
|
testAction() { order.push('action'); return {} }
|
|
77
78
|
}
|
|
78
|
-
const response = {
|
|
79
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
79
80
|
const controller = new C({}, response);
|
|
80
81
|
|
|
81
82
|
await controller.performRequest('testAction');
|
|
@@ -92,7 +93,7 @@ describe('VidaServerController', () => {
|
|
|
92
93
|
namedAfter() { order.push('after2') }
|
|
93
94
|
testAction() { order.push('action'); return {} }
|
|
94
95
|
}
|
|
95
|
-
const response = {
|
|
96
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
96
97
|
const controller = new C({}, response);
|
|
97
98
|
|
|
98
99
|
await controller.performRequest('testAction');
|
|
@@ -105,7 +106,7 @@ describe('VidaServerController', () => {
|
|
|
105
106
|
setupCallbacks() { this.beforeCallback(() => false) }
|
|
106
107
|
}
|
|
107
108
|
C.prototype.testAction = testAction;
|
|
108
|
-
const response = {
|
|
109
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
109
110
|
const controller = new C({}, response);
|
|
110
111
|
|
|
111
112
|
await controller.performRequest('testAction');
|
|
@@ -121,7 +122,7 @@ describe('VidaServerController', () => {
|
|
|
121
122
|
}
|
|
122
123
|
testAction() { return {} }
|
|
123
124
|
}
|
|
124
|
-
const response = {
|
|
125
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
125
126
|
const controller = new C({}, response);
|
|
126
127
|
|
|
127
128
|
await controller.performRequest('testAction');
|
|
@@ -137,7 +138,7 @@ describe('VidaServerController', () => {
|
|
|
137
138
|
}
|
|
138
139
|
testAction() { return {} }
|
|
139
140
|
}
|
|
140
|
-
const response = {
|
|
141
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
141
142
|
const controller = new C({}, response);
|
|
142
143
|
|
|
143
144
|
await controller.performRequest('testAction');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { FooController } = require('../helpers/controllers/fooController');
|
|
2
|
+
const { buildResponseMock } = require('../helpers/response');
|
|
2
3
|
const TestHelpers = require('@vida-global/test-helpers');
|
|
3
4
|
const {
|
|
4
5
|
NoActiveStreamError,
|
|
@@ -76,7 +77,7 @@ describe('VidaServerController', () => {
|
|
|
76
77
|
const successHandlers = [
|
|
77
78
|
['renderCreationSuccessful', 201],
|
|
78
79
|
['renderAccepted', 202],
|
|
79
|
-
['
|
|
80
|
+
['renderNoContent', 204],
|
|
80
81
|
['renderMovedPermanently', 301],
|
|
81
82
|
['renderFound', 302],
|
|
82
83
|
['renderNotModified', 304],
|
|
@@ -159,7 +160,7 @@ describe('VidaServerController', () => {
|
|
|
159
160
|
let response;
|
|
160
161
|
let controller;
|
|
161
162
|
beforeEach(() => {
|
|
162
|
-
response =
|
|
163
|
+
response = buildResponseMock();
|
|
163
164
|
controller = new FooController({}, response);
|
|
164
165
|
});
|
|
165
166
|
|
|
@@ -221,30 +222,34 @@ describe('VidaServerController', () => {
|
|
|
221
222
|
expect(await controller._serializeResponseBody(true, {})).toBe(true);
|
|
222
223
|
});
|
|
223
224
|
|
|
224
|
-
it ('returns Date instances as their epoch
|
|
225
|
+
it ('returns Date instances as their epoch seconds', async () => {
|
|
225
226
|
const date = new Date('2020-01-02T03:04:05Z');
|
|
226
|
-
|
|
227
|
+
const seconds = Math.floor(date.getTime() / 1000);
|
|
228
|
+
expect(await controller._serializeResponseBody(date, {})).toBe(seconds);
|
|
227
229
|
});
|
|
228
230
|
|
|
229
231
|
it ('recursively processes Date values inside objects', async () => {
|
|
230
232
|
const date = new Date('2021-02-03T00:00:00Z');
|
|
233
|
+
const seconds = Math.floor(date.getTime() / 1000);
|
|
231
234
|
const result = await controller._serializeResponseBody({createdAt: date, name: 'foo'}, {});
|
|
232
|
-
expect(result).toEqual({createdAt:
|
|
235
|
+
expect(result).toEqual({createdAt: seconds, name: 'foo'});
|
|
233
236
|
});
|
|
234
237
|
|
|
235
238
|
it ('recursively processes arrays', async () => {
|
|
236
239
|
const date = new Date('2022-03-04T00:00:00Z');
|
|
240
|
+
const seconds = Math.floor(date.getTime() / 1000);
|
|
237
241
|
const result = await controller._serializeResponseBody([1, date, {ts: date}], {});
|
|
238
|
-
expect(result).toEqual([1,
|
|
242
|
+
expect(result).toEqual([1, seconds, {ts: seconds}]);
|
|
239
243
|
});
|
|
240
244
|
|
|
241
245
|
it ('invokes toApiResponse on objects that define it and recurses on the result', async () => {
|
|
242
246
|
const date = new Date('2023-04-05T00:00:00Z');
|
|
247
|
+
const seconds = Math.floor(date.getTime() / 1000);
|
|
243
248
|
const apiModel = {
|
|
244
249
|
async toApiResponse() { return {id: 1, createdAt: date}; }
|
|
245
250
|
};
|
|
246
251
|
const result = await controller._serializeResponseBody(apiModel, {});
|
|
247
|
-
expect(result).toEqual({id: 1, createdAt:
|
|
252
|
+
expect(result).toEqual({id: 1, createdAt: seconds});
|
|
248
253
|
});
|
|
249
254
|
|
|
250
255
|
it ('passes the options argument through to toApiResponse', async () => {
|
|
@@ -278,24 +283,24 @@ describe('VidaServerController', () => {
|
|
|
278
283
|
expect(controller.requestedFormat).toBe('json');
|
|
279
284
|
});
|
|
280
285
|
|
|
281
|
-
it ('
|
|
286
|
+
it ('ignores an xml Accept header and defaults to json', () => {
|
|
282
287
|
const controller = new FooController({headers: {accept: 'application/xml'}}, {});
|
|
283
|
-
expect(controller.requestedFormat).toBe('
|
|
288
|
+
expect(controller.requestedFormat).toBe('json');
|
|
284
289
|
});
|
|
285
290
|
|
|
286
|
-
it ('
|
|
291
|
+
it ('honors the _format param regardless of the Accept header', () => {
|
|
287
292
|
const controller = new FooController({query: {_format: 'json'}, headers: {accept: 'application/xml'}}, {});
|
|
288
293
|
expect(controller.requestedFormat).toBe('json');
|
|
289
294
|
});
|
|
290
295
|
|
|
291
|
-
it ('defaults to json when
|
|
296
|
+
it ('defaults to json when the param does not indicate a format', () => {
|
|
292
297
|
const controller = new FooController({}, {});
|
|
293
298
|
expect(controller.requestedFormat).toBe('json');
|
|
294
299
|
});
|
|
295
300
|
|
|
296
|
-
it ('
|
|
301
|
+
it ('defaults to json for an unrecognized _format, ignoring the Accept header', () => {
|
|
297
302
|
const controller = new FooController({query: {_format: 'yaml'}, headers: {accept: 'application/xml'}}, {});
|
|
298
|
-
expect(controller.requestedFormat).toBe('
|
|
303
|
+
expect(controller.requestedFormat).toBe('json');
|
|
299
304
|
});
|
|
300
305
|
});
|
|
301
306
|
|
|
@@ -329,7 +334,7 @@ describe('VidaServerController', () => {
|
|
|
329
334
|
describe('#render format dispatch', () => {
|
|
330
335
|
let response;
|
|
331
336
|
beforeEach(() => {
|
|
332
|
-
response =
|
|
337
|
+
response = buildResponseMock();
|
|
333
338
|
});
|
|
334
339
|
|
|
335
340
|
it ('renders JSON via response.json for a JSON request', async () => {
|
|
@@ -367,6 +372,18 @@ describe('VidaServerController', () => {
|
|
|
367
372
|
expect(sent).toContain('<status>ok</status>');
|
|
368
373
|
});
|
|
369
374
|
|
|
375
|
+
it ('wraps the body under a custom root element when xmlRootElement is overridden', async () => {
|
|
376
|
+
class CustomRootController extends FooController {
|
|
377
|
+
get xmlRootElement() { return 'Response'; }
|
|
378
|
+
}
|
|
379
|
+
const controller = new CustomRootController({query: {_format: 'xml'}}, response);
|
|
380
|
+
await controller.render({foo: 'bar'});
|
|
381
|
+
const sent = response.send.mock.calls[0][0];
|
|
382
|
+
expect(sent).toContain('<Response>');
|
|
383
|
+
expect(sent).toContain('</Response>');
|
|
384
|
+
expect(sent).not.toContain('<response>');
|
|
385
|
+
});
|
|
386
|
+
|
|
370
387
|
it ('renders array items as separate elements instead of concatenated text', async () => {
|
|
371
388
|
const controller = new FooController({query: {_format: 'xml'}}, response);
|
|
372
389
|
await controller.render({tags: ['one', 'two']});
|
|
@@ -384,6 +401,49 @@ describe('VidaServerController', () => {
|
|
|
384
401
|
});
|
|
385
402
|
|
|
386
403
|
|
|
404
|
+
describe('#render content-type', () => {
|
|
405
|
+
let response;
|
|
406
|
+
beforeEach(() => {
|
|
407
|
+
response = buildResponseMock();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const defaultTypeCases = [
|
|
411
|
+
['JSON', {}, 'application/json; charset=utf-8'],
|
|
412
|
+
['XML', {query: {_format: 'xml'}}, 'application/xml; charset=utf-8'],
|
|
413
|
+
];
|
|
414
|
+
it.each(defaultTypeCases)('sets the default %s content-type when none is set', async (_label, params, expected) => {
|
|
415
|
+
const controller = new FooController(params, response);
|
|
416
|
+
await controller.render({foo: 'bar'});
|
|
417
|
+
expect(response.setHeader).toHaveBeenCalledWith('content-type', expected);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it ('sets the default text content-type when none is set for a string body', async () => {
|
|
421
|
+
const controller = new FooController({}, response);
|
|
422
|
+
await controller.render('hello');
|
|
423
|
+
expect(response.setHeader).toHaveBeenCalledWith('content-type', 'text/plain; charset=utf-8');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
const preexistingTypeCases = [
|
|
428
|
+
['JSON', {}],
|
|
429
|
+
['XML', {query: {_format: 'xml'}}],
|
|
430
|
+
['string', {}],
|
|
431
|
+
];
|
|
432
|
+
it.each(preexistingTypeCases)('does not override an already-set content-type for a %s render', async (label, params) => {
|
|
433
|
+
const customType = TestHelpers.Faker.Text.randomString();
|
|
434
|
+
response.setHeader('content-type', customType);
|
|
435
|
+
response.setHeader.mockClear();
|
|
436
|
+
|
|
437
|
+
const controller = new FooController(params, response);
|
|
438
|
+
const body = label === 'string' ? 'hello' : {foo: 'bar'};
|
|
439
|
+
await controller.render(body);
|
|
440
|
+
|
|
441
|
+
expect(response.setHeader).not.toHaveBeenCalledWith('content-type', expect.anything());
|
|
442
|
+
expect(response.getHeader('content-type')).toBe(customType);
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
|
|
387
447
|
describe('#_serializeArrayForResponseBody', () => {
|
|
388
448
|
class FakeRecord {
|
|
389
449
|
constructor(id) { this.id = id; }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/***************************************************************************************************
|
|
2
|
+
* MOCKS
|
|
3
|
+
***************************************************************************************************/
|
|
4
|
+
// A response double that keeps a real header store so getHeader reflects what setHeader wrote.
|
|
5
|
+
// This lets tests exercise the renderer's "don't override an already-set content type" logic.
|
|
6
|
+
function buildResponseMock(overrides = {}) {
|
|
7
|
+
const headers = {};
|
|
8
|
+
const response = {
|
|
9
|
+
json: jest.fn(),
|
|
10
|
+
send: jest.fn(),
|
|
11
|
+
setHeader: jest.fn((name, value) => { headers[`${name}`.toLowerCase()] = value; }),
|
|
12
|
+
getHeader: jest.fn((name) => headers[`${name}`.toLowerCase()]),
|
|
13
|
+
statusCode: 200,
|
|
14
|
+
};
|
|
15
|
+
return Object.assign(response, overrides);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
module.exports = { buildResponseMock };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { FooController } = require('./helpers/controllers/fooController');
|
|
2
2
|
const Errors = require('../../lib/server/errors');
|
|
3
3
|
const { randomPayload } = require('./helpers/serverController');
|
|
4
|
+
const { buildResponseMock } = require('./helpers/response');
|
|
4
5
|
const TestHelpers = require('@vida-global/test-helpers');
|
|
5
6
|
const {
|
|
6
7
|
VidaServerController,
|
|
@@ -338,7 +339,7 @@ describe('VidaServerController', () => {
|
|
|
338
339
|
});
|
|
339
340
|
|
|
340
341
|
it ('awaits render before calling response.end (regression: ERR_HTTP_HEADERS_SENT)', async () => {
|
|
341
|
-
const response = {
|
|
342
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
342
343
|
const controller = new FooController({}, response);
|
|
343
344
|
controller.testAction = jest.fn(async () => ({foo: TestHelpers.Faker.Text.randomString()}));
|
|
344
345
|
|
|
@@ -360,7 +361,7 @@ describe('VidaServerController', () => {
|
|
|
360
361
|
|
|
361
362
|
beforeEach(() => {
|
|
362
363
|
request = {headers: randomPayload()};
|
|
363
|
-
response = {headers: randomPayload(), statusCode: TestHelpers.Faker.Math.randomNumber()
|
|
364
|
+
response = buildResponseMock({headers: randomPayload(), statusCode: TestHelpers.Faker.Math.randomNumber()});
|
|
364
365
|
request.headers['content-type'] = TestHelpers.Faker.Text.randomString();
|
|
365
366
|
controller = new FooController(request, response);
|
|
366
367
|
});
|
|
@@ -524,7 +525,7 @@ describe('VidaServerController', () => {
|
|
|
524
525
|
namedBefore() { order.push('before2') }
|
|
525
526
|
testAction() { order.push('action'); return {} }
|
|
526
527
|
}
|
|
527
|
-
const response = {
|
|
528
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
528
529
|
const controller = new C({}, response);
|
|
529
530
|
|
|
530
531
|
await controller.performRequest('testAction');
|
|
@@ -541,7 +542,7 @@ describe('VidaServerController', () => {
|
|
|
541
542
|
namedAfter() { order.push('after2') }
|
|
542
543
|
testAction() { order.push('action'); return {} }
|
|
543
544
|
}
|
|
544
|
-
const response = {
|
|
545
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
545
546
|
const controller = new C({}, response);
|
|
546
547
|
|
|
547
548
|
await controller.performRequest('testAction');
|
|
@@ -554,7 +555,7 @@ describe('VidaServerController', () => {
|
|
|
554
555
|
setupCallbacks() { this.beforeCallback(() => false) }
|
|
555
556
|
}
|
|
556
557
|
C.prototype.testAction = testAction;
|
|
557
|
-
const response = {
|
|
558
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
558
559
|
const controller = new C({}, response);
|
|
559
560
|
|
|
560
561
|
await controller.performRequest('testAction');
|
|
@@ -570,7 +571,7 @@ describe('VidaServerController', () => {
|
|
|
570
571
|
}
|
|
571
572
|
testAction() { return {} }
|
|
572
573
|
}
|
|
573
|
-
const response = {
|
|
574
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
574
575
|
const controller = new C({}, response);
|
|
575
576
|
|
|
576
577
|
await controller.performRequest('testAction');
|
|
@@ -586,7 +587,7 @@ describe('VidaServerController', () => {
|
|
|
586
587
|
}
|
|
587
588
|
testAction() { return {} }
|
|
588
589
|
}
|
|
589
|
-
const response = {
|
|
590
|
+
const response = buildResponseMock({ end: jest.fn() });
|
|
590
591
|
const controller = new C({}, response);
|
|
591
592
|
|
|
592
593
|
await controller.performRequest('testAction');
|