comq 0.4.1-dev.3 → 0.5.0-dev.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.
- package/.github/labels.yaml +15 -0
- package/.github/workflows/labels.yaml +18 -0
- package/.github/workflows/release.yaml +20 -8
- package/docker-compose.yaml +20 -2
- package/docs/notes.md +14 -8
- package/examples/const.js +1 -3
- package/examples/events/consumer.js +5 -6
- package/examples/events/producer.js +3 -3
- package/examples/package.json +3 -0
- package/examples/rpc/consumer.js +3 -3
- package/examples/rpc/producer.js +3 -3
- package/features/connection.feature +3 -0
- package/features/events.feature +1 -1
- package/features/shards.feature +61 -0
- package/features/steps/.test/backpressure.test.js +2 -2
- package/features/steps/.test/broker.test.js +32 -6
- package/features/steps/.test/connection.test.js +105 -1
- package/features/steps/.test/context.mock.js +2 -2
- package/features/steps/.test/context.test.js +13 -5
- package/features/steps/.test/events.test.js +66 -3
- package/features/steps/.test/rpc.test.js +99 -1
- package/features/steps/.types/context.d.ts +11 -1
- package/features/steps/backpressure.js +1 -1
- package/features/steps/broker.js +26 -9
- package/features/steps/connection.js +69 -2
- package/features/steps/context.js +28 -12
- package/features/steps/events.js +41 -2
- package/features/steps/rpc.js +58 -1
- package/lerna.json +1 -1
- package/package.json +5 -5
- package/readme.md +75 -25
- package/source/channel.js +56 -32
- package/source/connect.js +28 -2
- package/source/connection.js +24 -17
- package/source/decode.js +1 -1
- package/source/events.js +7 -0
- package/source/io.js +21 -14
- package/source/shards/channel.js +219 -0
- package/source/shards/connection.js +56 -0
- package/source/shards/index.js +5 -0
- package/test/channel.test.js +240 -145
- package/test/connect.test.js +39 -11
- package/test/connection.mock.js +17 -13
- package/test/connection.test.js +65 -12
- package/test/decode.test.js +5 -5
- package/test/io.close.test.js +2 -2
- package/test/io.consume.test.js +1 -1
- package/test/io.reply.test.js +7 -7
- package/test/io.request.test.js +27 -1
- package/test/presets.test.js +1 -1
- package/test/shards/channel.test.js +367 -0
- package/test/shards/connection.test.js +106 -0
- package/types/amqp.d.ts +3 -1
- package/types/channel.d.ts +8 -5
- package/types/connection.d.ts +3 -1
- package/types/diagnostic.d.ts +1 -1
- package/types/topology.d.ts +4 -0
- /package/source/{presets → topology}/event.json +0 -0
- /package/source/{presets → topology}/index.js +0 -0
- /package/source/{presets → topology}/reply.json +0 -0
- /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:
|
|
31
|
-
|
|
32
|
-
|
|
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 "
|
|
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 "
|
|
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:
|
package/docker-compose.yaml
CHANGED
|
@@ -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
|
-
|
|
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
|
-
##
|
|
39
|
+
## Flow Control Lock
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
+
> ComQ does not currently provide an option to ignore back pressure mechanism.
|
package/examples/const.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { url } = require('../const')
|
|
3
|
+
import { connect } from 'comq'
|
|
4
|
+
import { url } from '../const.js'
|
|
6
5
|
|
|
7
|
-
const group = process.argv[2]
|
|
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()
|
|
32
|
+
await run()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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()
|
|
37
|
+
await run()
|
package/examples/rpc/consumer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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()
|
|
40
|
+
await run()
|
package/examples/rpc/producer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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()
|
|
32
|
+
await run()
|
package/features/events.feature
CHANGED
|
@@ -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 {
|
|
3
|
+
const { comq } = require('./comq.mock')
|
|
4
4
|
|
|
5
|
-
const connect = jest.fn(
|
|
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
|
-
|
|
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
|
-
|
|
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',
|