comq 0.23.0 → 0.24.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.
Files changed (41) hide show
  1. package/discussions/amqp-1.0.md +127 -0
  2. package/features/heartbeat.feature +16 -0
  3. package/features/rpc.call.feature +23 -1
  4. package/features/rpc.call.recovery.feature +2 -23
  5. package/features/rpc.parked.feature +6 -6
  6. package/features/rpc.shards.retransmission.feature +46 -0
  7. package/features/shards.feature +1 -1
  8. package/features/silence.feature +3 -2
  9. package/features/steps/.types/context.d.ts +6 -1
  10. package/features/steps/brokers.js +1 -1
  11. package/features/steps/call.js +90 -30
  12. package/features/steps/connection.js +23 -0
  13. package/features/steps/context.js +1 -2
  14. package/features/steps/events.js +1 -1
  15. package/features/steps/hooks.js +0 -1
  16. package/features/steps/network.js +11 -2
  17. package/features/steps/networks.js +31 -2
  18. package/features/steps/parking.js +5 -5
  19. package/features/steps/retransmission.js +165 -0
  20. package/features/steps/rpc.js +0 -6
  21. package/package.json +20 -4
  22. package/readme.md +46 -46
  23. package/source/channel.js +3 -1
  24. package/source/connection.js +10 -90
  25. package/source/io.js +106 -25
  26. package/source/shards/channel.js +73 -12
  27. package/test/amqplib.mock.js +1 -1
  28. package/test/connection.mock.js +3 -2
  29. package/test/connection.test.js +16 -78
  30. package/test/{io.request.terms.test.js → io.call.terms.test.js} +31 -30
  31. package/test/io.call.test.js +5 -13
  32. package/test/io.reply.test.js +5 -3
  33. package/test/io.request.test.js +277 -2
  34. package/test/memory.test.js +1 -3
  35. package/test/shards/channel.test.js +168 -2
  36. package/types/amqp.d.ts +1 -1
  37. package/types/channel.d.ts +14 -5
  38. package/types/io.d.ts +18 -8
  39. package/features/rpc.timeout.feature +0 -19
  40. package/source/batch.js +0 -62
  41. package/test/batch.test.js +0 -94
@@ -21,7 +21,8 @@ let requests
21
21
  /** @type {jest.MockedObject<comq.Channel>} */
22
22
  let replies
23
23
 
24
- const queue = generate()
24
+ const exchange = generate()
25
+ const key = generate()
25
26
  const payload = { [generate()]: generate() }
26
27
 
