comq 0.4.1 → 0.5.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 (61) hide show
  1. package/.github/labels.yaml +15 -0
  2. package/.github/workflows/labels.yaml +18 -0
  3. package/.github/workflows/release.yaml +20 -8
  4. package/docker-compose.yaml +20 -2
  5. package/docs/notes.md +14 -8
  6. package/examples/const.js +1 -3
  7. package/examples/events/consumer.js +5 -6
  8. package/examples/events/producer.js +3 -3
  9. package/examples/package.json +3 -0
  10. package/examples/rpc/consumer.js +3 -3
  11. package/examples/rpc/producer.js +3 -3
  12. package/features/connection.feature +3 -0
  13. package/features/events.feature +1 -1
  14. package/features/shards.feature +61 -0
  15. package/features/steps/.test/backpressure.test.js +2 -2
  16. package/features/steps/.test/broker.test.js +32 -6
  17. package/features/steps/.test/connection.test.js +105 -1
  18. package/features/steps/.test/context.mock.js +2 -2
  19. package/features/steps/.test/context.test.js +13 -5
  20. package/features/steps/.test/events.test.js +66 -3
  21. package/features/steps/.test/rpc.test.js +99 -1
  22. package/features/steps/.types/context.d.ts +11 -1
  23. package/features/steps/backpressure.js +1 -1
  24. package/features/steps/broker.js +26 -9
  25. package/features/steps/connection.js +69 -2
  26. package/features/steps/context.js +28 -12
  27. package/features/steps/events.js +41 -2
  28. package/features/steps/rpc.js +58 -1
  29. package/lerna.json +1 -1
  30. package/package.json +5 -5
  31. package/readme.md +75 -25
  32. package/source/channel.js +56 -32
  33. package/source/connect.js +28 -2
  34. package/source/connection.js +24 -17
  35. package/source/decode.js +1 -1
  36. package/source/events.js +7 -0
  37. package/source/io.js +21 -14
  38. package/source/shards/channel.js +219 -0
  39. package/source/shards/connection.js +56 -0
  40. package/source/shards/index.js +5 -0
  41. package/test/channel.test.js +240 -145
  42. package/test/connect.test.js +39 -11
  43. package/test/connection.mock.js +17 -13
  44. package/test/connection.test.js +65 -12
  45. package/test/decode.test.js +5 -5
  46. package/test/io.close.test.js +2 -2
  47. package/test/io.consume.test.js +1 -1
  48. package/test/io.reply.test.js +7 -7
  49. package/test/io.request.test.js +27 -1
  50. package/test/presets.test.js +1 -1
  51. package/test/shards/channel.test.js +367 -0
  52. package/test/shards/connection.test.js +106 -0
  53. package/types/amqp.d.ts +3 -1
  54. package/types/channel.d.ts +8 -5
  55. package/types/connection.d.ts +3 -1
  56. package/types/diagnostic.d.ts +1 -1
  57. package/types/topology.d.ts +4 -0
  58. /package/source/{presets → topology}/event.json +0 -0
  59. /package/source/{presets → topology}/index.js +0 -0
  60. /package/source/{presets → topology}/reply.json +0 -0
  61. /package/source/{presets → topology}/request.json +0 -0
@@ -0,0 +1,15 @@
1
+ - name: "Bug"
2
+ description: "Something isn't working"
3
+ color: "d73a4a"
4
+ - name: "Critical"
5
+ description: "Immediate action required"
6
+ color: "ffff00"
7
+ - name: "Docs"
8
+ description: "Documentation improvements"
9
+ color: "0075ca"
10
+ - name: "Feature"
11
+ description: "Feature request"
12
+ color: "73E25B"
13
+ - name: "Improvement"
14
+ description: "Make something better"
15
+ color: "6A1877"
@@ -0,0 +1,18 @@
1
+ name: Labels
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - 'dev'
7
+ paths:
8
+ - '.github/labels.yml'
9
+ - '.github/workflows/labels.yml'
10
+
11
+ jobs:
12
+ labeler:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v3
17
+ - name: Run Labeler
18
+ uses: crazy-max/ghaction-github-labeler@v4
@@ -6,6 +6,10 @@ on:
6
6
  branches:
7
7
  - main
8
8
  - dev
9
+ paths:
10
+ - 'source/**'
11
+ - 'types/**'
12
+ - 'package-lock.json'
9
13
 
10
14
  jobs:
11
15
  publish:
