comq 0.15.6 → 0.15.8

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 (51) hide show
  1. package/.releaserc +1 -8
  2. package/features/recovery.feature +30 -0
  3. package/features/silence.feature +46 -0
  4. package/features/steps/.types/context.d.ts +17 -0
  5. package/features/steps/backpressure.js +1 -1
  6. package/features/steps/broker.js +1 -1
  7. package/features/steps/brokers.js +1 -1
  8. package/features/steps/common.js +1 -1
  9. package/features/steps/connection.js +41 -1
  10. package/features/steps/context.js +12 -2
  11. package/features/steps/events.js +1 -1
  12. package/features/steps/hooks.js +1 -0
  13. package/features/steps/network.js +50 -0
  14. package/features/steps/networks.js +90 -0
  15. package/features/steps/parameters.js +6 -0
  16. package/features/steps/poison.js +1 -1
  17. package/features/steps/rpc.js +1 -1
  18. package/features/steps/tasks.js +1 -1
  19. package/package.json +3 -6
  20. package/readme.md +21 -6
  21. package/source/attributes/lazy.test.js +1 -1
  22. package/source/attributes/track.test.js +3 -3
  23. package/source/connection.js +137 -12
  24. package/source/emitter.js +23 -1
  25. package/source/encoders/index.js +0 -2
  26. package/source/events.js +2 -2
  27. package/source/io.js +6 -5
  28. package/source/shards/channel.js +34 -17
  29. package/source/shards/connection.js +0 -5
  30. package/test/amqplib.mock.js +16 -4
  31. package/test/channel.fixtures.js +1 -1
  32. package/test/channel.test.js +1 -1
  33. package/test/connection.test.js +176 -6
  34. package/test/decode.test.js +0 -13
  35. package/test/emitter.test.js +71 -0
  36. package/test/encode.test.js +0 -9
  37. package/test/encodings.js +1 -1
  38. package/test/helpers.js +77 -0
  39. package/test/io.close.test.js +4 -3
  40. package/test/io.diagnostics.test.js +2 -2
  41. package/test/io.reply.test.js +2 -2
  42. package/test/io.request.test.js +16 -2
  43. package/test/pipeline.test.js +1 -1
  44. package/test/shards/channel.test.js +97 -9
  45. package/test/shards/connection.test.js +4 -3
  46. package/types/diagnostic.d.ts +2 -2
  47. package/types/encoding.d.ts +1 -2
  48. package/types/io.d.ts +2 -0
  49. package/AGENTS.md +0 -35
  50. package/CHANGELOG.md +0 -76
  51. package/source/encoders/msgpack.js +0 -6