27
28
  beforeEach(async () => {
@@ -34,12 +35,12 @@ beforeEach(async () => {
34
35
  // endregion
35
36
 
36
37
  describe('timeout', () => {
37
- it('should expire the Request when the caller stops waiting', async () => {
38
- io.request(queue, payload, { timeout: 10000 }).catch(noop)
38
+ it('should expire the call when the caller stops waiting', async () => {
39
+ io.call(exchange, key, payload, { timeout: 10000 }).catch(noop)
39
40
 
40
41
  await initialized()
41
42
 
42
- const expiration = Number(requests.send.mock.calls[0][2].expiration)
43
+ const expiration = Number(requests.route.mock.calls[0][3].expiration)
43
44
 
44
45
  expect(expiration).toBeGreaterThan(9000)
45
46
  expect(expiration).toBeLessThanOrEqual(10000)
@@ -48,29 +49,29 @@ describe('timeout', () => {
48
49
  it('should keep the encoding', async () => {
49
50
  const encoding = 'application/octet-stream'
50
51
 
51
- io.request(queue, randomBytes(8), { encoding, timeout: 10000 }).catch(noop)
52
+ io.call(exchange, key, randomBytes(8), { encoding, timeout: 10000 }).catch(noop)
52
53
 
53
54
  await initialized()
54
55
 
55
- expect(requests.send.mock.calls[0][2].contentType).toStrictEqual(encoding)
56
+ expect(requests.route.mock.calls[0][3].contentType).toStrictEqual(encoding)
56
57
  })
57
58
 
58
59
  it('should stop waiting while the channels are being created', async () => {
59
60
  connection.createChannel.mockImplementation(() => new Promise(noop))
60
61
 
61
- const promise = io.request(queue, payload, { timeout: 20 })
62
+ const promise = io.call(exchange, key, payload, { timeout: 20 })
62
63
 
63
64
  await expect(promise).rejects.toMatchObject({ name: 'TimeoutError' })
64
65
  })
65
66
 
66
67
  it('should reject once it has passed', async () => {
67
- const promise = io.request(queue, payload, { timeout: 20 })
68
+ const promise = io.call(exchange, key, payload, { timeout: 20 })
68
69
 
69
70
  await expect(promise).rejects.toMatchObject({ name: 'TimeoutError' })
70
71
  })
71
72
 
72
73
  it('should resolve with a reply that arrives in time', async () => {
73
- const promise = io.request(queue, payload, { timeout: 10000 })
74
+ const promise = io.call(exchange, key, payload, { timeout: 10000 })
74
75
  const content = randomBytes(8)
75
76
 
76
77
  await initialized()
@@ -80,7 +81,7 @@ describe('timeout', () => {
80
81
  })
81
82
 
82
83
  it('should re-send with the time that is left', async () => {
83
- io.request(queue, payload, { timeout: 10000 }).catch(noop)
84
+ io.call(exchange, key, payload, { timeout: 10000 }).catch(noop)
84
85
 
85
86
  await initialized()
86
87
  await timeout(20)
@@ -89,10 +90,10 @@ describe('timeout', () => {
89
90
 
90
91
  await immediate()
91
92
 
92
- expect(requests.send).toHaveBeenCalledTimes(2)
93
+ expect(requests.route).toHaveBeenCalledTimes(2)
93
94
 
94
- const first = Number(requests.send.mock.calls[0][2].expiration)
95
- const second = Number(requests.send.mock.calls[1][2].expiration)
95
+ const first = Number(requests.route.mock.calls[0][3].expiration)
96
+ const second = Number(requests.route.mock.calls[1][3].expiration)
96
97
 
97
98
  expect(second).toBeLessThan(first)
98
99
  })
@@ -102,7 +103,7 @@ describe('signal', () => {
102
103
  it('should reject with the reason it was aborted for', async () => {
103
104
  const controller = new AbortController()
104
105
  const reason = new Error(generate())
105
- const promise = io.request(queue, payload, { signal: controller.signal })
106
+ const promise = io.call(exchange, key, payload, { signal: controller.signal })
106
107
 
107
108
  await initialized()
108
109
 
@@ -111,14 +112,14 @@ describe('signal', () => {
111
112
  await expect(promise).rejects.toBe(reason)
112
113
  })
113
114
 
114
- it('should leave the Request in its queue', async () => {
115
+ it('should leave the call in its queue', async () => {
115
116
  const controller = new AbortController()
116
117
 
117
- io.request(queue, payload, { signal: controller.signal }).catch(noop)
118
+ io.call(exchange, key, payload, { signal: controller.signal }).catch(noop)
118
119
 
119
120
  await initialized()
120
121
 
121
- expect(requests.send.mock.calls[0][2].expiration).toBeUndefined()
122
+ expect(requests.route.mock.calls[0][3].expiration).toBeUndefined()
122
123
  })
123
124
 
124
125
  it('should reject at once when aborted before the channels exist', async () => {
@@ -129,27 +130,27 @@ describe('signal', () => {
129
130
 
130
131
  controller.abort(reason)
131
132
 
132
- await expect(io.request(queue, payload, { signal: controller.signal })).rejects.toBe(reason)
133
+ await expect(io.call(exchange, key, payload, { signal: controller.signal })).rejects.toBe(reason)
133
134
  })
134
135
 
135
136
  it('should send nothing once aborted', async () => {
136
137
  const controller = new AbortController()
137
138
 
138
- // the channels exist before the aborted Request is made
139
- io.request(queue, payload).catch(noop)
139
+ // the channels exist before the aborted call is made
140
+ io.call(exchange, key, payload).catch(noop)
140
141
 
141
142
  await initialized()
142
143
 
143
144
  controller.abort()
144
145
 
145
- await expect(io.request(queue, payload, { signal: controller.signal })).rejects.toBeDefined()
146
+ await expect(io.call(exchange, key, payload, { signal: controller.signal })).rejects.toBeDefined()
146
147
 
147
- expect(requests.send).toHaveBeenCalledTimes(1)
148
+ expect(requests.route).toHaveBeenCalledTimes(1)
148
149
  })
149
150
 
150
151
  it('should not re-send what it abandoned', async () => {
151
152
  const controller = new AbortController()
152
- const promise = io.request(queue, payload, { signal: controller.signal })
153
+ const promise = io.call(exchange, key, payload, { signal: controller.signal })
153
154
 
154
155
  await initialized()
155
156
 
@@ -161,19 +162,19 @@ describe('signal', () => {
161
162
 
162
163
  await immediate()
163
164
 
164
- expect(requests.send).toHaveBeenCalledTimes(1)
165
+ expect(requests.route).toHaveBeenCalledTimes(1)
165
166
  })
166
167
 
167
168
  it('should stop waiting for a publication', async () => {
168
- io.request(queue, payload).catch(noop)
169
+ io.call(exchange, key, payload).catch(noop)
169
170
 
170
171
  await initialized()
171
172
 
172
- requests.send.mockImplementationOnce(() => new Promise(noop))
173
+ requests.route.mockImplementationOnce(() => new Promise(noop))
173
174
 
174
175
  const controller = new AbortController()
175
176
  const reason = new Error(generate())
176
- const promise = io.request(queue, payload, { signal: controller.signal })
177
+ const promise = io.call(exchange, key, payload, { signal: controller.signal })
177
178
 
178
179
  await immediate()
179
180
 
@@ -184,7 +185,7 @@ describe('signal', () => {
184
185
 
185
186
  it('should discard a reply that arrives afterwards', async () => {
186
187
  const controller = new AbortController()
187
- const promise = io.request(queue, payload, { signal: controller.signal })
188
+ const promise = io.call(exchange, key, payload, { signal: controller.signal })
188
189
 
189
190
  await initialized()
190
191
 
@@ -197,7 +198,7 @@ describe('signal', () => {
197
198
 
198
199
  it('should end the wait within the timeout', async () => {
199
200
  const controller = new AbortController()
200
- const promise = io.request(queue, payload, { signal: controller.signal, timeout: 20 })
201
+ const promise = io.call(exchange, key, payload, { signal: controller.signal, timeout: 20 })
201
202
 
202
203
  await expect(promise).rejects.toMatchObject({ name: 'TimeoutError' })
203
204
  })
@@ -216,7 +217,7 @@ async function initialized () {
216
217
  * @param {Buffer} [content]
217
218
  */
218
219
  async function answer (index, content = randomBytes(8)) {
219
- const { correlationId } = requests.send.mock.calls[index][2]
220
+ const { correlationId } = requests.route.mock.calls[index][3]
220
221
  const deliver = replies.consume.mock.calls[0][1]
221
222
 
222
223
  await deliver({ content, properties: { correlationId } })
@@ -52,7 +52,7 @@ describe('call', () => {
52
52
 
53
53
  it('should publish to the exchange under the key', async () => {
54
54
  expect(requests.route).toHaveBeenCalledWith(exchange, key, expect.any(Buffer),
55
- expect.objectContaining({ mandatory: true }))
55
+ expect.objectContaining({ mandatory: true }), expect.any(Function))
56
56
  })
57
57
 
58
58
  it('should declare no queue to publish to', async () => {
@@ -98,16 +98,6 @@ describe('call', () => {
98
98
  })
99
99
  })
100
100
 
101
- describe('timeout', () => {
102
- it('should stop waiting while the channels are being created', async () => {
103
- connection.createChannel.mockImplementation(() => new Promise(() => undefined))
104
-
105
- const promise = io.call(exchange, key, payload, { timeout: 20 })
106
-
107
- await expect(promise).rejects.toMatchObject({ name: 'TimeoutError' })
108
- })
109
- })
110
-
111
101
  describe('back', () => {
112
102
  const producer = jest.fn(() => generate())
113
103
 
@@ -126,14 +116,16 @@ describe('back', () => {
126
116
  const contentType = 'application/json'
127
117
  const properties = { contentType, correlationId: generate(), replyTo: generate() }
128
118
 
129
- await consumer({ content: encode(payload, contentType), properties })
119
+ const request = { content: encode(payload, contentType), properties }
120
+
121
+ await consumer(request)
130
122
 
131
123
  replies = await findChannel('reply')
132
124
 
133
125
  expect(producer).toHaveBeenCalledWith(payload)
134
126
 
135
127
  expect(replies.fire).toHaveBeenCalledWith(properties.replyTo, expect.any(Buffer),
136
- expect.objectContaining({ correlationId: properties.correlationId }))
128
+ expect.objectContaining({ correlationId: properties.correlationId }), request)
137
129
  })
138
130
  })
139
131
 
@@ -98,7 +98,8 @@ describe('reply', () => {
98
98
  const reply = await produce.mock.results[0].value
99
99
  const buffer = encode(reply, properties.contentType)
100
100
 
101
- expect(replies.fire).toHaveBeenCalledWith(properties.replyTo, buffer, expect.anything())
101
+ // the request goes along, so that the reply goes back the way it came
102
+ expect(replies.fire).toHaveBeenCalledWith(properties.replyTo, buffer, expect.anything(), message)
102
103
  })
103
104
 
104
105
  it('should throw if producer returned undefined', async () => {
@@ -190,7 +191,7 @@ describe('encoding', () => {
190
191
  mandatory: true
191
192
  }
192
193
 
193
- expect(replies.fire).toHaveBeenCalledWith(properties.replyTo, reply, props)
194
+ expect(replies.fire).toHaveBeenCalledWith(properties.replyTo, reply, props, message)
194
195
  })
195
196
 
196
197
  it('should set octet-stream for Buffer reply', async () => {
@@ -215,7 +216,8 @@ describe('encoding', () => {
215
216
  .toHaveBeenCalledWith(
216
217
  expect.any(String),
217
218
  buffer,
218
- expect.objectContaining({ contentType: 'application/octet-stream' })
219
+ expect.objectContaining({ contentType: 'application/octet-stream' }),
220
+ message
219
221
  )
220
222
  })
221
223
 
@@ -5,6 +5,7 @@
5
5
  const stream = require('node:stream')
6
6
  const { randomBytes } = require('node:crypto')
7
7
  const { generate } = require('randomstring')
8
+ const { Promex } = require('promex')
8
9
  const { immediate } = require('./helpers')
9
10
  const { encode } = require('../source/encode')
10
11
 
@@ -115,6 +116,19 @@ describe('send', () => {
115
116
  expect(call[2]).toMatchObject({ contentType })
116
117
  })
117
118
 
119
+ it('should not expire', async () => {
120
+ expect(call[2].expiration).toBeUndefined()
121
+ })
122
+
123
+ it('should refuse options other than the encoding', async () => {
124
+ requests.send.mockClear()
125
+
126
+ await expect(io.request(queue, payload, { timeout: 1000 })).rejects.toBeInstanceOf(TypeError)
127
+ await expect(io.request(queue, payload, { signal: new AbortController().signal })).rejects.toBeInstanceOf(TypeError)
128
+
129
+ expect(requests.send).not.toHaveBeenCalled()
130
+ })
131
+
118
132
  it('should throw if encoding is not supported', async () => {
119
133
  const encoding = /** @type {comq.Encoding} */ 'wtf/' + generate()
120
134
 
@@ -314,15 +328,276 @@ describe('send', () => {
314
328
  })
315
329
  })
316
330
 
331
+ describe('shards', () => {
332
+ beforeEach(async () => {
333
+ jest.clearAllMocks()
334
+
335
+ connection = mock.connection(true)
336
+ io = new IO(connection)
337
+ })
338
+
339
+ /**
340
+ * Sends a Request through a shard.
341
+ *
342
+ * @param {number} index
343
+ * @param {string} target
344
+ * @return {Promise<void>}
345
+ */
346
+ async function ask (index, target) {
347
+ requests = await requestChannel()
348
+
349
+ requests.send.mockImplementationOnce(async (_queue, _buffer, _options, via) => via(index))
350
+
351
+ io.request(target, payload)
352
+
353
+ await immediate()
354
+ }
355
+
356
+ /**
357
+ * @return {Promise<jest.MockedObject<comq.Channel>>}
358
+ */
359
+ async function requestChannel () {
360
+ if (connection.createChannel.mock.calls.length === 0) {
361
+ // the channels are created with the first Request
362
+ io.request(generate(), payload)
363
+
364
+ await immediate()
365
+ }
366
+
367
+ return findChannel('request')
368
+ }
369
+
370
+ /**
371
+ * @param {jest.MockedObject<comq.Channel>} channel
372
+ * @return {string[]} the queues each publish went to
373
+ */
374
+ const targets = (channel) => channel.send.mock.calls.map(([queue]) => queue)
375
+
376
+ it('should re-send only the requests the lost shard holds', async () => {
377
+ const a = generate()
378
+ const b = generate()
379
+
380
+ await ask(0, a)
381
+ await ask(1, b)
382
+
383
+ requests.send.mockClear()
384
+
385
+ await lose(1)
386
+
387
+ expect(targets(requests)).toStrictEqual([b])
388
+
389
+ requests.send.mockClear()
390
+
391
+ // b has been re-sent through the shard that stays, and is held by it now
392
+ await lose(0)
393
+
394
+ expect(targets(requests)).toContain(a)
395
+ expect(targets(requests)).toContain(b)
396
+ })
397
+
398
+ it('should re-send only the requests of a shard whose reply channel recovers', async () => {
399
+ const a = generate()
400
+ const b = generate()
401
+
402
+ await ask(0, a)
403
+ await ask(1, b)
404
+
405
+ replies = await findChannel('reply')
406
+ requests.send.mockClear()
407
+
408
+ const calls = replies.diagnose.mock.calls.filter((call) => call[0] === 'recover')
409
+
410
+ for (const [, listener] of calls) listener(1)
411
+
412
+ await immediate()
413
+
414
+ expect(targets(requests)).toStrictEqual([b])
415
+ })
416
+
417
+ it('should re-send only the requests of a shard removed from the pool', async () => {
418
+ const a = generate()
419
+ const b = generate()
420
+
421
+ await ask(0, a)
422
+ await ask(1, b)
423
+
424
+ requests.send.mockClear()
425
+
426
+ const calls = requests.diagnose.mock.calls.filter((call) => call[0] === 'remove')
427
+
428
+ for (const [, listener] of calls) listener(0)
429
+
430
+ await immediate()
431
+
432
+ expect(targets(requests).includes(a)).toStrictEqual(true)
433
+ expect(targets(requests).includes(b)).toStrictEqual(false)
434
+ })
435
+
436
+ it('should re-send a request the lost shard held while it was being published', async () => {
437
+ requests = await requestChannel()
438
+
439
+ const published = new Promex()
440
+
441
+ requests.send.mockImplementationOnce(async (_queue, _buffer, _options, via) => {
442
+ via(1)
443
+
444
+ await published
445
+ })
446
+
447
+ const target = generate()
448
+
449
+ io.request(target, payload)
450
+
451
+ await immediate()
452
+ await lose(1)
453
+
454
+ // not before it is published
455
+ expect(targets(requests).filter((queue) => queue === target)).toHaveLength(1)
456
+
457
+ published.resolve()
458
+
459
+ await immediate()
460
+ await immediate()
461
+
462
+ expect(targets(requests).filter((queue) => queue === target)).toHaveLength(2)
463
+ })
464
+
465
+ it('should not re-send a request that failed over to another shard', async () => {
466
+ requests = await requestChannel()
467
+
468
+ const published = new Promex()
469
+
470
+ requests.send.mockImplementationOnce(async (_queue, _buffer, _options, via) => {
471
+ via(1)
472
+
473
+ await published
474
+
475
+ // the pool fails over from the lost shard
476
+ via(0)
477
+ })
478
+
479
+ const target = generate()
480
+
481
+ io.request(target, payload)
482
+
483
+ await immediate()
484
+ await lose(1)
485
+
486
+ published.resolve()
487
+
488
+ await immediate()
489
+ await immediate()
490
+
491
+ expect(targets(requests).filter((queue) => queue === target)).toHaveLength(1)
492
+
493
+ // it is held by the shard it went through
494
+ await lose(0)
495
+
496
+ expect(targets(requests).filter((queue) => queue === target)).toHaveLength(2)
497
+ })
498
+
499
+ it('should not re-send a request that went through another shard than the one lost as it was published', async () => {
500
+ requests = await requestChannel()
501
+
502
+ const published = new Promex()
503
+
504
+ requests.send.mockImplementationOnce(async (_queue, _buffer, _options, via) => {
505
+ via(0)
506
+
507
+ await published
508
+ })
509
+
510
+ const target = generate()
511
+
512
+ io.request(target, payload)
513
+
514
+ await immediate()
515
+ await lose(1)
516
+
517
+ published.resolve()
518
+
519
+ await immediate()
520
+ await immediate()
521
+
522
+ expect(targets(requests).filter((queue) => queue === target)).toHaveLength(1)
523
+ })
524
+
525
+ it('should re-send a returned call from the shard it was published on next', async () => {
526
+ requests = await requestChannel()
527
+
528
+ const key = generate()
529
+
530
+ requests.route.mockImplementationOnce(async (_exchange, _key, _buffer, _options, via) => via(0))
531
+
532
+ io.call(generate(), key, payload)
533
+
534
+ await immediate()
535
+
536
+ const [,,, properties] = requests.route.mock.calls[0]
537
+ const message = { fields: { exchange: generate(), routingKey: key }, properties }
538
+ const calls = requests.diagnose.mock.calls.filter((call) => call[0] === 'reroute')
539
+
540
+ expect(calls.length).toBeGreaterThan(0)
541
+
542
+ for (const [, listener] of calls) listener(message, 1)
543
+
544
+ await lose(0)
545
+
546
+ expect(requests.route).toHaveBeenCalledTimes(1)
547
+
548
+ await lose(1)
549
+
550
+ expect(requests.route).toHaveBeenCalledTimes(2)
551
+ })
552
+
553
+ it('should re-send a call the lost shard holds', async () => {
554
+ requests = await requestChannel()
555
+
556
+ requests.route.mockImplementationOnce(async (_exchange, _key, _buffer, _options, via) => via(1))
557
+
558
+ io.call(generate(), generate(), payload)
559
+
560
+ await immediate()
561
+ await lose(0)
562
+
563
+ expect(requests.route).toHaveBeenCalledTimes(1)
564
+
565
+ await lose(1)
566
+
567
+ expect(requests.route).toHaveBeenCalledTimes(2)
568
+ })
569
+ })
570
+
571
+ describe('recovery', () => {
572
+ it('should re-send every unanswered Request of a connection that is not sharded', async () => {
573
+ const other = generate()
574
+
575
+ io.request(other, payload)
576
+
577
+ await immediate()
578
+
579
+ requests.send.mockClear()
580
+
581
+ const calls = requests.diagnose.mock.calls.filter((call) => call[0] === 'recover')
582
+
583
+ for (const [, listener] of calls) listener()
584
+
585
+ await immediate()
586
+
587
+ expect(requests.send.mock.calls.map(([queue]) => queue).sort()).toStrictEqual([queue, other].sort())
588
+ })
589
+ })
590
+
317
591
  /**
318
592
  * Fires the listeners the IO has attached to the shard loss diagnostic.
319
593
  *
594
+ * @param {number} [index]
320
595
  * @return {Promise<void>}
321
596
  */
322
- async function lose () {
597
+ async function lose (index = 0) {
323
598
  const calls = requests.diagnose.mock.calls.filter((call) => call[0] === 'lost')
324
599
 
325
- for (const [, listener] of calls) listener(0)
600
+ for (const [, listener] of calls) listener(index)
326
601
 
327
602
  await immediate()
328
603
  await immediate()
@@ -12,7 +12,7 @@ const { timeout } = require('./helpers')
12
12
  const mock = require('./amqplib.mock')
13
13
  const { amqplib, connect } = mock
14
14
 
15
- jest.mock('amqplib', () => mock.amqplib)
15
+ jest.mock('@toa.io/amqplib', () => mock.amqplib)
16
16
 
17
17
  const { connect: open, assert } = require('../source')
18
18
  const { SingletonConnection } = require('../source/singleton')
@@ -29,14 +29,12 @@ beforeEach(() => {
29
29
  amqplib.connect.mockImplementation(async (url) => wired(await connect(url)))
30
30
 
31
31
  global.COMQ_TESTING_SHUTDOWN_TIMEOUT = 1
32
- global.COMQ_TESTING_WATCHDOG_INTERVAL = 60_000
33
32
  })
34
33
 
35
34
  afterEach(() => {
36
35
  SingletonConnection.__lets_pretend_this_method_doesnt_exist()
37
36
 
38
37
  delete global.COMQ_TESTING_SHUTDOWN_TIMEOUT
39
- delete global.COMQ_TESTING_WATCHDOG_INTERVAL
40
38
  })
41
39
 
42
40
  afterAll(() => {