@natlibfi/melinda-rest-api-commons 6.1.0-alpha.0 → 6.2.0-alpha.0

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.
Files changed (92) hide show
  1. package/.github/workflows/melinda-node-tests-and-publish.yml +4 -2
  2. package/README.md +53 -5
  3. package/dist/amqp.d.ts +41 -0
  4. package/dist/amqp.d.ts.map +1 -0
  5. package/dist/amqp.js +237 -235
  6. package/dist/amqp.js.map +1 -7
  7. package/dist/constants.d.ts +49 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +38 -38
  10. package/dist/constants.js.map +1 -7
  11. package/dist/helpers/conversion.d.ts +8 -0
  12. package/dist/helpers/conversion.d.ts.map +1 -0
  13. package/dist/helpers/conversion.js +70 -64
  14. package/dist/helpers/conversion.js.map +1 -7
  15. package/dist/helpers/fix-constants.d.ts +76 -0
  16. package/dist/helpers/fix-constants.d.ts.map +1 -0
  17. package/dist/helpers/fix-constants.js +41 -41
  18. package/dist/helpers/fix-constants.js.map +1 -7
  19. package/dist/helpers/fix-handle-tempurns.d.ts +3 -0
  20. package/dist/helpers/fix-handle-tempurns.d.ts.map +1 -0
  21. package/dist/helpers/fix-handle-tempurns.js +73 -71
  22. package/dist/helpers/fix-handle-tempurns.js.map +1 -7
  23. package/dist/helpers/fix-strip-f884s.d.ts +10 -0
  24. package/dist/helpers/fix-strip-f884s.d.ts.map +1 -0
  25. package/dist/helpers/fix-strip-f884s.js +161 -163
  26. package/dist/helpers/fix-strip-f884s.js.map +1 -7
  27. package/dist/helpers/fix.d.ts +6 -0
  28. package/dist/helpers/fix.d.ts.map +1 -0
  29. package/dist/helpers/fix.js +133 -121
  30. package/dist/helpers/fix.js.map +1 -7
  31. package/dist/helpers/validation.d.ts +9 -0
  32. package/dist/helpers/validation.d.ts.map +1 -0
  33. package/dist/helpers/validation.js +14 -16
  34. package/dist/helpers/validation.js.map +1 -7
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -7
  39. package/dist/mongo.d.ts +165 -0
  40. package/dist/mongo.d.ts.map +1 -0
  41. package/dist/mongo.js +595 -547
  42. package/dist/mongo.js.map +1 -7
  43. package/dist/mongoError.d.ts +2 -0
  44. package/dist/mongoError.d.ts.map +1 -0
  45. package/dist/mongoError.js +19 -0
  46. package/dist/mongoError.js.map +1 -0
  47. package/dist/mongoLog.d.ts +50 -0
  48. package/dist/mongoLog.d.ts.map +1 -0
  49. package/dist/mongoLog.js +274 -242
  50. package/dist/mongoLog.js.map +1 -7
  51. package/dist/utils.d.ts +36 -0
  52. package/dist/utils.d.ts.map +1 -0
  53. package/dist/utils.js +112 -110
  54. package/dist/utils.js.map +1 -7
  55. package/eslint.config.ts +66 -0
  56. package/package.json +15 -11
  57. package/{tests/amqp-error-branches.test.js → test/amqp-error-branches.test.ts} +68 -52
  58. package/{tests/amqp.healthtest.js → test/amqp.healthtest.ts} +7 -3
  59. package/{tests/amqp.tests.localtest.js → test/amqp.tests.localtest.ts} +54 -35
  60. package/{tests/helpers/conversion.test.js → test/helpers/conversion.test.ts} +26 -14
  61. package/{tests/helpers/fix-edge.test.js → test/helpers/fix-edge.test.ts} +16 -12
  62. package/{tests/helpers/fix.test.js → test/helpers/fix.test.ts} +14 -8
  63. package/{tests/helpers/validation.test.js → test/helpers/validation.test.ts} +7 -6
  64. package/{tests/mongo-add-blob-size.test.js → test/mongo-add-blob-size.test.ts} +14 -15
  65. package/{tests/mongo-check-and-check-state.test.js → test/mongo-check-and-check-state.test.ts} +6 -8
  66. package/{tests/mongo-check-and-set-import-job-state.test.js → test/mongo-check-and-set-import-job-state.test.ts} +6 -9
  67. package/{tests/mongo-check-timeout.test.js → test/mongo-check-timeout.test.ts} +6 -9
  68. package/{tests/mongo-create-bulk.test.js → test/mongo-create-bulk.test.ts} +6 -7
  69. package/{tests/mongo-create-prio.test.js → test/mongo-create-prio.test.ts} +6 -6
  70. package/{tests/mongo-error-branches.test.js → test/mongo-error-branches.test.ts} +147 -51
  71. package/{tests/mongo-get-one.test.js → test/mongo-get-one.test.ts} +6 -6
  72. package/{tests/mongo-get-stream.test.js → test/mongo-get-stream.test.ts} +6 -8
  73. package/{tests/mongo-push-ids.test.js → test/mongo-push-ids.test.ts} +6 -6
  74. package/{tests/mongo-push-messages.test.js → test/mongo-push-messages.test.ts} +6 -6
  75. package/{tests/mongo-query-by-id.test.js → test/mongo-query-by-id.test.ts} +6 -9
  76. package/{tests/mongo-query.test.js → test/mongo-query.test.ts} +6 -7
  77. package/{tests/mongo-read-content.test.js → test/mongo-read-content.test.ts} +6 -9
  78. package/{tests/mongo-remove.test.js → test/mongo-remove.test.ts} +6 -8
  79. package/{tests/mongo-set-blob-size.test.js → test/mongo-set-blob-size.test.ts} +6 -6
  80. package/{tests/mongo-set-import-job-state.test.js → test/mongo-set-import-job-state.test.ts} +6 -6
  81. package/{tests/mongo-set-operation.test.js → test/mongo-set-operation.test.ts} +6 -6
  82. package/{tests/mongo-set-operations.test.js → test/mongo-set-operations.test.ts} +6 -6
  83. package/{tests/mongo-set-state.test.js → test/mongo-set-state.test.ts} +6 -6
  84. package/{tests/mongoLog-protect.test.js → test/mongoLog-protect.test.ts} +18 -17
  85. package/{tests/mongoLog.test.js → test/mongoLog.test.ts} +8 -8
  86. package/{tests/testUtils.js → test/testUtils.ts} +49 -15
  87. package/{tests/utils.test.js → test/utils.test.ts} +5 -5
  88. package/tsconfig.json +62 -0
  89. package/tsconfig.test.json +17 -0
  90. package/dist/helpers/fix-utils.js +0 -39
  91. package/dist/helpers/fix-utils.js.map +0 -7
  92. 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.js';