@@ -27,24 +31,32 @@ jobs:
27
31
  - name: Run tests
28
32
  run: npm i && npm test
29
33
 
30
- - name: Run features
31
- if: github.ref == 'refs/heads/main'
32
- run: docker-compose up -d && npx cucumber-js --fail-fast --exit --format summary --publish-quiet
34
+ - name: Import GPG key
35
+ id: gpg
36
+ uses: crazy-max/ghaction-import-gpg@v5
37
+ with:
38
+ gpg_private_key: ${{ secrets.TOABOT_GPG_PRIVATE_KEY }}
39
+ passphrase: ${{ secrets.TOABOT_GPG_PASSPHRASE }}
40
+ git_user_signingkey: true
41
+ git_commit_gpgsign: true
33
42
 
34
43
  - name: Bump version
35
44
  run: |
36
- git config user.name "toabot"
37
- git config user.email "127577558+toabot@users.noreply.github.com"
38
-
39
45
  if [ ${{ github.base_ref }} = main ]; then
40
- npx lerna@6.5.1 version --conventional-graduate --no-commit-hooks --no-changelog --yes --message "chore(release): %s"
46
+ npx lerna@6.5.1 version --conventional-graduate --no-commit-hooks --no-changelog --yes --message "ci: %s" --sign-git-commit --sign-git-tag
41
47
  git fetch
42
48
  git checkout dev
43
49
  git merge main
44
50
  git push --all origin --no-verify
45
51
  else
46
- npx lerna@6.5.1 version --conventional-prerelease --preid dev --no-commit-hooks --no-changelog --yes --message "chore(prerelease): %s" --git-tag-command "echo %s"
52
+ npx lerna@6.5.1 version --conventional-prerelease --preid dev --no-commit-hooks --no-changelog --yes --message "ci: %s" --git-tag-command "echo %s" --sign-git-commit
47
53
  fi
54
+ env:
55
+ GITHUB_TOKEN: ${{ secrets.TOABOT_GITHUB_TOKEN }}
56
+ GIT_AUTHOR_NAME: ${{ steps.gpg.outputs.name }}
57
+ GIT_AUTHOR_EMAIL: ${{ steps.gpg.outputs.email }}
58
+ GIT_COMMITTER_NAME: ${{ steps.gpg.outputs.name }}
59
+ GIT_COMMITTER_EMAIL: ${{ steps.gpg.outputs.email }}
48
60
 
49
61
  - name: Publish packages
50
62
  env:
@@ -1,8 +1,8 @@
1
1
  version: "3.9"
2
2
  services:
3
- rabbitmq:
3
+ rabbitmq-0:
4
4
  image: rabbitmq:3.10.0-management
5
- container_name: comq-rmq
5
+ container_name: comq-rmq-0
6
6
  ports:
7
7
  - '5673:5672'
8
8
  - '15673:15672'
@@ -18,3 +18,21 @@ services:
18
18
  resources:
19
19
  limits:
20
20
  memory: 512MB
21
+ rabbitmq-1:
22
+ image: rabbitmq:3.10.0-management
23
+ container_name: comq-rmq-1
24
+ ports:
25
+ - '5674:5672'
26
+ - '15674:15672'
27
+ environment:
28
+ RABBITMQ_DEFAULT_USER: developer
29
+ RABBITMQ_DEFAULT_PASS: secret
30
+ healthcheck:
31
+ test: rabbitmq-diagnostics -q ping
32
+ interval: 5s
33
+ timeout: 1s
34
+ retries: 5
35
+ deploy:
36
+ resources:
37
+ limits:
38
+ memory: 512MB
package/docs/notes.md CHANGED
@@ -3,8 +3,8 @@
3
3
  ## "At least once"
4
4
 
5
5
  RabbitMQ delivers a message **before** it has been written to disk, therefore provides "at least
6
- once" delivery guarantee. Considering that, system using communications over AMQP
7
- must be idempotent.
6
+ once" delivery guarantee. Considering that, system using communications over AMQP must be
7
+ idempotent.
8
8
 
9
9
  ## Cause-Effect Confirmation Lag
10
10
 
@@ -36,12 +36,18 @@ A lightweight Producer (one that produces responses quickly) can result in being
36
36
  from the Consumer's perspective (that is, does not consume messages with an expected rate), even
37
37
  though the Producer being idle while waiting for response publication confirmations.
38
38
 
39
- ## `replyTo` property prefix `direct:///`
39
+ ## Flow Control Lock
40
40
 