package/.releaserc CHANGED
@@ -5,12 +5,6 @@
5
5
  "plugins": [
6
6
  "@semantic-release/commit-analyzer",
7
7
  "@semantic-release/release-notes-generator",
8
- [
9
- "@semantic-release/changelog",
10
- {
11
- "changelogFile": "CHANGELOG.md"
12
- }
13
- ],
14
8
  [
15
9
  "@semantic-release/npm"
16
10
  ],
@@ -19,8 +13,7 @@
19
13
  {
20
14
  "assets": [
21
15
  "package.json",
22
- "package-lock.json",
23
- "CHANGELOG.md"
16
+ "package-lock.json"
24
17
  ],
25
18
  "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
26
19
  }
@@ -84,6 +84,36 @@ Feature: Request-Reply Topology Recovery
84
84
  5
85
85
  """
86
86
 
87
+ Scenario: Sending another request while the broker is down
88
+
89
+ The reply queue is already being consumed from, so the request is published as soon as the
90
+ request channel recovers, which is before the reply queue has been declared again
91
+
92
+ Given function replying `add_numbers` queue:
93
+ """
94
+ ({ a, b }) => { return a + b }
95
+ """
96
+ When the consumer sends the following request to the `add_numbers` queue:
97
+ """yaml
98
+ a: 1
99
+ b: 2
100
+ """
101
+ Then the consumer receives the reply:
102
+ """yaml
103
+ 3
104
+ """
105
+ When the broker has crashed
106
+ And the consumer sends the following request to the `add_numbers` queue:
107
+ """yaml
108
+ a: 2
109
+ b: 3
110
+ """
111
+ Then the broker is up
112
+ And the consumer receives the reply:
113
+ """yaml
114
+ 5
115
+ """
116
+
87
117
  Scenario: Defining emitter while the broker is down
88
118
  Given the broker is down
89
119
  And `logger` consuming events from the `numbers_added` exchange is expected
@@ -0,0 +1,46 @@
1
+ Feature: Silent Connection Tolerance
2
+
3
+ A connection that has gone silent is what a machine that woke from sleep is
4
+ left with: the socket is open, neither the broker nor the operating system
5
+ reports anything, and nothing but the watchdog tells it from an idle connection.
6
+
7
+ Background:
8
+ Given watchdog interval is set to 2000ms with 60s AMQP heartbeat
9
+ And a network that can go silent
10
+
11
+ Scenario: Restoring a connection that has gone silent
12
+ Given an active connection to the broker
13
+ And a producer replying `echo` queue
14
+ When the network goes silent
15
+ Then the connection is lost within 10 seconds
16
+ When the consumer sends a request to the `echo` queue
17
+ Then the consumer receives the reply
18
+
19
+ Scenario: Restoring a sharded connection whose every shard has gone silent
20
+ Given an active sharded connection
21
+ And a producer replying `echo` queue
22
+ When the network goes silent
23
+ Then the connection is lost within 10 seconds
24
+ When the consumer sends a request to the `echo` queue
25
+ Then the consumer receives the reply
26
+
27
+ Scenario: Answering a request sent as the connection goes silent
28
+ Given an active connection to the broker
29
+ And a producer replying `echo` queue
30
+ When the consumer sends a request to the `echo` queue as the network goes silent
31
+ Then the consumer receives the reply
32
+ And the connection is lost within 10 seconds
33
+
34
+ Scenario: Answering a request sent as every shard goes silent
35
+ Given an active sharded connection
36
+ And a producer replying `echo` queue
37
+ When the consumer sends a request to the `echo` queue as the network goes silent
38
+ Then the consumer receives the reply
39
+ And the connection is lost within 10 seconds
40
+
41
+ Scenario: Reporting that there is nowhere left to publish
42
+ Given an active sharded connection
43
+ And a producer replying `echo` queue
44
+ When the network goes silent
45
+ Then publishing is paused within 10 seconds
46
+ And publishing is resumed within 10 seconds
@@ -1,4 +1,5 @@
1
1
  import { Readable } from 'node:stream'
2
+ import { Socket } from 'node:net'
2
3
  import * as _diagnostics from '../../../types/diagnostic'
3
4
  import * as _io from '../../../types/io'
4
5
  import * as _amqp from '../../../types/amqp'
@@ -34,10 +35,26 @@ declare namespace comq.features {
34
35
  streamsValues: Record<number, any[]>
35
36
  streamsEnded: Record<number, boolean>
36
37
  generatorDestroyed: boolean
38
+ networks: Network[]
37
39
 
38
40
  connect(user?: string, password?: string): Promise<void>
39
41
  assert(user?: string, password?: string): Promise<void>
40
42
  disconnect(): Promise<void>
43
+ unplug(): Promise<void>
44
+ }
45
+
46
+ interface Network {
47
+ readonly address: string
48
+
49
+ open(): Promise<void>
50
+ silence(): void
51
+ close(): Promise<void>
52
+ }
53
+
54
+ interface Tunnel {
55
+ client: Socket
56
+ upstream: Socket
57
+ silent: boolean
41
58
  }
42
59
 
43
60
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const assert = require('node:assert')
4
4
  const { randomBytes } = require('node:crypto')
5
- const { quantity, timeout } = require('@toa.io/generic')
5
+ const { quantity, timeout } = require('../../test/helpers')
6
6
 
7
7
  const { When } = require('@cucumber/cucumber')
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { random } = require('@toa.io/generic')
3
+ const { random } = require('../../test/helpers')
4
4
 
5
5
  const { Given } = require('@cucumber/cucumber')
6
6
  const { BROKERS_AMOUNT, actions } = require('./brokers')
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { execFile } = require('node:child_process')
4
4
  const { promisify } = require('node:util')
5
- const { timeout } = require('@toa.io/generic')
5
+ const { timeout } = require('../../test/helpers')
6
6
  const { RabbitMQContainer } = require('@testcontainers/rabbitmq')
7
7
  const { getContainerRuntimeClient } = require('testcontainers')
8
8
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { When } = require('@cucumber/cucumber')
4
- const { timeout } = require('@toa.io/generic')
4
+ const { timeout } = require('../../test/helpers')
5
5
 
6
6
  When('after {number}ms',
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('node:assert')
4
- const { timeout } = require('@toa.io/generic')
4
+ const { timeout } = require('../../test/helpers')
5
5
 
6
6
  const { Given, When, Then } = require('@cucumber/cucumber')
7
7
 
@@ -167,6 +167,27 @@ Then('the connection is {connection-event}',
167
167
  assert.equal(this.events[event], true, 'connection was not ' + key)
168
168
  })
169
169
 
170
+ Then('the connection is lost within {number} second(s)',
171
+ /**
172
+ * @param {number} seconds
173
+ * @this {comq.features.Context}
174
+ */
175
+ async function (seconds) {
176
+ await emitted.call(this, 'close', seconds, 'connection was not lost')
177
+ })
178
+
179
+ Then('publishing is {paused-event} within {number} second(s)',
180
+ /**
181
+ * @param {'paused' | 'resumed'} key
182
+ * @param {number} seconds
183
+ * @this {comq.features.Context}
184
+ */
185
+ async function (key, seconds) {
186
+ const event = PUBLISHING_EVENTS[key]
187
+
188
+ await emitted.call(this, event, seconds, 'publishing was not ' + key)
189
+ })
190
+
170
191
  Given('the connection has started sealing',
171
192
  /**
172
193
  * @this {comq.features.Context}
@@ -197,6 +218,25 @@ const connect = async (context, user, password) => {
197
218
  }
198
219
  }
199
220
 
221
+ /**
222
+ * @param {comq.diagnostics.Event} event
223
+ * @param {number} seconds
224
+ * @param {string} message
225
+ * @this {comq.features.Context}
226
+ */
227
+ async function emitted (event, seconds, message) {
228
+ const deadline = Date.now() + seconds * 1000
229
+
230
+ while (this.events[event] !== true && Date.now() < deadline) await timeout(50)
231
+
232
+ assert.equal(this.events[event], true, message)
233
+ }
234
+
235
+ const PUBLISHING_EVENTS = {
236
+ paused: 'pause',
237
+ resumed: 'resume'
238
+ }
239
+
200
240
  const CONNECTION_EVENTS = {
201
241
  lost: 'close',
202
242
  restored: 'open'
@@ -34,6 +34,9 @@ class Context extends World {
34
34
  streamsEnded = {}
35
35
  generatorDestroyed = false
36
36
 
37
+ /** @type {comq.features.Network[]} */
38
+ networks = []
39
+
37
40
  async connect (user, password) {
38
41
  const urls = this.#urls(user, password)
39
42
 
@@ -46,6 +49,12 @@ class Context extends World {
46
49
  await this.#connect(urls, assert)
47
50
  }
48
51
 
52
+ async unplug () {
53
+ await Promise.all(this.networks.map((network) => network.close()))
54
+
55
+ this.networks = []
56
+ }
57
+
49
58
  async disconnect () {
50
59
  if (this.io === undefined) return
51
60
 
@@ -89,7 +98,8 @@ class Context extends World {
89
98
  }
90
99
 
91
100
  #url (i, user, password) {
92
- const url = PROTOCOL + user + ':' + password + '@' + getAddress(i)
101
+ const address = this.networks[i]?.address ?? getAddress(i)
102
+ const url = PROTOCOL + user + ':' + password + '@' + address
93
103
  const heartbeat = global.COMQ_TESTING_AMQP_HEARTBEAT
94
104
 
95
105
  // the watchdog measures silence, so it may only be shortened along with the
@@ -101,6 +111,6 @@ class Context extends World {
101
111
  const PROTOCOL = 'amqp://'
102
112
 
103
113
  /** @type {comq.diagnostics.Event[]} */
104
- const EVENTS = ['open', 'close', 'flow', 'discard']
114
+ const EVENTS = ['open', 'close', 'flow', 'discard', 'pause', 'resume']
105
115
 
106
116
  exports.Context = Context
@@ -3,7 +3,7 @@
3
3
  const stream = require('node:stream')
4
4
  const assert = require('node:assert')
5
5
  const { randomBytes } = require('node:crypto')
6
- const { timeout, quantity, match } = require('@toa.io/generic')
6
+ const { timeout, quantity, match } = require('../../test/helpers')
7
7
  const { parse } = require('./yaml')
8
8
  const { Given, When, Then } = require('@cucumber/cucumber')
9
9
 
@@ -20,6 +20,7 @@ After(
20
20
  )
21
21
 
22
22
  await this.disconnect()
23
+ await this.unplug()
23
24
  })
24
25
 
25
26
  AfterAll(async function () {
@@ -0,0 +1,50 @@
1
+ 'use strict'
2
+
3
+ const { Given, When } = require('@cucumber/cucumber')
4
+ const { BROKERS_AMOUNT } = require('./brokers')
5
+ const { Network } = require('./networks')
6
+
7
+ Given('a network that can go silent',
8
+ /**
9
+ * @this {comq.features.Context}
10
+ */
11
+ async function () {
12
+ for (let n = 0; n < BROKERS_AMOUNT; n++) {
13
+ const network = new Network(n)
14
+
15
+ await network.open()
16
+
17
+ this.networks[n] = network
18
+ }
19
+ })
20
+
21
+ When('the network goes silent',
22
+ /**
23
+ * @this {comq.features.Context}
24
+ */
25
+ function () {
26
+ silence.call(this)
27
+ })
28
+
29
+ // a request that is answered over a connection that is already silent proves
30
+ // nothing, hence the window between the two is left as short as a step boundary
31
+ When('the consumer sends a request to the {token} queue as the network goes silent',
32
+ /**
33
+ * @param {string} queue
34
+ * @this {comq.features.Context}
35
+ */
36
+ function (queue) {
37
+ this.reply = this.io.request(queue, 'hello')
38
+
39
+ silence.call(this)
40
+ })
41
+
42
+ /**
43
+ * @this {comq.features.Context}
44
+ */
45
+ function silence () {
46
+ // only a connection lost from now on counts as lost
47
+ delete this.events.close
48
+
49
+ for (const network of this.networks) network.silence()
50
+ }
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+
3
+ const net = require('node:net')
4
+ const { getAddress } = require('./brokers')
5
+
6
+ /**
7
+ * A network that can go silent, which is what a machine that woke from sleep is
8
+ * left with: the socket stays open, nothing is delivered over it, and neither
9
+ * the broker nor the operating system reports anything. Stopping, killing or
10
+ * pausing a broker does not reproduce it, since all three end up closing the
11
+ * socket the moment the connection is used.
12
+ */
13
+ class Network {
14
+ /** @type {net.Server} */
15
+ #server
16
+
17
+ /** @type {Set<comq.features.Tunnel>} */
18
+ #tunnels = new Set()
19
+
20
+ /** @type {string} */
21
+ #host
22
+
23
+ /** @type {number} */
24
+ #port
25
+
26
+ /**
27
+ * @param {number} [n] broker index
28
+ */
29
+ constructor (n = 0) {
30
+ const [host, port] = getAddress(n).split(':')
31
+
32
+ this.#host = host
33
+ this.#port = Number(port)
34
+ }
35
+
36
+ get address () {
37
+ return 'localhost:' + this.#server.address().port
38
+ }
39
+
40
+ async open () {
41
+ this.#server = net.createServer(this.#tunnel)
42
+
43
+ await new Promise((resolve) => this.#server.listen(0, '127.0.0.1', resolve))
44
+ }
45
+
46
+ /**
47
+ * Only the tunnels that are already open go silent: a connection established
48
+ * afterwards is forwarded as usual, just as it is once a machine is awake.
49
+ */
50
+ silence () {
51
+ for (const tunnel of this.#tunnels) tunnel.silent = true
52
+ }
53
+
54
+ async close () {
55
+ for (const tunnel of this.#tunnels) this.#collapse(tunnel)
56
+
57
+ await new Promise((resolve) => this.#server.close(resolve))
58
+ }
59
+
60
+ /**
61
+ * @param {net.Socket} client
62
+ */
63
+ #tunnel = (client) => {
64
+ const upstream = net.connect(this.#port, this.#host)
65
+
66
+ /** @type {comq.features.Tunnel} */
67
+ const tunnel = { client, upstream, silent: false }
68
+
69
+ this.#tunnels.add(tunnel)
70
+
71
+ client.on('data', (chunk) => { if (!tunnel.silent) upstream.write(chunk) })
72
+ upstream.on('data', (chunk) => { if (!tunnel.silent) client.write(chunk) })
73
+
74
+ for (const socket of [client, upstream]) {
75
+ socket.on('error', () => this.#collapse(tunnel))
76
+ socket.on('close', () => this.#collapse(tunnel))
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {comq.features.Tunnel} tunnel
82
+ */
83
+ #collapse (tunnel) {
84
+ this.#tunnels.delete(tunnel)
85
+ tunnel.client.destroy()
86
+ tunnel.upstream.destroy()
87
+ }
88
+ }
89
+
90
+ exports.Network = Network
@@ -38,6 +38,12 @@ defineParameterType({
38
38
  transformer: (value) => value
39
39
  })
40
40
 
41
+ defineParameterType({
42
+ name: 'paused-event',
43
+ regexp: /(paused|resumed)/,
44
+ transformer: (value) => value
45
+ })
46
+
41
47
  defineParameterType({
42
48
  name: 'message',
43
49
  regexp: /(request|event)/,
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('node:assert')
4
- const { timeout } = require('@toa.io/generic')
4
+ const { timeout } = require('../../test/helpers')
5
5
  const { Then } = require('@cucumber/cucumber')
6
6
 
7
7
  Then('the message is discarded',
@@ -4,7 +4,7 @@ const stream = require('node:stream')
4
4
  const assert = require('node:assert')
5
5
  const { randomBytes } = require('node:crypto')
6
6
  const { parse } = require('./yaml')
7
- const { match, timeout, quantity, random } = require('@toa.io/generic')
7
+ const { match, timeout, quantity, random } = require('../../test/helpers')
8
8
  const { Given, When, Then } = require('@cucumber/cucumber')
9
9
  const { Readable } = require('node:stream')
10
10
  const { actions, BROKERS_AMOUNT } = require('./brokers')
@@ -2,7 +2,7 @@
2
2
 
3
3
  const assert = require('node:assert')
4
4
  const { Given, Then, When } = require('@cucumber/cucumber')
5
- const { quantity, timeout } = require('@toa.io/generic')
5
+ const { quantity, timeout } = require('../../test/helpers')
6
6
  const { randomBytes } = require('node:crypto')
7
7
  const stream = require('node:stream')
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comq",
3
- "version": "0.15.6",
3
+ "version": "0.15.8",
4
4
  "description": "ComQ",
5
5
  "author": {
6
6
  "name": "Artem Gurtovoi",
@@ -27,15 +27,13 @@
27
27
  "feat": "scripts/feat.js"
28
28
  },
29
29
  "devDependencies": {
30
- "@cucumber/cucumber": "^13.2.0",
31
- "@semantic-release/changelog": "^7.0.0",
30
+ "@cucumber/cucumber": "^13.2.1",
32
31
  "@semantic-release/commit-analyzer": "^13.0.1",
33
- "@semantic-release/git": "^10.0.1",
32
+ "@semantic-release/git": "^11.0.1",
34
33
  "@semantic-release/github": "^12.0.2",
35
34
  "@semantic-release/npm": "^13.1.2",
36
35
  "@semantic-release/release-notes-generator": "^14.1.0",
37
36
  "@testcontainers/rabbitmq": "^12.0.4",
38
- "@toa.io/generic": "^1.0.0-alpha.208",
39
37
  "@types/amqplib": "^0.10.8",
40
38
  "@types/jest": "^30.0.0",
41
39
  "fast-glob": "^3.3.3",
@@ -50,7 +48,6 @@
50
48
  },
51
49
  "dependencies": {
52
50
  "amqplib": "^2.0.1",
53
- "msgpackr": "^2.0.5",
54
51
  "promex": "^0.1.1",
55
52
  "reretry": "^0.1.0"
56
53
  },
package/readme.md CHANGED
@@ -299,7 +299,6 @@ If the specified encoding format is not supported, an exception will be thrown.
299
299
  The following encoding formats are supported:
300
300
 
301
301
  - `application/json`
302
- - `application/msgpack`
303
302
  - `application/octet-stream`
304
303
  - `text/plain`
305
304
 
@@ -314,6 +313,12 @@ the connection is restored.
314
313
 
315
314
  When the established connection is lost, it will be automatically restored.
316
315
  Reconnection attempts will be made indefinitely, with intervals increasing up to 30 seconds.
316
+ Unless the URL sets one, a 15 second heartbeat is requested, so that a connection that is gone
317
+ without a word, such as after a machine wakes from sleep, is noticed within a minute instead of
318
+ being left to whatever the broker suggests.
319
+ A connection that stays silent for three heartbeats is destroyed regardless of what the broker and
320
+ the operating system have reported, since neither is guaranteed to report anything at all.
321
+ Requesting `heartbeat=0` disables both.
317
322
  If the broker rejects the connection, for example, due to access being denied, an exception will be thrown.
318
323
  Once reconnected, the topology will be recovered, and any unanswered requests and unconfirmed events will be
319
324
  retransmitted.
@@ -491,11 +496,12 @@ Subscribe to one of the diagnostic events:
491
496
  Optional [`error`](https://amqp-node.github.io/amqplib/channel_api.html#model_events) is passed
492
497
  as an argument.
493
498
  - `error`: an attempt to restore the connection has failed. The exception is passed as an argument.
494
- Attempts continue, so this event is the only way to observe an ongoing outage.
499
+ Attempts continue until one succeeds.
500
+ - `reconnect`: an attempt to restore the connection has started. Followed by `open` or `error`.
495
501
  - `flow`: back pressure is applied to a channel. [Channel type](./types/topology.d.ts) is passed as
496
502
  an argument.
497
503
  - `drain`: back pressure is removed from a channel. Channel type is passed.
498
- - `remove`: channel is removed from the [pool](#sharded-connection).
504
+ - `remove`: channel is removed from the [pool](#sharded-connection), having failed to publish.
499
505
  - `lost`: a shard has lost its connection, hence the requests awaiting their replies on it are
500
506
  re-sent. Channel type is passed.
501
507
  - `recover`: channel's topology is recovered. Channel type is passed.
@@ -503,7 +509,13 @@ Subscribe to one of the diagnostic events:
503
509
  exceptions. Channel type,
504
510
  raw [amqp message object](https://amqp-node.github.io/amqplib/channel_api.html#channel_consume)
505
511
  and the exception are passed as arguments.
512
+ - `return`: message is returned by the broker as unroutable. Channel type and the raw
513
+ [amqp message object](https://amqp-node.github.io/amqplib/channel_api.html#channel_publish) are
514
+ passed as arguments. In the case of a [sharded connection](#sharded-connection), the message is
515
+ reported only once every shard has rejected it.
506
516
  - `pause`: channel is paused. Channel type is passed.
517
+ In the case of a [sharded connection](#sharded-connection), it means that there is no shard left
518
+ to publish to, be it because every one of them has rejected a publish or lost its connection.
507
519
  - `resume`: channel is resumed. Channel type is passed.
508
520
 
509
521
  In the case of a [sharded connection](#sharded-connection), an additional argument specifying the
@@ -520,9 +532,12 @@ established, there is no way to capture the initial `open` event.
520
532
  ```javascript
521
533
  io.diagnose('flow', (type) => console.log(`Back pressure was applied to the ${type} channel`))
522
534
 
523
- io.diagnose('open', () => console.log('AMQP connection established'))
524
- io.diagnose('close', (error) => console.log('AMQP connection closed', error?.message))
525
- io.diagnose('error', (error) => console.log('AMQP connection failed', error.message))
535
+ io.diagnose('reconnect', (shard) => console.log('AMQP reconnecting', { shard }))
536
+ io.diagnose('open', (shard) => console.log('AMQP connection established', { shard }))
537
+ io.diagnose('close', (error, shard) => console.log('AMQP connection closed', { message: error?.message, shard }))
538
+ io.diagnose('error', (error, shard) => console.log('AMQP connection failed', { message: error.message, shard }))
539
+ io.diagnose('lost', (type, shard) => console.log('AMQP shard lost', { type, shard }))
540
+ io.diagnose('recover', (type, shard) => console.log('AMQP channel recovered', { type, shard }))
526
541
  ```
527
542
 
528
543
  # Gratitude
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { generate } = require('randomstring')
4
- const { timeout } = require('@toa.io/generic')
4
+ const { timeout } = require('../../test/helpers')
5
5
 
6
6
  const { lazy } = require('./lazy')
7
7
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { generate } = require('randomstring')
4
- const { promex } = require('@toa.io/generic')
4
+ const { Promex } = require('promex')
5
5
 
6
6
  const { track } = require('./track')
7
7
 
@@ -35,8 +35,8 @@ let test
35
35
  beforeEach(() => {
36
36
  jest.clearAllMocks()
37
37
 
38
- done = promex()
39
- undone = promex()
38
+ done = new Promex()
39
+ undone = new Promex()
40
40
  test = new Test()
41
41
  })
42
42