9
- import createMongoLog from '../src/mongoLog.js';
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
- function makeOperator(overrides = {}) {
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
- async function makeMongo(deps) {
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,7 +285,7 @@ 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.MONGO_TIMEOUT_MINUTES = '5';
288
+ process.env['MONGO_TIMEOUT_MINUTES'] = '5';
280
289
  const oldDate = new Date(Date.now() - 4 * 60 * 1000); // 4 min old: fine at 5m, not at 1m
281
290
  const mongo = await makeMongo({
282
291
  operator: makeOperator({
@@ -290,7 +299,7 @@ test('checkTimeOut honours MONGO_TIMEOUT_MINUTES (older item than the window is
290
299
  gridFSBucket: makeGridFSBucket()
291
300
  });
292
301
  const result = await mongo.checkTimeOut({correlationId: 'FOO'});
293
- delete process.env.MONGO_TIMEOUT_MINUTES;
302
+ delete process.env['MONGO_TIMEOUT_MINUTES'];
294
303
  assert.equal(result, true);
295
304
  });
296
305
 
@@ -299,7 +308,7 @@ test('checkTimeOut honours MONGO_TIMEOUT_MINUTES (older item than the window is
299
308
  // (orphan partial chunks). Both are now wired to reject + cleanup.
300
309
 
301
310
  test('createBulk rejects with ApiError 500 when input stream errors mid-upload, and cleans up the partial file', async () => {
302
- const deletedFiles = [];
311
+ const deletedFiles: any[] = [];
303
312
  // errors itself once flowing mode starts
304
313
  const input = new Readable({
305
314
  read() {
@@ -311,8 +320,7 @@ test('createBulk rejects with ApiError 500 when input stream errors mid-upload,
311
320
  gridFSBucket: makeGridFSBucket({
312
321
  openUploadStream: () => new PassThrough(),
313
322
  find: () => ({toArray: async () => [{_id: 'partial-1', filename: 'FOO'}]}),
314
- delete: async (id) => {
315
- // eslint-disable-next-line functional/immutable-data
323
+ delete: async (id: unknown) => {
316
324
  deletedFiles.push(id);
317
325
  return true;
318
326
  }
@@ -331,7 +339,7 @@ test('createBulk rejects with ApiError 500 when input stream errors mid-upload,
331
339
  (error) => {
332
340
  assert.ok(error instanceof ApiError);
333
341
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
334
- assert.ok(error.payload.includes('input stream boom'));
342
+ assert.ok((error.payload as string).includes('input stream boom'));
335
343
  return true;
336
344
  }
337
345
  );
@@ -363,7 +371,7 @@ test('createBulk rejects with ApiError 500 when the GridFS output stream errors
363
371
  (error) => {
364
372
  assert.ok(error instanceof ApiError);
365
373
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
366
- assert.ok(error.payload.includes('disk on fire'));
374
+ assert.ok((error.payload as string).includes('disk on fire'));
367
375
  return true;
368
376
  }
369
377
  );
@@ -375,7 +383,8 @@ test('createBulk rejects with ApiError 500 when the GridFS output stream errors
375
383
  test('pushIds rejects with ApiError 400 when handledIds is not an array', async () => {
376
384
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
377
385
  await assert.rejects(
378
- () => mongo.pushIds({correlationId: 'FOO', handledIds: 'nope', rejectedIds: []}),
386
+ // intentionally malformed input: handledIds must be string[]
387
+ () => mongo.pushIds(asMalformed({correlationId: 'FOO', handledIds: 'nope', rejectedIds: []})),
379
388
  (error) => {
380
389
  assert.ok(error instanceof ApiError);
381
390
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -387,7 +396,8 @@ test('pushIds rejects with ApiError 400 when handledIds is not an array', async
387
396
  test('pushIds rejects with ApiError 400 when rejectedIds is not an array', async () => {
388
397
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
389
398
  await assert.rejects(
390
- () => mongo.pushIds({correlationId: 'FOO', handledIds: [], rejectedIds: undefined}),
399
+ // intentionally malformed input: rejectedIds must be string[]
400
+ () => mongo.pushIds(asMalformed({correlationId: 'FOO', handledIds: [], rejectedIds: undefined})),
391
401
  (error) => {
392
402
  assert.ok(error instanceof ApiError);
393
403
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -408,8 +418,8 @@ test('pushIds rejects with ApiError 500 "Mongo errored" when updateOne throws',
408
418
  (error) => {
409
419
  assert.ok(error instanceof ApiError);
410
420
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
411
- assert.ok(error.payload.includes('Mongo errored'));
412
- assert.ok(error.payload.includes('pushIds boom'));
421
+ assert.ok((error.payload as string).includes('Mongo errored'));
422
+ assert.ok((error.payload as string).includes('pushIds boom'));
413
423
  return true;
414
424
  }
415
425
  );
@@ -418,7 +428,8 @@ test('pushIds rejects with ApiError 500 "Mongo errored" when updateOne throws',
418
428
  test('pushMessages rejects with ApiError 400 when messages is not an array', async () => {
419
429
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
420
430
  await assert.rejects(
421
- () => mongo.pushMessages({correlationId: 'FOO', messages: 'nope'}),
431
+ // intentionally malformed input: messages must be an array
432
+ () => mongo.pushMessages(asMalformed({correlationId: 'FOO', messages: 'nope'})),
422
433
  (error) => {
423
434
  assert.ok(error instanceof ApiError);
424
435
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -439,8 +450,8 @@ test('pushMessages rejects with ApiError 500 "Mongo errored" when updateOne thro
439
450
  (error) => {
440
451
  assert.ok(error instanceof ApiError);
441
452
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
442
- assert.ok(error.payload.includes('Mongo errored'));
443
- assert.ok(error.payload.includes('pushMessages boom'));
453
+ assert.ok((error.payload as string).includes('Mongo errored'));
454
+ assert.ok((error.payload as string).includes('pushMessages boom'));
444
455
  return true;
445
456
  }
446
457
  );
@@ -452,7 +463,8 @@ test('pushMessages rejects with ApiError 500 "Mongo errored" when updateOne thro
452
463
  test('checkAndSetState rejects with ApiError 400 when correlationId is not a string', async () => {
453
464
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
454
465
  await assert.rejects(
455
- () => mongo.checkAndSetState({correlationId: {'$ne': ''}, state: 'VALIDATING'}),
466
+ // intentionally malformed input: correlationId must be a string
467
+ () => mongo.checkAndSetState(asMalformed({correlationId: {'$ne': ''}, state: 'VALIDATING'})),
456
468
  (error) => {
457
469
  assert.ok(error instanceof ApiError);
458
470
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -464,7 +476,8 @@ test('checkAndSetState rejects with ApiError 400 when correlationId is not a str
464
476
  test('getOne rejects with ApiError 400 when queueItemState is not a string', async () => {
465
477
  const mongo = await makeMongo({operator: makeOperator({findOne: async () => null}), gridFSBucket: makeGridFSBucket()});
466
478
  await assert.rejects(
467
- () => mongo.getOne({queueItemState: {'$ne': ''}}),
479
+ // intentionally malformed input: queueItemState must be a string
480
+ () => mongo.getOne(asMalformed({queueItemState: {'$ne': ''}})),
468
481
  (error) => {
469
482
  assert.ok(error instanceof ApiError);
470
483
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -476,7 +489,8 @@ test('getOne rejects with ApiError 400 when queueItemState is not a string', asy
476
489
  test('getOne rejects with ApiError 400 when importJobState is not an object', async () => {
477
490
  const mongo = await makeMongo({operator: makeOperator({findOne: async () => null}), gridFSBucket: makeGridFSBucket()});
478
491
  await assert.rejects(
479
- () => mongo.getOne({importJobState: 'IN_QUEUE'}),
492
+ // intentionally malformed input: importJobState must be an object
493
+ () => mongo.getOne(asMalformed({importJobState: 'IN_QUEUE'})),
480
494
  (error) => {
481
495
  assert.ok(error instanceof ApiError);
482
496
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -510,10 +524,9 @@ test('getOne rejects with ApiError 400 when an importJobState value is not a str
510
524
  });
511
525
 
512
526
  test('getOne queries findOne with a valid importJobState filter', async () => {
513
- const filters = [];
527
+ const filters: any[] = [];
514
528
  const mongo = await makeMongo({
515
- operator: makeOperator({findOne: async (filter) => {
516
- // eslint-disable-next-line functional/immutable-data
529
+ operator: makeOperator({findOne: async (filter: unknown) => {
517
530
  filters.push(filter);
518
531
  return null;
519
532
  }}),
@@ -527,7 +540,8 @@ test('getOne queries findOne with a valid importJobState filter', async () => {
527
540
  test('setBlobSize rejects with ApiError 400 when blobSize is not a number', async () => {
528
541
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
529
542
  await assert.rejects(
530
- () => mongo.setBlobSize({correlationId: 'FOO', blobSize: '7'}),
543
+ // intentionally malformed input: blobSize must be a number
544
+ () => mongo.setBlobSize(asMalformed({correlationId: 'FOO', blobSize: '7'})),
531
545
  (error) => {
532
546
  assert.ok(error instanceof ApiError);
533
547
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -537,10 +551,9 @@ test('setBlobSize rejects with ApiError 400 when blobSize is not a number', asyn
537
551
  });
538
552
 
539
553
  test('setBlobSize stores a real number when blobSize is a number', async () => {
540
- const updates = [];
554
+ const updates: any[] = [];
541
555
  const mongo = await makeMongo({
542
- operator: makeOperator({findOneAndUpdate: async (filter, update) => {
543
- // eslint-disable-next-line functional/immutable-data
556
+ operator: makeOperator({findOneAndUpdate: async (filter: unknown, update: unknown) => {
544
557
  updates.push({filter, update});
545
558
  return {correlationId: 'FOO', blobSize: 7};
546
559
  }}),
@@ -555,7 +568,8 @@ test('setBlobSize stores a real number when blobSize is a number', async () => {
555
568
  test('pushMessages rejects with ApiError 400 when messageField is not a string', async () => {
556
569
  const mongo = await makeMongo({operator: makeOperator(), gridFSBucket: makeGridFSBucket()});
557
570
  await assert.rejects(
558
- () => mongo.pushMessages({correlationId: 'FOO', messages: ['m1'], messageField: 42}),
571
+ // intentionally malformed input: messageField must be a string
572
+ () => mongo.pushMessages(asMalformed({correlationId: 'FOO', messages: ['m1'], messageField: 42})),
559
573
  (error) => {
560
574
  assert.ok(error instanceof ApiError);
561
575
  assert.equal(error.status, httpStatus.BAD_REQUEST);
@@ -588,7 +602,7 @@ test('createBulk (stream): insertOne with acknowledged=false throws ApiError 500
588
602
  (error) => {
589
603
  assert.ok(error instanceof ApiError);
590
604
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
591
- assert.ok(error.payload.includes('Mongo errored'));
605
+ assert.ok((error.payload as string).includes('Mongo errored'));
592
606
  return true;
593
607
  }
594
608
  );
@@ -625,8 +639,8 @@ test('createBulk (stream): a failing GridFS cleanup does not mask the original u
625
639
  (error) => {
626
640
  assert.ok(error instanceof ApiError);
627
641
  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'));
642
+ assert.ok((error.payload as string).includes('input stream boom'));
643
+ assert.ok(!(error.payload as string).includes('gridfs cleanup boom'));
630
644
  return true;
631
645
  }
632
646
  );
@@ -644,7 +658,7 @@ test('remove: non-MongoDriverError from deleteOne throws ApiError 500 with the e
644
658
  (error) => {
645
659
  assert.ok(error instanceof ApiError);
646
660
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
647
- assert.ok(error.payload.includes('plain delete boom'));
661
+ assert.ok((error.payload as string).includes('plain delete boom'));
648
662
  return true;
649
663
  }
650
664
  );
@@ -663,18 +677,62 @@ test('setOperation rejects with ApiError 404 when findOneAndUpdate returns null'
663
677
  (error) => {
664
678
  assert.ok(error instanceof ApiError);
665
679
  assert.equal(error.status, httpStatus.NOT_FOUND);
666
- assert.ok(error.payload.message.includes('not found'));
680
+ assert.ok((error.payload as {message: string}).message.includes('not found'));
667
681
  return true;
668
682
  }
669
683
  );
670
684
  });
671
685
 
686
+ // Q-3: previously-unwrapped public methods now reject with ApiError 500
687
+ // ("Mongo errored: ...") when the driver fails, instead of leaking the raw
688
+ // MongoDriverError. Non-driver errors (400/404 ApiErrors) still pass
689
+ // through unchanged.
690
+
691
+ test('mongo query: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
692
+ const mongo = await makeMongo({
693
+ operator: makeOperator({find: () => {
694
+ throw new MongoDriverError('query driver boom');
695
+ }}),
696
+ gridFSBucket: makeGridFSBucket()
697
+ });
698
+ await assert.rejects(
699
+ () => mongo.query({}),
700
+ mongoErrored500('query driver boom')
701
+ );
702
+ });
703
+
704
+ test('mongo queryById: MongoDriverError from findOne rejects with ApiError 500 "Mongo errored"', async () => {
705
+ const mongo = await makeMongo({
706
+ operator: makeOperator({findOne: async () => {
707
+ throw new MongoDriverError('queryById driver boom');
708
+ }}),
709
+ gridFSBucket: makeGridFSBucket()
710
+ });
711
+ await assert.rejects(
712
+ () => mongo.queryById({correlationId: 'FOO'}),
713
+ mongoErrored500('queryById driver boom')
714
+ );
715
+ });
716
+
717
+ test('mongo checkTimeOut: MongoDriverError from findOne rejects with ApiError 500 "Mongo errored"', async () => {
718
+ const mongo = await makeMongo({
719
+ operator: makeOperator({findOne: async () => {
720
+ throw new MongoDriverError('checkTimeOut driver boom');
721
+ }}),
722
+ gridFSBucket: makeGridFSBucket()
723
+ });
724
+ await assert.rejects(
725
+ () => mongo.checkTimeOut({correlationId: 'FOO'}),
726
+ mongoErrored500('checkTimeOut driver boom')
727
+ );
728
+ });
729
+
672
730
  // mongoLog: same deps-seam convention as mongo (factory last param). When
673
731
  // deps.collection is provided no real connection is made and every
674
732
  // db.collection('logs') call routes to the injected object. These tests
675
733
  // cover the catch blocks that re-wrap driver failures as ApiError 500.
676
734
 
677
- function makeLogCollection(overrides = {}) {
735
+ function makeLogCollection(overrides: Record<string, any> = {}) {
678
736
  return {
679
737
  insertOne: async () => ({acknowledged: true, insertedId: 1}),
680
738
  updateMany: async () => ({acknowledged: true, matchedCount: 0, modifiedCount: 0}),
@@ -683,16 +741,19 @@ function makeLogCollection(overrides = {}) {
683
741
  };
684
742
  }
685
743
 
686
- async function makeMongoLog(deps) {
744
+ /**
745
+ * @param deps the injected stub collection
746
+ */
747
+ async function makeMongoLog(deps: Record<string, any>) {
687
748
  return createMongoLog(DUMMY_URI, 'rest-api', deps);
688
749
  }
689
750
 
690
- function mongoErrored500(text) {
691
- return (error) => {
751
+ function mongoErrored500(text: string) {
752
+ return (error: unknown) => {
692
753
  assert.ok(error instanceof ApiError);
693
754
  assert.equal(error.status, httpStatus.INTERNAL_SERVER_ERROR);
694
- assert.ok(error.payload.includes('Mongo errored'));
695
- assert.ok(error.payload.includes(text));
755
+ assert.ok((error.payload as string).includes('Mongo errored'));
756
+ assert.ok((error.payload as string).includes(text));
696
757
  return true;
697
758
  };
698
759
  }
@@ -723,6 +784,42 @@ test('mongoLog addLogItem: insertOne rejecting throws ApiError 500 with "Mongo e
723
784
  );
724
785
  });
725
786
 
787
+ test('mongoLog query: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
788
+ const mongoLog = await makeMongoLog({
789
+ collection: makeLogCollection({find: () => {
790
+ throw new MongoDriverError('mongoLog query driver boom');
791
+ }})
792
+ });
793
+ await assert.rejects(
794
+ () => mongoLog.query({}),
795
+ mongoErrored500('mongoLog query driver boom')
796
+ );
797
+ });
798
+
799
+ test('mongoLog queryById: MongoDriverError from find rejects with ApiError 500 "Mongo errored"', async () => {
800
+ const mongoLog = await makeMongoLog({
801
+ collection: makeLogCollection({find: () => {
802
+ throw new MongoDriverError('mongoLog queryById driver boom');
803
+ }})
804
+ });
805
+ await assert.rejects(
806
+ () => mongoLog.queryById('FOO'),
807
+ mongoErrored500('mongoLog queryById driver boom')
808
+ );
809
+ });
810
+
811
+ test('mongoLog getListOfCatalogers: MongoDriverError from distinct rejects with ApiError 500 "Mongo errored"', async () => {
812
+ const mongoLog = await makeMongoLog({
813
+ collection: makeLogCollection({distinct: async () => {
814
+ throw new MongoDriverError('mongoLog distinct driver boom');
815
+ }})
816
+ });
817
+ await assert.rejects(
818
+ () => mongoLog.getListOfCatalogers(),
819
+ mongoErrored500('mongoLog distinct driver boom')
820
+ );
821
+ });
822
+
726
823
  test('mongoLog protect: updateMany rejecting throws ApiError 500 with "Mongo errored" message', async () => {
727
824
  const mongoLog = await makeMongoLog({
728
825
  collection: makeLogCollection({updateMany: async () => {
@@ -772,11 +869,10 @@ test('mongoLog removeBySequences: deleteMany rejecting throws ApiError 500 with
772
869
  });
773
870
 
774
871
  test('mongoLog protect: injected collection routes the update and returns its result', async () => {
775
- const filters = [];
872
+ const filters: any[] = [];
776
873
  const result = {acknowledged: true, matchedCount: 1, modifiedCount: 1};
777
874
  const mongoLog = await makeMongoLog({
778
- collection: makeLogCollection({updateMany: async (filter) => {
779
- // eslint-disable-next-line functional/immutable-data
875
+ collection: makeLogCollection({updateMany: async (filter: unknown) => {
780
876
  filters.push(filter);
781
877
  return result;
782
878
  }})
@@ -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.js';
7
+ import {getMongoOperator, handleError} from './testUtils.ts';
8
8
 
9
9
 
10
- let mongoFixtures; // eslint-disable-line functional/no-let
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.js';
7
+ import {getMongoOperator, handleError, streamToString} from './testUtils.ts';
8
8
 
9
- let mongoFixtures; // eslint-disable-line functional/no-let
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
- // eslint-disable-next-line max-statements
51
- async function callback({
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.js';
6
+ import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.ts';
7
7
 
8
8
 
9
- let mongoFixtures; // eslint-disable-line functional/no-let
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.js';
7
+ import {getMongoOperator, handleError, compareToFirstDbEntry} from './testUtils.ts';
8
8
 
9
- let mongoFixtures; // eslint-disable-line functional/no-let
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;