41
- `IO.request` accepts `replyTo` property modifier function `(replyTo): string` as fourth argument. It
42
- is the temporary solution to provide an option for compatibility with
43
- official [RabbitMQ client for .NET](https://github.com/rabbitmq/rabbitmq-dotnet-client/blob/feceedccd6b81d4b461795390432dc3330fdf596/projects/Unit/TestPublicationAddress.cs).
41
+ When the [flow control mechanism](../readme.md#flow-control) is applied, an application that
42
+ consumes requests and sends other request will hit the prefetch limit of incoming messages and
43
+ become unresponsive until the issue is resolved.
44
44
 
45
- It is yet to be clarified and implemented correctly. [#6](https://github.com/toa-io/comq/issues/6)
45
+ When an application becomes unresponsive, it can cause a chain reaction that makes all of its
46
+ clients unresponsive, up to the system's *entry point* (which is typically the API Gateway).
47
+ Therefore, the flow control of a distributed system is managed by its entry point, giving developers
48
+ the choice to either stop accepting new requests (e.g., by replying with
49
+ a [`429`](https://www.rfc-editor.org/rfc/rfc6585.html#section-4) HTTP status code for an API), or to
50
+ continue sending new requests ignoring back pressure mechanism, risking a potential RabbitMQ crash
51
+ due to running out of memory.
46
52
 
47
- See [Direct Reply-to](https://www.rabbitmq.com/direct-reply-to.html).
53
+ > ComQ does not currently provide an option to ignore back pressure mechanism.
package/examples/const.js CHANGED
@@ -1,3 +1 @@
1
- 'use strict'
2
-
3
- exports.url = 'amqp://developer:secret@localhost:5673'
1
+ export const url = 'amqp://developer:secret@localhost:5673'
@@ -1,10 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const { randomBytes } = require('node:crypto')
4
- const { connect } = require('comq')
5
- const { url } = require('../const')
3
+ import { connect } from 'comq'
4
+ import { url } from '../const.js'
6
5
 
7
- const group = process.argv[2] ?? randomBytes(4).toString('hex')
6
+ const group = process.argv[2]
8
7
 
9
8
  let io
10
9
 
@@ -15,7 +14,7 @@ async function run () {
15
14
 
16
15
  await io.consume('random_numbers', group, consume)
17
16
 
18
- console.log('Waiting for random numbers as ' + group)
17
+ console.log('Waiting for random numbers ' + (group ? 'as ' + group : 'exclusively'))
19
18
 
20
19
  process.on('SIGINT', exit)
21
20
  }
@@ -30,4 +29,4 @@ async function exit () {
30
29
  console.log('\nDisconnected')
31
30
  }
32
31
 
33
- run().then()
32
+ await run()
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { connect } = require('comq')
4
- const { url } = require('../const')
3
+ import { connect } from 'comq'
4
+ import { url } from '../const.js'
5
5
 
6
6
  let io
7
7
  let interval
@@ -34,4 +34,4 @@ async function exit () {
34
34
  console.log('\nDisconnected')
35
35
  }
36
36
 
37
- run().then()
37
+ await run()
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { connect } = require('comq')
4
- const { url } = require('../const')
3
+ import { connect } from 'comq'
4
+ import { url } from '../const.js'
5
5
 
6
6
  let io
7
7
  let interval
@@ -37,4 +37,4 @@ async function exit () {
37
37
  console.log('\nDisconnected')
38
38
  }
39
39
 
40
- run().then()
40
+ await run()
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { connect } = require('comq')
4
- const { url } = require('../const')
3
+ import { connect } from 'comq'
4
+ import { url } from '../const.js'
5
5
 
6
6
  let io
7
7
 
@@ -29,4 +29,4 @@ async function exit () {
29
29
  console.log('\nDisconnected')
30
30
  }
31
31
 
32
- run().then()
32
+ await run()
@@ -1,5 +1,8 @@
1
1
  Feature: Connection Tolerance
2
2
 
3
+ Scenario: Connect
4
+ Given an active connection to the broker
5
+
3
6
  Scenario: Connecting to a broker before it was started
4
7
  Given the broker is down
5
8
  When I attempt to connect to the broker for 0.2 seconds
@@ -1,4 +1,4 @@
1
- Feature: Events (pub/sub)
1
+ Feature: Events
2
2
 
3
3
  Background:
4
4
  Given an active connection to the broker
@@ -0,0 +1,61 @@
1
+ Feature: Sharded Connection
2
+
3
+ Scenario: Establishing sharded connection
4
+ Given an active sharded connection
5
+
6
+ Scenario: Connecting and waiting for 'open' event
7
+ Given the connection to both shards is established
8
+
9
+ Scenario: Connecting while a shard is down
10
+ Given one of the brokers has crashed
11
+ When I attempt to establish sharded connection
12
+ Then the connection is established
13
+ Then the broker is up
14
+
15
+ Scenario: Sending a request to a sharded connection while one broker is down
16
+ Given one of the brokers has crashed
17
+ And an active sharded connection
18
+ And a producer replying `add_numbers` queue
19
+ When the consumer sends a request to the `add_numbers` queue
20
+ Then the consumer receives the reply
21
+ Then the broker is up
22
+
23
+ Scenario: Publishing an event to a sharded connection missing one broker
24
+ Given the connection to both shards is established
25
+ And events are exclusively consumed from the `numbers_added` exchange
26
+ And one of the brokers has crashed
27
+ When an event is emitted to the `numbers_added` exchange
28
+ Then the event is received
29
+ Then the broker is up
30
+
31
+ Scenario: Shard crashes while sending requests
32
+ Given the connection to both shards is established
33
+ And a producer replying `flood` queue
34
+ And I'm sending 1kB requests to the `flood` queue at 1kHz
35
+ When one of the brokers has crashed
36
+ Then no exceptions are thrown
37
+ And all replies have been received
38
+ Then the broker is up
39
+
40
+ Scenario: Shard crashes while publishing events
41
+ Given the connection to both shards is established
42
+ And events are exclusively consumed from the `flood` exchange
43
+ And I'm publishing 1kB events to the `flood` exchange at 100Hz
44
+ When one of the brokers has crashed
45
+ Then no exceptions are thrown
46
+ And all events have been received
47
+ Then the broker is up
48
+
49
+ Scenario: Sealing sharded connection while one broker is down
50
+ Given the connection to both shards is established
51
+ And a producer replying `echo` queue
52
+ And one of the brokers has crashed
53
+ And the connection has started sealing
54
+ When the consumer sends a request to the `echo` queue
55
+ And the broker is up
56
+ Then the connection is sealed
57
+ And the consumer does not receive the reply
58
+
59
+ Scenario: Connecting with wrong credentials
60
+ When I attempt to establish a sharded connection as "someone" with password "whatever"
61
+ Then an exception is thrown: "Handshake terminated by server: 403 (ACCESS-REFUSED)"
@@ -30,7 +30,7 @@ describe('When I\'m sending {quantity}B requests to the {token} queue at {quanti
30
30
  beforeAll(async () => {
31
31
  jest.clearAllMocks()
32
32
 
33
- context = /** @type {comq.features.Context} */ { io }
33
+ context = /** @type {comq.features.Context} */ { io, requestCount: 0, replyCount: 0 }
34
34
 
35
35
  await step.call(context, bytesQ, queue, frequencyQ, seconds)
36
36
  })
@@ -62,7 +62,7 @@ describe('Then back pressure was applied', () => {
62
62
  const step = tomato.steps.Th('back pressure was applied')
63
63
 
64
64
  beforeEach(() => {
65
- context = /** @type {comq.features.Context} */ { io }
65
+ context = /** @type {comq.features.Context} */ { io, events: {} }
66
66
  })
67
67
 
68
68
  it('should be', async () => undefined)
@@ -12,19 +12,26 @@ jest.setTimeout(10000)
12
12
 
13
13
  require('../broker')
14
14
 
15
- const context = {}
15
+ const context = /** @type {comq.features.Context} */ {}
16
16
 
17
17
  beforeEach(() => {
18
18
  jest.clearAllMocks()
19
19
  })
20
20
 
21
- describe('the broker is/has {status}', () => {
21
+ describe.each([false, true])('Given the broker is/has {status} #shards:%s', (sharded) => {
22
22
  const step = tomato.steps.Gi('the broker is/has {status}')
23
23
 
24
+ const containerNumber = sharded ? 1 : 0
25
+
24
26
  it('should be', async () => undefined)
25
27
 
26
28
  beforeEach(() => {
27
29
  context.io = generate()
30
+
31
+ if (sharded) {
32
+ context.sharded = true
33
+ context.shard = containerNumber
34
+ }
28
35
  })
29
36
 
30
37
  it('should start comq-rmq container', async () => {
@@ -33,7 +40,7 @@ describe('the broker is/has {status}', () => {
33
40
 
34
41
  await step.call(context, 'up')
35
42
 
36
- expect(command.execute).toHaveBeenCalledWith('docker start comq-rmq')
43
+ expect(command.execute).toHaveBeenCalledWith('docker start comq-rmq-' + containerNumber)
37
44
  })
38
45
 
39
46
  it('should wait for healthy state', async () => {
@@ -62,12 +69,31 @@ describe('the broker is/has {status}', () => {
62
69
  it('should stop comq-rmq container', async () => {
63
70
  await step.call(context, 'down')
64
71
 
65
- expect(command.execute).toHaveBeenCalledWith('docker stop comq-rmq')
72
+ expect(command.execute).toHaveBeenCalledWith('docker stop comq-rmq-' + containerNumber)
66
73
  })
67
74
 
68
- it('should kill comq-rmq container', async () => {
75
+ it('should kill comq-rmq-0 container', async () => {
69
76
  await step.call(context, 'crashed')
70
77
 
71
- expect(command.execute).toHaveBeenCalledWith('docker kill comq-rmq')
78
+ expect(command.execute).toHaveBeenCalledWith('docker kill comq-rmq-' + containerNumber)
79
+ })
80
+ })
81
+
82
+ describe('Given one of the brokers is/has {status}', () => {
83
+ const step = tomato.steps.Gi('one of the brokers is/has {status}')
84
+
85
+ it('should store picked broker', async () => {
86
+ await step.call(context, 'down')
87
+
88
+ expect(context.shard).toStrictEqual(expect.any(Number))
89
+ })
90
+
91
+ it('should start one of the containers', async () => {
92
+ command.execute.mockImplementationOnce(async () => undefined)
93
+ command.execute.mockImplementationOnce(async () => ({ output: 'healthy' }))
94
+
95
+ await step.call(context, 'up')
96
+
97
+ expect(command.execute).toHaveBeenCalledWith('docker start comq-rmq-' + context.shard)
72
98
  })
73
99
  })
@@ -13,7 +13,7 @@ jest.mock('comq', () => mock.comq)
13
13
 
14
14
  require('../connection')
15
15
 
16
- /** @type {comq.features.Context} */
16
+ /** @type {jest.MockedObject<comq.features.Context>} */
17
17
  let context
18
18
 
19
19
  beforeEach(() => {
@@ -34,6 +34,38 @@ describe('Given an active connection to the broker', () => {
34
34
  it('should connect', async () => {
35
35
  expect(context.connect).toHaveBeenCalled()
36
36
  })
37
+
38
+ it('should store promise', async () => {
39
+ expect(context.connecting).toBeInstanceOf(Promise)
40
+ })
41
+ })
42
+
43
+ describe('Given an active sharded connection', () => {
44
+ const step = tomato.steps.Gi('an active sharded connection')
45
+
46
+ it('should be', async () => undefined)
47
+
48
+ beforeEach(async () => {
49
+ await step.call(context)
50
+ })
51
+
52
+ it('should set sharded to true', async () => {
53
+ expect(context.sharded).toStrictEqual(true)
54
+ })
55
+
56
+ it('should connect', async () => {
57
+ expect(context.connect).toHaveBeenCalled()
58
+ })
59
+
60
+ it('should store promise', async () => {
61
+ expect(context.connecting).toStrictEqual(expect.any(Promise))
62
+ })
63
+ })
64
+
65
+ describe('Given the connection to both shards is established', () => {
66
+ tomato.steps.Gi('the connection to both shards is established')
67
+
68
+ it('should be', async () => undefined)
37
69
  })
38
70
 
39
71
  describe('When I attempt to connect to the broker for {number} second(s)', () => {
@@ -125,6 +157,40 @@ describe('When I attempt to connect to the broker as {string} with password {str
125
157
  })
126
158
  })
127
159
 
160
+ describe('When I attempt to establish sharded connection', () => {
161
+ const step = tomato.steps.Wh('I attempt to establish sharded connection')
162
+
163
+ it('should be', async () => undefined)
164
+
165
+ it('should set context.sharded to true', async () => {
166
+ await step.call(context)
167
+
168
+ expect(context.sharded).toStrictEqual(true)
169
+ })
170
+
171
+ it('should connect', async () => {
172
+ await step.call(context)
173
+
174
+ expect(context.connect).toHaveBeenCalled()
175
+ })
176
+ })
177
+
178
+ describe('When I attempt to establish a sharded connection as {string} with password {string}', () => {
179
+ const step = tomato.steps.Wh('I attempt to establish a sharded connection as {string} with password {string}')
180
+
181
+ it('should be', async () => undefined)
182
+
183
+ const user = generate()
184
+ const password = generate()
185
+
186
+ it('should connect with credentials', async () => {
187
+ await step.call(context, user, password)
188
+
189
+ expect(context.sharded).toStrictEqual(true)
190
+ expect(context.connect).toHaveBeenCalledWith(user, password)
191
+ })
192
+ })
193
+
128
194
  describe.each([['', true], [' not', false]])('Then the connection is%s established',
129
195
  (not, connected) => {
130
196
  const step = tomato.steps.Th(`the connection is${not} established`)
@@ -185,3 +251,41 @@ describe('Then an exception is thrown: {string}', () => {
185
251
  expect(() => step.call(context)).toThrow(AssertionError)
186
252
  })
187
253
  })
254
+
255
+ describe('Given the connection has started sealing', () => {
256
+ const step = tomato.steps.Gi('the connection has started sealing')
257
+
258
+ it('should be', async () => undefined)
259
+
260
+ it('should seal the connection', async () => {
261
+ context.io = /** @type {jest.MockedObject<comq.IO>} */ {
262
+ seal: jest.fn(async () => undefined)
263
+ }
264
+
265
+ await step.call(context)
266
+
267
+ expect(context.io.seal).toHaveBeenCalled()
268
+ expect(context.sealing).toBeInstanceOf(Promise)
269
+ })
270
+ })
271
+
272
+ describe('Then the connection is sealed', () => {
273
+ const step = tomato.steps.Th('the connection is sealed')
274
+
275
+ it('should be', async () => undefined)
276
+
277
+ it('should await for sealing', async () => {
278
+ context.sealing = promex()
279
+
280
+ let sealed = false
281
+
282
+ setImmediate(() => {
283
+ context.sealing.resolve()
284
+ sealed = true
285
+ })
286
+
287
+ await step.call(context)
288
+
289
+ expect(sealed).toStrictEqual(true)
290
+ })
291
+ })
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { generate } = require('randomstring')
3
+ const { comq } = require('./comq.mock')
4
4
 
5
- const connect = jest.fn(async () => generate())
5
+ const connect = jest.fn(() => comq.connect())
6
6
  const disconnect = jest.fn(async () => undefined)
7
7
 
8
8
  /**
@@ -20,11 +20,9 @@ beforeAll(async () => {
20
20
 
21
21
  // noinspection JSValidateTypes
22
22
  context = new Context(_)
23
-
24
- await context.connect()
25
23
  })
26
24
 
27
- describe('connect', () => {
25
+ describe.each([false, true])('connect (shards: %s)', (sharded) => {
28
26
  it('should be', async () => {
29
27
  expect(context.connect).toBeDefined()
30
28
  })
@@ -33,11 +31,18 @@ describe('connect', () => {
33
31
  let io
34
32
 
35
33
  beforeEach(async () => {
34
+ context.sharded = sharded
35
+
36
+ await context.connect()
37
+
36
38
  io = await comq.connect.mock.results[0].value
37
39
  })
38
40
 
39
41
  it('should connect', async () => {
40
- expect(comq.connect).toHaveBeenCalledWith('amqp://developer:secret@localhost:5673')
42
+ const shards = ['amqp://developer:secret@localhost:5673', 'amqp://developer:secret@localhost:5674']
43
+
44
+ if (sharded) expect(comq.connect).toHaveBeenCalledWith(...shards)
45
+ else expect(comq.connect).toHaveBeenCalledWith(shards[0])
41
46
  })
42
47
 
43
48
  it('should connect with credentials', async () => {
@@ -46,7 +51,10 @@ describe('connect', () => {
46
51
 
47
52
  await context.connect(user, password)
48
53
 
49
- expect(comq.connect).toHaveBeenCalledWith(`amqp://${user}:${password}@localhost:5673`)
54
+ const shards = [`amqp://${user}:${password}@localhost:5673`, `amqp://${user}:${password}@localhost:5674`]
55
+
56
+ if (sharded) expect(comq.connect).toHaveBeenCalledWith(...shards)
57
+ else expect(comq.connect).toHaveBeenCalledWith(shards[0])
50
58
  })
51
59
 
52
60
  it.each(['open', 'close', 'flow', 'discard'])('should store %s event',