@natlibfi/melinda-rest-api-commons 6.1.0-alpha.0 → 6.2.0-alpha.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/.github/workflows/melinda-node-tests-and-publish.yml +4 -2
- package/README.md +211 -14
- package/dist/amqp.d.ts +41 -0
- package/dist/amqp.d.ts.map +1 -0
- package/dist/amqp.js +251 -235
- package/dist/amqp.js.map +1 -7
- package/dist/constants.d.ts +49 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +38 -38
- package/dist/constants.js.map +1 -7
- package/dist/helpers/conversion.d.ts +8 -0
- package/dist/helpers/conversion.d.ts.map +1 -0
- package/dist/helpers/conversion.js +70 -64
- package/dist/helpers/conversion.js.map +1 -7
- package/dist/helpers/fix-constants.d.ts +76 -0
- package/dist/helpers/fix-constants.d.ts.map +1 -0
- package/dist/helpers/fix-constants.js +41 -41
- package/dist/helpers/fix-constants.js.map +1 -7
- package/dist/helpers/fix-handle-tempurns.d.ts +3 -0
- package/dist/helpers/fix-handle-tempurns.d.ts.map +1 -0
- package/dist/helpers/fix-handle-tempurns.js +73 -71
- package/dist/helpers/fix-handle-tempurns.js.map +1 -7
- package/dist/helpers/fix-strip-f884s.d.ts +10 -0
- package/dist/helpers/fix-strip-f884s.d.ts.map +1 -0
- package/dist/helpers/fix-strip-f884s.js +161 -163
- package/dist/helpers/fix-strip-f884s.js.map +1 -7
- package/dist/helpers/fix.d.ts +6 -0
- package/dist/helpers/fix.d.ts.map +1 -0
- package/dist/helpers/fix.js +133 -121
- package/dist/helpers/fix.js.map +1 -7
- package/dist/helpers/validation.d.ts +9 -0
- package/dist/helpers/validation.d.ts.map +1 -0
- package/dist/helpers/validation.js +14 -16
- package/dist/helpers/validation.js.map +1 -7
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -7
- package/dist/mongo.d.ts +165 -0
- package/dist/mongo.d.ts.map +1 -0
- package/dist/mongo.js +595 -547
- package/dist/mongo.js.map +1 -7
- package/dist/mongoError.d.ts +2 -0
- package/dist/mongoError.d.ts.map +1 -0
- package/dist/mongoError.js +19 -0
- package/dist/mongoError.js.map +1 -0
- package/dist/mongoLog.d.ts +50 -0
- package/dist/mongoLog.d.ts.map +1 -0
- package/dist/mongoLog.js +274 -242
- package/dist/mongoLog.js.map +1 -7
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +112 -110
- package/dist/utils.js.map +1 -7
- package/eslint.config.ts +66 -0
- package/package.json +17 -11
- package/{tests/amqp-error-branches.test.js → test/amqp-error-branches.test.ts} +135 -61
- package/{tests/amqp.healthtest.js → test/amqp.healthtest.ts} +19 -11
- package/{tests/amqp.tests.localtest.js → test/amqp.tests.localtest.ts} +54 -35
- package/{tests/helpers/conversion.test.js → test/helpers/conversion.test.ts} +26 -14
- package/{tests/helpers/fix-edge.test.js → test/helpers/fix-edge.test.ts} +16 -12
- package/{tests/helpers/fix.test.js → test/helpers/fix.test.ts} +14 -8
- package/{tests/helpers/validation.test.js → test/helpers/validation.test.ts} +7 -6
- package/{tests/mongo-add-blob-size.test.js → test/mongo-add-blob-size.test.ts} +14 -15
- package/{tests/mongo-check-and-check-state.test.js → test/mongo-check-and-check-state.test.ts} +6 -8
- package/{tests/mongo-check-and-set-import-job-state.test.js → test/mongo-check-and-set-import-job-state.test.ts} +6 -9
- package/{tests/mongo-check-timeout.test.js → test/mongo-check-timeout.test.ts} +6 -9
- package/{tests/mongo-create-bulk.test.js → test/mongo-create-bulk.test.ts} +6 -7
- package/{tests/mongo-create-prio.test.js → test/mongo-create-prio.test.ts} +6 -6
- package/{tests/mongo-error-branches.test.js → test/mongo-error-branches.test.ts} +166 -65
- package/{tests/mongo-get-one.test.js → test/mongo-get-one.test.ts} +6 -6
- package/{tests/mongo-get-stream.test.js → test/mongo-get-stream.test.ts} +6 -8
- package/{tests/mongo-push-ids.test.js → test/mongo-push-ids.test.ts} +6 -6
- package/{tests/mongo-push-messages.test.js → test/mongo-push-messages.test.ts} +6 -6
- package/{tests/mongo-query-by-id.test.js → test/mongo-query-by-id.test.ts} +6 -9
- package/{tests/mongo-query.test.js → test/mongo-query.test.ts} +6 -7
- package/{tests/mongo-read-content.test.js → test/mongo-read-content.test.ts} +6 -9
- package/{tests/mongo-remove.test.js → test/mongo-remove.test.ts} +6 -8
- package/{tests/mongo-set-blob-size.test.js → test/mongo-set-blob-size.test.ts} +6 -6
- package/{tests/mongo-set-import-job-state.test.js → test/mongo-set-import-job-state.test.ts} +6 -6
- package/{tests/mongo-set-operation.test.js → test/mongo-set-operation.test.ts} +6 -6
- package/{tests/mongo-set-operations.test.js → test/mongo-set-operations.test.ts} +6 -6
- package/{tests/mongo-set-state.test.js → test/mongo-set-state.test.ts} +6 -6
- package/{tests/mongoLog-protect.test.js → test/mongoLog-protect.test.ts} +18 -17
- package/{tests/mongoLog.test.js → test/mongoLog.test.ts} +8 -8
- package/{tests/testUtils.js → test/testUtils.ts} +49 -15
- package/{tests/utils.test.js → test/utils.test.ts} +5 -5
- package/tsconfig.json +62 -0
- package/tsconfig.test.json +17 -0
- package/dist/helpers/fix-utils.js +0 -39
- package/dist/helpers/fix-utils.js.map +0 -7
- package/eslint.config.js +0 -53
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
|
|
2
1
|
import assert from 'node:assert';
|
|
3
2
|
import {test} from 'node:test';
|
|
4
3
|
import {Readable, PassThrough} from 'node:stream';
|
|
5
4
|
import httpStatus from 'http-status';
|
|
6
5
|
import {MongoDriverError} from 'mongodb';
|
|
7
6
|
import {Error as ApiError} from '@natlibfi/melinda-commons';
|
|
8
|
-
import createMongo from '../src/mongo.
|
|
9
|
-
import createMongoLog from '../src/mongoLog.
|
|
7
|
+
import createMongo from '../src/mongo.ts';
|
|
8
|
+
import createMongoLog from '../src/mongoLog.ts';
|
|
10
9
|
|
|
11
10
|
const DUMMY_URI = 'mongodb://stub:27017/rest-api';
|
|
12
11
|
const COLLECTION = 'foobar';
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
// the deps seam takes unknown-typed dependencies, so plain stub objects
|
|
14
|
+
// are fine to inject (the driver types are never exercised here)
|
|
15
|
+
|
|
16
|
+
function makeOperator(overrides: Record<string, any> = {}) {
|
|
15
17
|
return {
|
|
16
18
|
insertOne: async () => ({acknowledged: true, insertedId: 1}),
|
|
17
19
|
deleteOne: async () => ({acknowledged: true, deletedCount: 1}),
|
|
@@ -19,7 +21,7 @@ function makeOperator(overrides = {}) {
|
|
|
19
21
|
};
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
function makeGridFSBucket(overrides = {}) {
|
|
24
|
+
function makeGridFSBucket(overrides: Record<string, any> = {}) {
|
|
23
25
|
return {
|
|
24
26
|
find: () => ({toArray: async () => []}),
|
|
25
27
|
openDownloadStreamByName: () => {
|
|
@@ -29,10 +31,18 @@ function makeGridFSBucket(overrides = {}) {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
/**
|
|
35
|
+
* @param deps the injected stub operator/gridFSBucket
|
|
36
|
+
*/
|
|
37
|
+
async function makeMongo(deps: Record<string, any>) {
|
|
33
38
|
return createMongo(DUMMY_URI, COLLECTION, 'rest-api', deps);
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
// The 400-guard tests pass intentionally malformed inputs (non-string
|
|
42
|
+
// correlationId, non-array ids, ...) that break the operator param types on
|
|
43
|
+
// purpose; this helper bypasses the type check for those objects
|
|
44
|
+
const asMalformed = <T>(params: any): T => params as T;
|
|
45
|
+
|
|
36
46
|
test('createPrio: insertOne with acknowledged=false throws ApiError 500', async () => {
|
|
37
47
|
const mongo = await makeMongo({
|
|
38
48
|
operator: makeOperator({insertOne: async () => ({acknowledged: false})}),
|
|
@@ -60,7 +70,7 @@ test('createPrio: insertOne rejecting throws ApiError 500 with "Mongo errored" m
|
|
|
60
70
|
(error) => {
|
|
61
71
|
assert.ok(error instanceof ApiError);
|
|
62
72
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
63
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
73
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
64
74
|
return true;
|
|
65
75
|
}
|
|
66
76
|
);
|
|
@@ -105,13 +115,12 @@ test('remove: MongoDriverError "File not found" deletes queueItem and reports wh
|
|
|
105
115
|
});
|
|
106
116
|
|
|
107
117
|
test('remove: returns {removed: true, deletedContent: true} when the queueItem and its GridFS file both exist', async () => {
|
|
108
|
-
const deletedFiles = [];
|
|
118
|
+
const deletedFiles: any[] = [];
|
|
109
119
|
const mongo = await makeMongo({
|
|
110
120
|
operator: makeOperator({deleteOne: async () => ({acknowledged: true, deletedCount: 1})}),
|
|
111
121
|
gridFSBucket: makeGridFSBucket({
|
|
112
122
|
find: () => ({toArray: async () => [{_id: 'file-1', filename: 'FOO'}]}),
|
|
113
|
-
delete: async (id) => {
|
|
114
|
-
// eslint-disable-next-line functional/immutable-data
|
|
123
|
+
delete: async (id: unknown) => {
|
|
115
124
|
deletedFiles.push(id);
|
|
116
125
|
return true;
|
|
117
126
|
}
|
|
@@ -147,7 +156,7 @@ test('remove: MongoDriverError with other message throws ApiError 500', async ()
|
|
|
147
156
|
(error) => {
|
|
148
157
|
assert.ok(error instanceof ApiError);
|
|
149
158
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
150
|
-
assert.ok(error.payload.includes('some other driver error'));
|
|
159
|
+
assert.ok((error.payload as string).includes('some other driver error'));
|
|
151
160
|
return true;
|
|
152
161
|
}
|
|
153
162
|
);
|
|
@@ -190,7 +199,7 @@ test('getStream rejects with ApiError 500 when openDownloadStreamByName throws',
|
|
|
190
199
|
(error) => {
|
|
191
200
|
assert.ok(error instanceof ApiError);
|
|
192
201
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
193
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
202
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
194
203
|
return true;
|
|
195
204
|
}
|
|
196
205
|
);
|
|
@@ -276,22 +285,27 @@ test('createBulk (stream) returns the normalized {correlationId, acknowledged, q
|
|
|
276
285
|
});
|
|
277
286
|
|
|
278
287
|
test('checkTimeOut honours MONGO_TIMEOUT_MINUTES (older item than the window is aborted)', async () => {
|
|
279
|
-
process.env
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
288
|
+
process.env['MONGO_TIMEOUT_MINUTES'] = '5';
|
|
289
|
+
try {
|
|
290
|
+
const oldDate = new Date(Date.now() - 4 * 60 * 1000); // 4 min old: fine at 5m, not at 1m
|
|
291
|
+
const mongo = await makeMongo({
|
|
292
|
+
operator: makeOperator({
|
|
293
|
+
findOne: async () => ({
|
|
294
|
+
correlationId: 'FOO',
|
|
295
|
+
queueItemState: 'VALIDATING',
|
|
296
|
+
modificationTime: oldDate,
|
|
297
|
+
importJobState: {CREATE: 'DONE', UPDATE: 'DONE', FIX: 'DONE'}
|
|
298
|
+
})
|
|
299
|
+
}),
|
|
300
|
+
gridFSBucket: makeGridFSBucket()
|
|
301
|
+
});
|
|
302
|
+
const result = await mongo.checkTimeOut({correlationId: 'FOO'});
|
|
303
|
+
assert.equal(result, true);
|
|
304
|
+
} finally {
|
|
305
|
+
// restore the env even on failure so a later operator does not inherit a
|
|
306
|
+
// 5-minute timeout window
|
|
307
|
+
delete process.env['MONGO_TIMEOUT_MINUTES'];
|
|
308
|
+
}
|
|
295
309
|
});
|
|
296
310
|
|
|
297
311
|
// createBulk stream path used to have no error handling on the GridFS
|
|
@@ -299,7 +313,7 @@ test('checkTimeOut honours MONGO_TIMEOUT_MINUTES (older item than the window is
|
|
|
299
313
|
// (orphan partial chunks). Both are now wired to reject + cleanup.
|
|
300
314
|
|
|
301
315
|
test('createBulk rejects with ApiError 500 when input stream errors mid-upload, and cleans up the partial file', async () => {
|
|
302
|
-
const deletedFiles = [];
|
|
316
|
+
const deletedFiles: any[] = [];
|
|
303
317
|
// errors itself once flowing mode starts
|
|
304
318
|
const input = new Readable({
|
|
305
319
|
read() {
|
|
@@ -311,8 +325,7 @@ test('createBulk rejects with ApiError 500 when input stream errors mid-upload,
|
|
|
311
325
|
gridFSBucket: makeGridFSBucket({
|
|
312
326
|
openUploadStream: () => new PassThrough(),
|
|
313
327
|
find: () => ({toArray: async () => [{_id: 'partial-1', filename: 'FOO'}]}),
|
|
314
|
-
delete: async (id) => {
|
|
315
|
-
// eslint-disable-next-line functional/immutable-data
|
|
328
|
+
delete: async (id: unknown) => {
|
|
316
329
|
deletedFiles.push(id);
|
|
317
330
|
return true;
|
|
318
331
|
}
|
|
@@ -331,7 +344,7 @@ test('createBulk rejects with ApiError 500 when input stream errors mid-upload,
|
|
|
331
344
|
(error) => {
|
|
332
345
|
assert.ok(error instanceof ApiError);
|
|
333
346
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
334
|
-
assert.ok(error.payload.includes('input stream boom'));
|
|
347
|
+
assert.ok((error.payload as string).includes('input stream boom'));
|
|
335
348
|
return true;
|
|
336
349
|
}
|
|
337
350
|
);
|
|
@@ -363,7 +376,7 @@ test('createBulk rejects with ApiError 500 when the GridFS output stream errors
|
|
|
363
376
|
(error) => {
|
|
364
377
|
assert.ok(error instanceof ApiError);
|
|
365
378
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
366
|
-
assert.ok(error.payload.includes('disk on fire'));
|
|
379
|
+
assert.ok((error.payload as string).includes('disk on fire'));
|
|
367
380
|
return true;
|
|
368
381
|
}
|
|
369
382
|
);
|
|
@@ -375,7 +388,8 @@ test('createBulk rejects with ApiError 500 when the GridFS output stream errors
|
|
|
375
388
|
test('pushIds rejects with ApiError 400 when handledIds is not an array', async () => {
|
|
376
389
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
377
390
|
await assert.rejects(
|
|
378
|
-
|
|
391
|
+
// intentionally malformed input: handledIds must be string[]
|
|
392
|
+
() => mongo.pushIds(asMalformed({correlationId: 'FOO', handledIds: 'nope', rejectedIds: []})),
|
|
379
393
|
(error) => {
|
|
380
394
|
assert.ok(error instanceof ApiError);
|
|
381
395
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -387,7 +401,8 @@ test('pushIds rejects with ApiError 400 when handledIds is not an array', async
|
|
|
387
401
|
test('pushIds rejects with ApiError 400 when rejectedIds is not an array', async () => {
|
|
388
402
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
389
403
|
await assert.rejects(
|
|
390
|
-
|
|
404
|
+
// intentionally malformed input: rejectedIds must be string[]
|
|
405
|
+
() => mongo.pushIds(asMalformed({correlationId: 'FOO', handledIds: [], rejectedIds: undefined})),
|
|
391
406
|
(error) => {
|
|
392
407
|
assert.ok(error instanceof ApiError);
|
|
393
408
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -408,8 +423,8 @@ test('pushIds rejects with ApiError 500 "Mongo errored" when updateOne throws',
|
|
|
408
423
|
(error) => {
|
|
409
424
|
assert.ok(error instanceof ApiError);
|
|
410
425
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
411
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
412
|
-
assert.ok(error.payload.includes('pushIds boom'));
|
|
426
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
427
|
+
assert.ok((error.payload as string).includes('pushIds boom'));
|
|
413
428
|
return true;
|
|
414
429
|
}
|
|
415
430
|
);
|
|
@@ -418,7 +433,8 @@ test('pushIds rejects with ApiError 500 "Mongo errored" when updateOne throws',
|
|
|
418
433
|
test('pushMessages rejects with ApiError 400 when messages is not an array', async () => {
|
|
419
434
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
420
435
|
await assert.rejects(
|
|
421
|
-
|
|
436
|
+
// intentionally malformed input: messages must be an array
|
|
437
|
+
() => mongo.pushMessages(asMalformed({correlationId: 'FOO', messages: 'nope'})),
|
|
422
438
|
(error) => {
|
|
423
439
|
assert.ok(error instanceof ApiError);
|
|
424
440
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -439,8 +455,8 @@ test('pushMessages rejects with ApiError 500 "Mongo errored" when updateOne thro
|
|
|
439
455
|
(error) => {
|
|
440
456
|
assert.ok(error instanceof ApiError);
|
|
441
457
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
442
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
443
|
-
assert.ok(error.payload.includes('pushMessages boom'));
|
|
458
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
459
|
+
assert.ok((error.payload as string).includes('pushMessages boom'));
|
|
444
460
|
return true;
|
|
445
461
|
}
|
|
446
462
|
);
|
|
@@ -452,7 +468,8 @@ test('pushMessages rejects with ApiError 500 "Mongo errored" when updateOne thro
|
|
|
452
468
|
test('checkAndSetState rejects with ApiError 400 when correlationId is not a string', async () => {
|
|
453
469
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
454
470
|
await assert.rejects(
|
|
455
|
-
|
|
471
|
+
// intentionally malformed input: correlationId must be a string
|
|
472
|
+
() => mongo.checkAndSetState(asMalformed({correlationId: {'$ne': ''}, state: 'VALIDATING'})),
|
|
456
473
|
(error) => {
|
|
457
474
|
assert.ok(error instanceof ApiError);
|
|
458
475
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -464,7 +481,8 @@ test('checkAndSetState rejects with ApiError 400 when correlationId is not a str
|
|
|
464
481
|
test('getOne rejects with ApiError 400 when queueItemState is not a string', async () => {
|
|
465
482
|
const mongo = await makeMongo({operator: makeOperator({findOne: async () => null}), gridFSBucket: makeGridFSBucket()});
|
|
466
483
|
await assert.rejects(
|
|
467
|
-
|
|
484
|
+
// intentionally malformed input: queueItemState must be a string
|
|
485
|
+
() => mongo.getOne(asMalformed({queueItemState: {'$ne': ''}})),
|
|
468
486
|
(error) => {
|
|
469
487
|
assert.ok(error instanceof ApiError);
|
|
470
488
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -476,7 +494,8 @@ test('getOne rejects with ApiError 400 when queueItemState is not a string', asy
|
|
|
476
494
|
test('getOne rejects with ApiError 400 when importJobState is not an object', async () => {
|
|
477
495
|
const mongo = await makeMongo({operator: makeOperator({findOne: async () => null}), gridFSBucket: makeGridFSBucket()});
|
|
478
496
|
await assert.rejects(
|
|
479
|
-
|
|
497
|
+
// intentionally malformed input: importJobState must be an object
|
|
498
|
+
() => mongo.getOne(asMalformed({importJobState: 'IN_QUEUE'})),
|
|
480
499
|
(error) => {
|
|
481
500
|
assert.ok(error instanceof ApiError);
|
|
482
501
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -510,10 +529,9 @@ test('getOne rejects with ApiError 400 when an importJobState value is not a str
|
|
|
510
529
|
});
|
|
511
530
|
|
|
512
531
|
test('getOne queries findOne with a valid importJobState filter', async () => {
|
|
513
|
-
const filters = [];
|
|
532
|
+
const filters: any[] = [];
|
|
514
533
|
const mongo = await makeMongo({
|
|
515
|
-
operator: makeOperator({findOne: async (filter) => {
|
|
516
|
-
// eslint-disable-next-line functional/immutable-data
|
|
534
|
+
operator: makeOperator({findOne: async (filter: unknown) => {
|
|
517
535
|
filters.push(filter);
|
|
518
536
|
return null;
|
|
519
537
|
}}),
|
|
@@ -527,7 +545,8 @@ test('getOne queries findOne with a valid importJobState filter', async () => {
|
|
|
527
545
|
test('setBlobSize rejects with ApiError 400 when blobSize is not a number', async () => {
|
|
528
546
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
529
547
|
await assert.rejects(
|
|
530
|
-
|
|
548
|
+
// intentionally malformed input: blobSize must be a number
|
|
549
|
+
() => mongo.setBlobSize(asMalformed({correlationId: 'FOO', blobSize: '7'})),
|
|
531
550
|
(error) => {
|
|
532
551
|
assert.ok(error instanceof ApiError);
|
|
533
552
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -537,10 +556,9 @@ test('setBlobSize rejects with ApiError 400 when blobSize is not a number', asyn
|
|
|
537
556
|
});
|
|
538
557
|
|
|
539
558
|
test('setBlobSize stores a real number when blobSize is a number', async () => {
|
|
540
|
-
const updates = [];
|
|
559
|
+
const updates: any[] = [];
|
|
541
560
|
const mongo = await makeMongo({
|
|
542
|
-
operator: makeOperator({findOneAndUpdate: async (filter, update) => {
|
|
543
|
-
// eslint-disable-next-line functional/immutable-data
|
|
561
|
+
operator: makeOperator({findOneAndUpdate: async (filter: unknown, update: unknown) => {
|
|
544
562
|
updates.push({filter, update});
|
|
545
563
|
return {correlationId: 'FOO', blobSize: 7};
|
|
546
564
|
}}),
|
|
@@ -555,7 +573,8 @@ test('setBlobSize stores a real number when blobSize is a number', async () => {
|
|
|
555
573
|
test('pushMessages rejects with ApiError 400 when messageField is not a string', async () => {
|
|
556
574
|
const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
|
|
557
575
|
await assert.rejects(
|
|
558
|
-
|
|
576
|
+
// intentionally malformed input: messageField must be a string
|
|
577
|
+
() => mongo.pushMessages(asMalformed({correlationId: 'FOO', messages: ['m1'], messageField: 42})),
|
|
559
578
|
(error) => {
|
|
560
579
|
assert.ok(error instanceof ApiError);
|
|
561
580
|
assert.equal(error.status, httpStatus.BAD_REQUEST);
|
|
@@ -588,7 +607,7 @@ test('createBulk (stream): insertOne with acknowledged=false throws ApiError 500
|
|
|
588
607
|
(error) => {
|
|
589
608
|
assert.ok(error instanceof ApiError);
|
|
590
609
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
591
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
610
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
592
611
|
return true;
|
|
593
612
|
}
|
|
594
613
|
);
|
|
@@ -625,8 +644,8 @@ test('createBulk (stream): a failing GridFS cleanup does not mask the original u
|
|
|
625
644
|
(error) => {
|
|
626
645
|
assert.ok(error instanceof ApiError);
|
|
627
646
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
628
|
-
assert.ok(error.payload.includes('input stream boom'));
|
|
629
|
-
assert.ok(!error.payload.includes('gridfs cleanup boom'));
|
|
647
|
+
assert.ok((error.payload as string).includes('input stream boom'));
|
|
648
|
+
assert.ok(!(error.payload as string).includes('gridfs cleanup boom'));
|
|
630
649
|
return true;
|
|
631
650
|
}
|
|
632
651
|
);
|
|
@@ -644,7 +663,7 @@ test('remove: non-MongoDriverError from deleteOne throws ApiError 500 with the e
|
|
|
644
663
|
(error) => {
|
|
645
664
|
assert.ok(error instanceof ApiError);
|
|
646
665
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
647
|
-
assert.ok(error.payload.includes('plain delete boom'));
|
|
666
|
+
assert.ok((error.payload as string).includes('plain delete boom'));
|
|
648
667
|
return true;
|
|
649
668
|
}
|
|
650
669
|
);
|
|
@@ -663,18 +682,62 @@ test('setOperation rejects with ApiError 404 when findOneAndUpdate returns null'
|
|
|
663
682
|
(error) => {
|
|
664
683
|
assert.ok(error instanceof ApiError);
|
|
665
684
|
assert.equal(error.status, httpStatus.NOT_FOUND);
|
|
666
|
-
assert.ok(error.payload.message.includes('not found'));
|
|
685
|
+
assert.ok((error.payload as {message: string}).message.includes('not found'));
|
|
667
686
|
return true;
|
|
668
687
|
}
|
|
669
688
|
);
|
|
670
689
|
});
|
|
671
690
|
|
|
691
|
+
// Q-3: previously-unwrapped public methods now reject with ApiError 500
|
|
692
|
+
// ("Mongo errored: ...") when the driver fails, instead of leaking the raw
|
|
693
|
+
// MongoDriverError. Non-driver errors (400/404 ApiErrors) still pass
|
|
694
|
+
// through unchanged.
|
|
695
|
+
|
|
696
|
+
test('mongo query: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
|
|
697
|
+
const mongo = await makeMongo({
|
|
698
|
+
operator: makeOperator({find: () => {
|
|
699
|
+
throw new MongoDriverError('query driver boom');
|
|
700
|
+
}}),
|
|
701
|
+
gridFSBucket: makeGridFSBucket()
|
|
702
|
+
});
|
|
703
|
+
await assert.rejects(
|
|
704
|
+
() => mongo.query({}),
|
|
705
|
+
mongoErrored500('query driver boom')
|
|
706
|
+
);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
test('mongo queryById: MongoDriverError from findOne rejects with ApiError 500 "Mongo errored"', async () => {
|
|
710
|
+
const mongo = await makeMongo({
|
|
711
|
+
operator: makeOperator({findOne: async () => {
|
|
712
|
+
throw new MongoDriverError('queryById driver boom');
|
|
713
|
+
}}),
|
|
714
|
+
gridFSBucket: makeGridFSBucket()
|
|
715
|
+
});
|
|
716
|
+
await assert.rejects(
|
|
717
|
+
() => mongo.queryById({correlationId: 'FOO'}),
|
|
718
|
+
mongoErrored500('queryById driver boom')
|
|
719
|
+
);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
test('mongo checkTimeOut: MongoDriverError from findOne rejects with ApiError 500 "Mongo errored"', async () => {
|
|
723
|
+
const mongo = await makeMongo({
|
|
724
|
+
operator: makeOperator({findOne: async () => {
|
|
725
|
+
throw new MongoDriverError('checkTimeOut driver boom');
|
|
726
|
+
}}),
|
|
727
|
+
gridFSBucket: makeGridFSBucket()
|
|
728
|
+
});
|
|
729
|
+
await assert.rejects(
|
|
730
|
+
() => mongo.checkTimeOut({correlationId: 'FOO'}),
|
|
731
|
+
mongoErrored500('checkTimeOut driver boom')
|
|
732
|
+
);
|
|
733
|
+
});
|
|
734
|
+
|
|
672
735
|
// mongoLog: same deps-seam convention as mongo (factory last param). When
|
|
673
736
|
// deps.collection is provided no real connection is made and every
|
|
674
737
|
// db.collection('logs') call routes to the injected object. These tests
|
|
675
738
|
// cover the catch blocks that re-wrap driver failures as ApiError 500.
|
|
676
739
|
|
|
677
|
-
function makeLogCollection(overrides = {}) {
|
|
740
|
+
function makeLogCollection(overrides: Record<string, any> = {}) {
|
|
678
741
|
return {
|
|
679
742
|
insertOne: async () => ({acknowledged: true, insertedId: 1}),
|
|
680
743
|
updateMany: async () => ({acknowledged: true, matchedCount: 0, modifiedCount: 0}),
|
|
@@ -683,16 +746,19 @@ function makeLogCollection(overrides = {}) {
|
|
|
683
746
|
};
|
|
684
747
|
}
|
|
685
748
|
|
|
686
|
-
|
|
749
|
+
/**
|
|
750
|
+
* @param deps the injected stub collection
|
|
751
|
+
*/
|
|
752
|
+
async function makeMongoLog(deps: Record<string, any>) {
|
|
687
753
|
return createMongoLog(DUMMY_URI, 'rest-api', deps);
|
|
688
754
|
}
|
|
689
755
|
|
|
690
|
-
function mongoErrored500(text) {
|
|
691
|
-
return (error) => {
|
|
756
|
+
function mongoErrored500(text: string) {
|
|
757
|
+
return (error: unknown) => {
|
|
692
758
|
assert.ok(error instanceof ApiError);
|
|
693
759
|
assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
|
|
694
|
-
assert.ok(error.payload.includes('Mongo errored'));
|
|
695
|
-
assert.ok(error.payload.includes(text));
|
|
760
|
+
assert.ok((error.payload as string).includes('Mongo errored'));
|
|
761
|
+
assert.ok((error.payload as string).includes(text));
|
|
696
762
|
return true;
|
|
697
763
|
};
|
|
698
764
|
}
|
|
@@ -723,6 +789,42 @@ test('mongoLog addLogItem: insertOne rejecting throws ApiError 500 with "Mongo e
|
|
|
723
789
|
);
|
|
724
790
|
});
|
|
725
791
|
|
|
792
|
+
test('mongoLog query: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
|
|
793
|
+
const mongoLog = await makeMongoLog({
|
|
794
|
+
collection: makeLogCollection({find: () => {
|
|
795
|
+
throw new MongoDriverError('mongoLog query driver boom');
|
|
796
|
+
}})
|
|
797
|
+
});
|
|
798
|
+
await assert.rejects(
|
|
799
|
+
() => mongoLog.query({}),
|
|
800
|
+
mongoErrored500('mongoLog query driver boom')
|
|
801
|
+
);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
test('mongoLog queryById: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
|
|
805
|
+
const mongoLog = await makeMongoLog({
|
|
806
|
+
collection: makeLogCollection({find: () => {
|
|
807
|
+
throw new MongoDriverError('mongoLog queryById driver boom');
|
|
808
|
+
}})
|
|
809
|
+
});
|
|
810
|
+
await assert.rejects(
|
|
811
|
+
() => mongoLog.queryById('FOO'),
|
|
812
|
+
mongoErrored500('mongoLog queryById driver boom')
|
|
813
|
+
);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
test('mongoLog getListOfCatalogers: MongoDriverError from distinct rejects with ApiError 500 "Mongo errored"', async () => {
|
|
817
|
+
const mongoLog = await makeMongoLog({
|
|
818
|
+
collection: makeLogCollection({distinct: async () => {
|
|
819
|
+
throw new MongoDriverError('mongoLog distinct driver boom');
|
|
820
|
+
}})
|
|
821
|
+
});
|
|
822
|
+
await assert.rejects(
|
|
823
|
+
() => mongoLog.getListOfCatalogers(),
|
|
824
|
+
mongoErrored500('mongoLog distinct driver boom')
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
|
|
726
828
|
test('mongoLog protect: updateMany rejecting throws ApiError 500 with "Mongo errored" message', async () => {
|
|
727
829
|
const mongoLog = await makeMongoLog({
|
|
728
830
|
collection: makeLogCollection({updateMany: async () => {
|
|
@@ -772,11 +874,10 @@ test('mongoLog removeBySequences: deleteMany rejecting throws ApiError 500 with
|
|
|
772
874
|
});
|
|
773
875
|
|
|
774
876
|
test('mongoLog protect: injected collection routes the update and returns its result', async () => {
|
|
775
|
-
const filters = [];
|
|
877
|
+
const filters: any[] = [];
|
|
776
878
|
const result = {acknowledged: true, matchedCount: 1, modifiedCount: 1};
|
|
777
879
|
const mongoLog = await makeMongoLog({
|
|
778
|
-
collection: makeLogCollection({updateMany: async (filter) => {
|
|
779
|
-
// eslint-disable-next-line functional/immutable-data
|
|
880
|
+
collection: makeLogCollection({updateMany: async (filter: unknown) => {
|
|
780
881
|
filters.push(filter);
|
|
781
882
|
return result;
|
|
782
883
|
}})
|
|
@@ -4,10 +4,10 @@ import mongoFixturesFactory from '@natlibfi/fixura-mongo';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen';
|
|
5
5
|
import createDebugLogger from 'debug';
|
|
6
6
|
//import {handleError, compareToFirstDbEntry, compareToDbEntry, formatQueueItem, streamToString} from './testUtils.js';
|
|
7
|
-
import {getMongoOperator, handleError} from './testUtils.
|
|
7
|
+
import {getMongoOperator, handleError} from './testUtils.ts';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
let mongoFixtures;
|
|
10
|
+
let mongoFixtures: any;
|
|
11
11
|
const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons/mongo:get-one:test');
|
|
12
12
|
|
|
13
13
|
generateTests({
|
|
@@ -41,14 +41,14 @@ generateTests({
|
|
|
41
41
|
|
|
42
42
|
async function initMongofixtures() {
|
|
43
43
|
mongoFixtures = await mongoFixturesFactory({
|
|
44
|
-
recurse: false,
|
|
45
44
|
rootPath: [import.meta.dirname, '..', 'test-fixtures', 'mongo', 'get-one'],
|
|
46
45
|
gridFS: {bucketName: 'foobar'},
|
|
47
46
|
useObjectId: true
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
async function callback({
|
|
50
|
+
async function callback(callbackParams: Record<string, any>) {
|
|
51
|
+
const {
|
|
52
52
|
getFixture,
|
|
53
53
|
functionName,
|
|
54
54
|
params,
|
|
@@ -57,14 +57,14 @@ async function callback({
|
|
|
57
57
|
expectedErrorMessage = '',
|
|
58
58
|
expectedErrorStatus = '',
|
|
59
59
|
expectedOpResult = undefined
|
|
60
|
-
}
|
|
60
|
+
} = callbackParams;
|
|
61
61
|
|
|
62
62
|
const mongoOperator = await getMongoOperator(mongoFixtures);
|
|
63
63
|
//const expectedResult = await getFixture('expectedResult.json');
|
|
64
64
|
|
|
65
65
|
await doPreFillDb(preFillDb);
|
|
66
66
|
|
|
67
|
-
async function doPreFillDb(preFillDb) {
|
|
67
|
+
async function doPreFillDb(preFillDb: boolean) {
|
|
68
68
|
if (preFillDb) {
|
|
69
69
|
await mongoFixtures.populate(getFixture('dbContents.json'));
|
|
70
70
|
return;
|
|
@@ -4,9 +4,9 @@ import mongoFixturesFactory from '@natlibfi/fixura-mongo';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen';
|
|
5
5
|
import createDebugLogger from 'debug';
|
|
6
6
|
//import {handleError, compareToFirstDbEntry, compareToDbEntry, formatQueueItem, streamToString} from './testUtils.js';
|
|
7
|
-
import {getMongoOperator, handleError, streamToString} from './testUtils.
|
|
7
|
+
import {getMongoOperator, handleError, streamToString} from './testUtils.ts';
|
|
8
8
|
|
|
9
|
-
let mongoFixtures;
|
|
9
|
+
let mongoFixtures: any;
|
|
10
10
|
const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons/mongo:get-stream:test');
|
|
11
11
|
|
|
12
12
|
generateTests({
|
|
@@ -40,15 +40,14 @@ generateTests({
|
|
|
40
40
|
|
|
41
41
|
async function initMongofixtures() {
|
|
42
42
|
mongoFixtures = await mongoFixturesFactory({
|
|
43
|
-
recurse: false,
|
|
44
43
|
rootPath: [import.meta.dirname, '..', 'test-fixtures', 'mongo', 'get-stream'],
|
|
45
44
|
gridFS: {bucketName: 'foobar'},
|
|
46
45
|
useObjectId: true
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
async function callback(callbackParams: Record<string, any>) {
|
|
50
|
+
const {
|
|
52
51
|
getFixture,
|
|
53
52
|
functionName,
|
|
54
53
|
params,
|
|
@@ -58,14 +57,14 @@ async function callback({
|
|
|
58
57
|
expectedErrorStatus = '',
|
|
59
58
|
contentStream = false,
|
|
60
59
|
createBulkParams = undefined
|
|
61
|
-
}
|
|
60
|
+
} = callbackParams;
|
|
62
61
|
|
|
63
62
|
const mongoOperator = await getMongoOperator(mongoFixtures);
|
|
64
63
|
//const expectedResult = await getFixture('expectedResult.json');
|
|
65
64
|
|
|
66
65
|
await doPreFillDb(preFillDb);
|
|
67
66
|
|
|
68
|
-
async function doPreFillDb(preFillDb) {
|
|
67
|
+
async function doPreFillDb(preFillDb: boolean) {
|
|
69
68
|
if (preFillDb) {
|
|
70
69
|
await mongoFixtures.populate(getFixture('dbContents.json'));
|
|
71
70
|
return;
|
|
@@ -80,7 +79,6 @@ async function callback({
|
|
|
80
79
|
debug(JSON.stringify(params));
|
|
81
80
|
//correlationId
|
|
82
81
|
|
|
83
|
-
// eslint-disable-next-line functional/no-conditional-statements
|
|
84
82
|
if (createBulkParams) {
|
|
85
83
|
const stream = contentStream ? await getFixture({components: ['contentStream'], reader: READERS.STREAM}) : createBulkParams.stream;
|
|
86
84
|
//debug(stream);
|
|
@@ -3,10 +3,10 @@ import mongoFixturesFactory from '@natlibfi/fixura-mongo';
|
|
|
3
3
|
import generateTests from '@natlibfi/fixugen';
|
|
4
4
|
import createDebugLogger from 'debug';
|
|
5
5
|
//import {handleError, compareToFirstDbEntry, compareToDbEntry, formatQueueItem, streamToString} from './testUtils.js';
|
|
6
|
-
import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.
|
|
6
|
+
import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.ts';
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
let mongoFixtures;
|
|
9
|
+
let mongoFixtures: any;
|
|
10
10
|
const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons/mongo:push-ids:test');
|
|
11
11
|
|
|
12
12
|
generateTests({
|
|
@@ -40,14 +40,14 @@ generateTests({
|
|
|
40
40
|
|
|
41
41
|
async function initMongofixtures() {
|
|
42
42
|
mongoFixtures = await mongoFixturesFactory({
|
|
43
|
-
recurse: false,
|
|
44
43
|
rootPath: [import.meta.dirname, '..', 'test-fixtures', 'mongo', 'push-ids'],
|
|
45
44
|
gridFS: {bucketName: 'foobar'},
|
|
46
45
|
useObjectId: true
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
async function callback({
|
|
49
|
+
async function callback(callbackParams: Record<string, any>) {
|
|
50
|
+
const {
|
|
51
51
|
getFixture,
|
|
52
52
|
functionName,
|
|
53
53
|
params,
|
|
@@ -56,14 +56,14 @@ async function callback({
|
|
|
56
56
|
expectedToThrow = false,
|
|
57
57
|
expectedErrorMessage = '',
|
|
58
58
|
expectedErrorStatus = ''
|
|
59
|
-
}
|
|
59
|
+
} = callbackParams;
|
|
60
60
|
|
|
61
61
|
const mongoOperator = await getMongoOperator(mongoFixtures);
|
|
62
62
|
const expectedResult = await getFixture('expectedResult.json');
|
|
63
63
|
|
|
64
64
|
await doPreFillDb(preFillDb);
|
|
65
65
|
|
|
66
|
-
async function doPreFillDb(preFillDb) {
|
|
66
|
+
async function doPreFillDb(preFillDb: boolean) {
|
|
67
67
|
if (preFillDb) {
|
|
68
68
|
await mongoFixtures.populate(getFixture('dbContents.json'));
|
|
69
69
|
return;
|
|
@@ -4,9 +4,9 @@ import mongoFixturesFactory from '@natlibfi/fixura-mongo';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen';
|
|
5
5
|
import createDebugLogger from 'debug';
|
|
6
6
|
//import {handleError, compareToFirstDbEntry, compareToDbEntry, formatQueueItem, streamToString} from './testUtils.js';
|
|
7
|
-
import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.
|
|
7
|
+
import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.ts';
|
|
8
8
|
|
|
9
|
-
let mongoFixtures;
|
|
9
|
+
let mongoFixtures: any;
|
|
10
10
|
const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons/mongo:push-messages:test');
|
|
11
11
|
|
|
12
12
|
generateTests({
|
|
@@ -40,14 +40,14 @@ generateTests({
|
|
|
40
40
|
|
|
41
41
|
async function initMongofixtures() {
|
|
42
42
|
mongoFixtures = await mongoFixturesFactory({
|
|
43
|
-
recurse: false,
|
|
44
43
|
rootPath: [import.meta.dirname, '..', 'test-fixtures', 'mongo', 'push-messages'],
|
|
45
44
|
gridFS: {bucketName: 'foobar'},
|
|
46
45
|
useObjectId: true
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
async function callback({
|
|
49
|
+
async function callback(callbackParams: Record<string, any>) {
|
|
50
|
+
const {
|
|
51
51
|
getFixture,
|
|
52
52
|
functionName,
|
|
53
53
|
params,
|
|
@@ -56,14 +56,14 @@ async function callback({
|
|
|
56
56
|
expectedToThrow = false,
|
|
57
57
|
expectedErrorMessage = '',
|
|
58
58
|
expectedErrorStatus = ''
|
|
59
|
-
}
|
|
59
|
+
} = callbackParams;
|
|
60
60
|
|
|
61
61
|
const mongoOperator = await getMongoOperator(mongoFixtures);
|
|
62
62
|
const expectedResult = await getFixture('expectedResult.json');
|
|
63
63
|
|
|
64
64
|
await doPreFillDb(preFillDb);
|
|
65
65
|
|
|
66
|
-
async function doPreFillDb(preFillDb) {
|
|
66
|
+
async function doPreFillDb(preFillDb: boolean) {
|
|
67
67
|
if (preFillDb) {
|
|
68
68
|
await mongoFixtures.populate(getFixture('dbContents.json'));
|
|
69
69
|
return;
|