comq 0.13.0 → 0.15.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.
@@ -0,0 +1,28 @@
1
+ name: Dev
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - dev
7
+
8
+ jobs:
9
+ features-fast:
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ HUSKY: 0
13
+
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v7
17
+
18
+ - name: Setup Node
19
+ uses: actions/setup-node@v7
20
+ with:
21
+ node-version: "24"
22
+ package-manager-cache: false
23
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
27
+ - name: Run fast feature tests
28
+ run: npm run features:fast
@@ -1,73 +1,43 @@
1
1
  name: Release
2
2
 
3
3
  on:
4
- pull_request:
5
- types: [closed]
4
+ push:
6
5
  branches:
7
6
  - release
8
- - alpha
9
- paths:
10
- - 'source/**'
11
- - 'types/**'
12
- - 'package-lock.json'
13
7
 
14
- env:
15
- CI: 1
8
+ permissions:
9
+ contents: write
10
+ issues: write
11
+ pull-requests: write
12
+ id-token: write
16
13
 
17
14
  jobs:
18
- publish:
19
- if: github.event.pull_request.merged == true
15
+ release:
20
16
  runs-on: ubuntu-latest
17
+ env:
18
+ HUSKY: 0
21
19
 
22
20
  steps:
23
- - name: Setup Node
24
- uses: actions/setup-node@v6
25
- with:
26
- node-version: 20
27
- registry-url: 'https://registry.npmjs.org'
28
-
29
21
  - name: Checkout
30
- uses: actions/checkout@v6
22
+ uses: actions/checkout@v7
31
23
  with:
32
24
  fetch-depth: 0
25
+ persist-credentials: false
33
26
 
34
- - name: Run tests
35
- run: npm i && npm test
36
-
37
- - name: Import GPG key
38
- id: gpg
39
- uses: crazy-max/ghaction-import-gpg@v6
27
+ - name: Setup Node
28
+ uses: actions/setup-node@v7
40
29
  with:
41
- gpg_private_key: ${{ secrets.TOABOT_GPG_PRIVATE_KEY }}
42
- passphrase: ${{ secrets.TOABOT_GPG_PASSPHRASE }}
43
- git_user_signingkey: true
44
- git_commit_gpgsign: true
30
+ node-version: "24"
31
+ registry-url: https://registry.npmjs.org
32
+ package-manager-cache: false
45
33
 
46
- - name: Bump version
47
- run: |
48
- if [ ${{ github.base_ref }} = release ]; then
49
- npx lerna@7.3.0 version --conventional-graduate --no-commit-hooks --no-changelog --yes --message "ci: %s" --sign-git-commit --sign-git-tag
50
- else
51
- npx lerna@7.3.0 version --conventional-prerelease --preid alpha --dist-tag alpha --no-commit-hooks --no-changelog --yes --message "ci: %s" --git-tag-command "echo %s" --sign-git-commit
52
- fi
53
-
54
- git fetch
55
- git checkout dev
56
- git merge ${{ github.base_ref }}
57
- git push --all origin --no-verify
58
- env:
59
- GITHUB_TOKEN: ${{ secrets.TOABOT_GITHUB_TOKEN }}
60
- GIT_AUTHOR_NAME: ${{ steps.gpg.outputs.name }}
61
- GIT_AUTHOR_EMAIL: ${{ steps.gpg.outputs.email }}
62
- GIT_COMMITTER_NAME: ${{ steps.gpg.outputs.name }}
63
- GIT_COMMITTER_EMAIL: ${{ steps.gpg.outputs.email }}
34
+ - name: Install dependencies
35
+ run: npm ci
36
+
37
+ - name: Run tests
38
+ run: npm test
64
39
 
65
- - name: Publish packages
40
+ - name: Semantic release
66
41
  env:
67
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
68
- run: |
69
- if [ ${{ github.base_ref }} = release ]; then
70
- npm publish
71
- else
72
- npm publish --tag alpha
73
- fi
42
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43
+ run: npx semantic-release
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env sh
2
+
3
+ [ -n "$CI" ] && exit 0
4
+
5
+ npm run features:fast
package/.releaserc ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "branches": [
3
+ "release"
4
+ ],
5
+ "plugins": [
6
+ "@semantic-release/commit-analyzer",
7
+ "@semantic-release/release-notes-generator",
8
+ [
9
+ "@semantic-release/changelog",
10
+ {
11
+ "changelogFile": "CHANGELOG.md"
12
+ }
13
+ ],
14
+ [
15
+ "@semantic-release/npm"
16
+ ],
17
+ [
18
+ "@semantic-release/git",
19
+ {
20
+ "assets": [
21
+ "package.json",
22
+ "package-lock.json",
23
+ "CHANGELOG.md"
24
+ ],
25
+ "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
26
+ }
27
+ ],
28
+ [
29
+ "@semantic-release/github",
30
+ {
31
+ "owner": "toa-io"
32
+ }
33
+ ]
34
+ ]
35
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,35 @@
1
+ # AGENTS.md
2
+
3
+ ## Cursor Cloud specific instructions
4
+
5
+ ### Project Overview
6
+
7
+ ComQ is a Node.js communication library over AMQP (RabbitMQ). It provides RPC, pub/sub, task queues, pipelines, and sharded connections.
8
+
9
+ ### Services
10
+
11
+ Feature tests start two RabbitMQ instances automatically via Testcontainers (`features/steps/brokers.js`). Credentials: `developer` / `secret`. AMQP ports are assigned dynamically on the host.
12
+
13
+ ### Docker
14
+
15
+ Feature tests require a running Docker daemon. In nested container environments, start Docker with:
16
+
17
+ ```bash
18
+ sudo dockerd --exec-opt native.cgroupdriver=cgroupfs &
19
+ sudo chmod 666 /var/run/docker.sock
20
+ ```
21
+
22
+ Do not use `docker compose up` for feature tests — Testcontainers manages broker lifecycle. `docker compose` may fail here because `deploy.resources.limits.memory` requires cgroupv2 memory limits that are unavailable in nested setups.
23
+
24
+ ### Commands
25
+
26
+ - `npm test` — runs `standard` (lint) + `jest` (300 unit tests, no RabbitMQ required)
27
+ - `npm run features` — runs all Cucumber feature tests (starts RabbitMQ via Testcontainers)
28
+ - `npm run lint` — runs `standard --fix --verbose | snazzy`
29
+
30
+ ### Testing Notes
31
+
32
+ - Unit tests (`jest`) use mocks and do NOT require running RabbitMQ.
33
+ - Feature tests (`cucumber-js`) start brokers in `BeforeAll` and stop them in `AfterAll`.
34
+ - Recovery/connection tests (in `features/connection.feature`, `features/recovery.feature`, `features/shards.feature`) stop/kill broker containers and take 5-15 minutes to complete.
35
+ - To run only fast feature tests (no broker stop/start): `npx cucumber-js --fail-fast -t 'not @manual' features/events.feature features/rpc.feature features/tasks.feature features/singleton.feature features/properties.feature features/tasks.shards.feature`
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # [0.15.0](https://github.com/toa-io/comq/compare/v0.14.0...v0.15.0) (2026-08-01)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * avoid channel recover deadlock after broker restart ([8eeaf2e](https://github.com/toa-io/comq/commit/8eeaf2e12321be71b57913adb6a8fb04bd24d439)), closes [#recovery](https://github.com/toa-io/comq/issues/recovery)
7
+ * destroy half-open connections via socket idle watchdog ([9f7ed5f](https://github.com/toa-io/comq/commit/9f7ed5fe50e9ab1857547a6e908185b6b472162f))
8
+ * ignore close events from replaced AMQP connections ([c1070ab](https://github.com/toa-io/comq/commit/c1070abeca9eb30a1c0ed07925bc06e530c1d9ce))
9
+ * reconnect hang after broker restart ([#235](https://github.com/toa-io/comq/issues/235)) ([be07079](https://github.com/toa-io/comq/commit/be07079863194301d0279e2bdd551260196ce20a))
10
+ * retry transient network errors on initial connect ([c436a20](https://github.com/toa-io/comq/commit/c436a2017a2b52d102afa05bc694c9f22faf2065)), closes [#transient](https://github.com/toa-io/comq/issues/transient)
11
+ * tolerate empty YAML documents after js-yaml 5 bump ([0a05ecb](https://github.com/toa-io/comq/commit/0a05ecbf744822b3aa884382642ce51a93922dfc))
12
+
13
+
14
+ ### Features
15
+
16
+ * allow request() to time out pending replies ([ac9e2bd](https://github.com/toa-io/comq/commit/ac9e2bd7eb0834d19d404c84f24548bd5475017f))
17
+ * tag container-manipulation features [@heavy](https://github.com/heavy) and add features:fast script ([c0f52ec](https://github.com/toa-io/comq/commit/c0f52ecf72c010e387e853b3daed6884c1cef474))
18
+
19
+ # [0.14.0](https://github.com/toa-io/comq/compare/v0.13.0...v0.14.0) (2026-05-24)
20
+
21
+
22
+ ### Features
23
+
24
+ * **features:** manage RabbitMQ brokers with Testcontainers ([1d9ca59](https://github.com/toa-io/comq/commit/1d9ca591ff458c75caf87ab63224b15942ba7d07))
25
+ * **features:** use Testcontainers for RabbitMQ in Gherkin tests ([#219](https://github.com/toa-io/comq/issues/219)) ([a33e66b](https://github.com/toa-io/comq/commit/a33e66bfc357f0f089c728bc60c08f6f24b2c8d1))
26
+
27
+ # Changelog
28
+
29
+ All notable changes to this project are documented in this file by [semantic-release](https://github.com/semantic-release/semantic-release).
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  rabbitmq-0:
3
- image: rabbitmq:3.10.0-management
3
+ image: rabbitmq:4.3-management
4
4
  container_name: comq-rmq-0
5
5
  ports:
6
6
  - '5673:5672'
@@ -18,7 +18,7 @@ services:
18
18
  limits:
19
19
  memory: 512MB
20
20
  rabbitmq-1:
21
- image: rabbitmq:3.10.0-management
21
+ image: rabbitmq:4.3-management
22
22
  container_name: comq-rmq-1
23
23
  ports:
24
24
  - '5674:5672'
@@ -8,5 +8,4 @@ Feature: Back pressure handling
8
8
  """
9
9
  () => { return Buffer.from('ok') }
10
10
  """
11
- When I'm sending 500kB requests to the `flood` queue at 5kHz for 0.5 seconds
12
- Then back pressure was applied
11
+ When I'm flooding the `flood` queue until back pressure is applied
@@ -1,3 +1,4 @@
1
+ @heavy
1
2
  Feature: Connection Tolerance
2
3
 
3
4
  Scenario: Connect
@@ -1,4 +1,11 @@
1
1
  # Features
2
2
 
3
- To run features, start the RabbitMQ server by running `docker compose up -d` in the package root,
4
- then execute `npm run features`.
3
+ Feature tests start two RabbitMQ instances automatically via [Testcontainers](https://node.testcontainers.org/modules/rabbitmq/).
4
+
5
+ Run them with:
6
+
7
+ ```bash
8
+ npm run features
9
+ ```
10
+
11
+ Docker must be available on the host. No manual `docker compose` setup is required.
@@ -1,3 +1,4 @@
1
+ @heavy
1
2
  Feature: Request-Reply Topology Recovery
2
3
 
3
4
  Background:
@@ -64,6 +64,7 @@ Feature: Reply streams
64
64
  And after 200ms
65
65
  Then the generator is destroyed
66
66
 
67
+ @heavy
67
68
  Scenario: Broker crashes while streaming reply
68
69
  Given a number generator with 90ms increasing delay replying `get_numbers` queue
69
70
  When the consumer requests a stream with request to the `get_numbers` queue
@@ -21,6 +21,7 @@ Feature: Reply streams over shards
21
21
  [0, 1, 2, 3, 4]
22
22
  """
23
23
 
24
+ @heavy
24
25
  Scenario: Broker crashes while fetching a stream
25
26
  Given a number generator with 90ms increasing delay replying `get_numbers` queue
26
27
  When the consumer requests a stream with request to the `get_numbers` queue
@@ -1,3 +1,4 @@
1
+ @heavy
1
2
  Feature: Sharded Connection
2
3
 
3
4
  Scenario: Establishing sharded connection
@@ -5,9 +6,10 @@ Feature: Sharded Connection
5
6
 
6
7
  Scenario: Connecting while a shard is down
7
8
  Given one of the brokers has crashed
8
- When I attempt to establish sharded connection
9
- Then an exception is thrown: "ECONNREFUSED"
10
- Then the broker is up
9
+ When I attempt to establish sharded connection for 1 second
10
+ Then the connection is not established
11
+ When the broker is up
12
+ Then the connection is established
11
13
 
12
14
  Scenario: Connecting with wrong credentials
13
15
  When I attempt to establish a sharded connection as "someone" with password "whatever"
@@ -41,7 +43,7 @@ Feature: Sharded Connection
41
43
  Scenario: Shard crashes while publishing events
42
44
  Given an active sharded connection
43
45
  And events are exclusively consumed from the `flood` exchange
44
- And I'm publishing 1kB events to the `flood` exchange at 100Hz
46
+ And I'm publishing 1kB events to the `flood` exchange at 10Hz
45
47
  When one of the brokers has crashed
46
48
  Then no exceptions are thrown
47
49
  And all events have been received
@@ -4,43 +4,45 @@ const assert = require('node:assert')
4
4
  const { randomBytes } = require('node:crypto')
5
5
  const { quantity, timeout } = require('@toa.io/generic')
6
6
 
7
- const { When, Then } = require('@cucumber/cucumber')
7
+ const { When } = require('@cucumber/cucumber')
8
8
 
9
- When('I\'m sending {quantity}B requests to the {token} queue at {quantity}Hz for {number} second(s)',
9
+ const BASE_PAYLOAD = quantity('500k')
10
+ const BASE_BATCH = 20
11
+ const DEADLINE = 25_000
12
+
13
+ When('I\'m flooding the {token} queue until back pressure is applied',
10
14
  /**
11
- * @param {string} bytesQ
12
15
  * @param {string} queue
13
- * @param {string} frequencyQ
14
- * @param {number} seconds
15
16
  * @this {comq.features.Context}
16
17
  */
17
- async function (bytesQ, queue, frequencyQ, seconds) {
18
- const bytes = quantity(bytesQ)
19
- const frequency = quantity(frequencyQ)
20
- const buffer = randomBytes(bytes)
21
- const times = seconds * frequency
22
-
23
- // intervals less than 1ms are not accurate
24
- const interval = Math.max((1000 / frequency), 1)
25
- const each = Math.max(1 / (1000 / frequency), 1)
26
-
27
- const promises = []
28
-
29
- for (let i = 0; i < times; i++) {
30
- const promise = this.io.request(queue, buffer)
31
-
32
- promises.push(promise)
33
-
34
- if ((i + 1) % each === 0) await timeout(interval)
18
+ async function (queue) {
19
+ const deadline = Date.now() + DEADLINE
20
+ const pending = []
21
+ const errors = []
22
+ let iteration = 0
23
+
24
+ while (!this.events.flow && Date.now() < deadline) {
25
+ if (errors.length > 0) throw errors[0]
26
+
27
+ const payload = BASE_PAYLOAD * (iteration + 1)
28
+ const batch = BASE_BATCH * (iteration + 1)
29
+ const buffer = randomBytes(payload)
30
+
31
+ for (let i = 0; i < batch; i++) {
32
+ pending.push(
33
+ this.io.request(queue, buffer).catch((exception) => {
34
+ errors.push(exception)
35
+ })
36
+ )
37
+ }
38
+
39
+ iteration++
40
+ await timeout(0)
41
+
42
+ if (errors.length > 0) throw errors[0]
35
43
  }
36
44
 
37
- await Promise.all(promises)
38
- })
45
+ if (errors.length > 0) throw errors[0]
39
46
 
40
- Then('back pressure was applied',
41
- /**
42
- * @this {comq.features.Context}
43
- */
44
- function () {
45
47
  assert.equal(this.events.flow, true, 'Back pressure hasn\'t been applied')
46
48
  })
@@ -1,13 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const { timeout, random } = require('@toa.io/generic')
4
- const { execute } = require('@toa.io/command')
3
+ const { random } = require('@toa.io/generic')
5
4
 
6
5
  const { Given } = require('@cucumber/cucumber')
6
+ const { BROKERS_AMOUNT, actions } = require('./brokers')
7
7
 
8
8
  Given('the broker is/has {status}',
9
9
  /**
10
- * @param {'up' | 'down'} status
10
+ * @param {'up' | 'down' | 'crashed'} status
11
11
  * @this {comq.features.Context}
12
12
  */
13
13
  async function (status) {
@@ -18,7 +18,7 @@ Given('the broker is/has {status}',
18
18
 
19
19
  Given('one of the brokers is/has {status}',
20
20
  /**
21
- * @param {'up' | 'down'} status
21
+ * @param {'up' | 'down' | 'crashed'} status
22
22
  * @this {comq.features.Context}
23
23
  */
24
24
  async function (status) {
@@ -28,29 +28,3 @@ Given('one of the brokers is/has {status}',
28
28
 
29
29
  await actions[status](shard)
30
30
  })
31
-
32
- const actions = {
33
- up: async (n = 0) => {
34
- await execute('docker start comq-rmq-' + n)
35
- await healthy(n)
36
- },
37
- down: async (n = 0) => {
38
- await execute('docker stop comq-rmq-' + n)
39
- },
40
- crashed: async (n = 0) => {
41
- await execute('docker kill comq-rmq-' + n)
42
- }
43
- }
44
-
45
- async function healthy (n) {
46
- let process
47
-
48
- do {
49
- await timeout(HEALTHCHECK_INTERVAL)
50
-
51
- process = await execute('docker inspect -f {{.State.Health.Status}} comq-rmq-' + n)
52
- } while (process.output !== 'healthy')
53
- }
54
-
55
- const BROKERS_AMOUNT = 2
56
- const HEALTHCHECK_INTERVAL = global.COMQ_TESTING_HEALTHCHECK_INTERVAL ?? 1000
@@ -0,0 +1,125 @@
1
+ 'use strict'
2
+
3
+ const { execFile } = require('node:child_process')
4
+ const { promisify } = require('node:util')
5
+ const { timeout } = require('@toa.io/generic')
6
+ const { RabbitMQContainer } = require('@testcontainers/rabbitmq')
7
+ const { getContainerRuntimeClient } = require('testcontainers')
8
+
9
+ const docker = promisify(execFile)
10
+
11
+ const IMAGE = 'rabbitmq:4.3-management'
12
+ const USER = 'developer'
13
+ const PASSWORD = 'secret'
14
+ const AMQP_PORT = 5672
15
+ const BROKERS_AMOUNT = 2
16
+ const HOST_PORTS = [5673, 5674]
17
+ const HEALTHCHECK_INTERVAL = global.COMQ_TESTING_HEALTHCHECK_INTERVAL ?? 1000
18
+
19
+ /** @type {import('@testcontainers/rabbitmq').StartedRabbitMQContainer[]} */
20
+ const brokers = []
21
+
22
+ async function startBrokers () {
23
+ if (brokers.length > 0) return
24
+
25
+ for (let n = 0; n < BROKERS_AMOUNT; n++) await removeStale(`comq-rmq-${n}`)
26
+
27
+ const started = await Promise.all(
28
+ HOST_PORTS.map((hostPort, n) =>
29
+ new RabbitMQContainer(IMAGE)
30
+ .withEnvironment({
31
+ RABBITMQ_DEFAULT_USER: USER,
32
+ RABBITMQ_DEFAULT_PASS: PASSWORD
33
+ })
34
+ .withAutoRemove(false)
35
+ .withName(`comq-rmq-${n}`)
36
+ .withExposedPorts({ container: AMQP_PORT, host: hostPort })
37
+ .withHealthCheck({
38
+ test: ['CMD', 'rabbitmq-diagnostics', '-q', 'ping'],
39
+ interval: 5000,
40
+ timeout: 1000,
41
+ retries: 5
42
+ })
43
+ .start()
44
+ )
45
+ )
46
+
47
+ brokers.push(...started)
48
+
49
+ await Promise.all(
50
+ Array.from({ length: BROKERS_AMOUNT }, (_, n) => healthy(n))
51
+ )
52
+ }
53
+
54
+ async function stopBrokers () {
55
+ await Promise.all(brokers.map((broker) => broker.stop({ remove: true })))
56
+ brokers.length = 0
57
+ }
58
+
59
+ /**
60
+ * @param {string} name
61
+ */
62
+ async function removeStale (name) {
63
+ const client = await getContainerRuntimeClient()
64
+ const listed = await client.container.list()
65
+
66
+ for (const info of listed) {
67
+ if (!info.Names.some((entry) => entry === `/${name}`)) continue
68
+
69
+ const container = client.container.getById(info.Id)
70
+
71
+ try {
72
+ await client.container.stop(container, { timeout: 0 })
73
+ } catch {
74
+ // already stopped
75
+ }
76
+
77
+ await container.remove({ force: true })
78
+ }
79
+ }
80
+
81
+ /**
82
+ * @param {number} [n]
83
+ * @returns {string}
84
+ */
85
+ function getAddress (n = 0) {
86
+ if (brokers[n] === undefined) throw new Error(`Broker ${n} is not started`)
87
+
88
+ return `localhost:${HOST_PORTS[n]}`
89
+ }
90
+
91
+ /**
92
+ * @param {number} [n]
93
+ */
94
+ async function healthy (n = 0) {
95
+ do {
96
+ await timeout(HEALTHCHECK_INTERVAL)
97
+
98
+ const { stdout } = await docker('docker', ['inspect', '-f', '{{.State.Health.Status}}', `comq-rmq-${n}`])
99
+
100
+ if (stdout.trim() === 'healthy') return
101
+ } while (true)
102
+ }
103
+
104
+ const actions = {
105
+ up: async (n = 0) => {
106
+ await docker('docker', ['start', `comq-rmq-${n}`])
107
+ await healthy(n)
108
+ },
109
+ down: async (n = 0) => {
110
+ await docker('docker', ['stop', `comq-rmq-${n}`])
111
+ },
112
+ crashed: async (n = 0) => {
113
+ await docker('docker', ['kill', `comq-rmq-${n}`])
114
+ }
115
+ }
116
+
117
+ module.exports = {
118
+ BROKERS_AMOUNT,
119
+ USER,
120
+ PASSWORD,
121
+ startBrokers,
122
+ stopBrokers,
123
+ getAddress,
124
+ actions
125
+ }
@@ -88,6 +88,21 @@ When('I attempt to establish sharded connection',
88
88
  await connect(this)
89
89
  })
90
90
 
91
+ When('I attempt to establish sharded connection for {number} second(s)',
92
+ /**
93
+ * @param {number} interval
94
+ * @this {comq.features.Context}
95
+ */
96
+ async function (interval) {
97
+ this.sharded = true
98
+
99
+ const gap = timeout(interval * 1000)
100
+
101
+ this.connecting = connect(this)
102
+
103
+ await Promise.any([this.connecting, gap])
104
+ })
105
+
91
106
  When('I attempt to establish a sharded connection as {string} with password {string}',
92
107
  /**
93
108
  * @param {string} user
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { World } = require('@cucumber/cucumber')
4
4
  const { connect, assert } = require('../../')
5
+ const { getAddress, USER, PASSWORD } = require('./brokers')
5
6
 
6
7
  /**
7
8
  * @implements {comq.features.Context}
@@ -88,14 +89,11 @@ class Context extends World {
88
89
  }
89
90
 
90
91
  #url (i, user, password) {
91
- return PROTOCOL + user + ':' + password + '@' + SHARDS[i]
92
+ return PROTOCOL + user + ':' + password + '@' + getAddress(i)
92
93
  }
93
94
  }
94
95
 
95
96
  const PROTOCOL = 'amqp://'
96
- const SHARDS = ['localhost:5673', 'localhost:5674']
97
- const USER = 'developer'
98
- const PASSWORD = 'secret'
99
97
 
100
98
  /** @type {comq.diagnostics.Event[]} */
101
99
  const EVENTS = ['open', 'close', 'flow', 'discard']
@@ -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 { timeout, quantity, match } = require('@toa.io/generic')
7
- const { load: parse } = require('js-yaml')
7
+ const { parse } = require('./yaml')
8
8
  const { Given, When, Then } = require('@cucumber/cucumber')
9
9
 
10
10
  Given('(that ){token} is consuming events from the {token} exchange',
@@ -1,11 +1,24 @@
1
1
  'use strict'
2
2
 
3
- const { After } = require('@cucumber/cucumber')
3
+ const { BeforeAll, After, AfterAll } = require('@cucumber/cucumber')
4
+ const { startBrokers, stopBrokers, actions, BROKERS_AMOUNT } = require('./brokers')
5
+
6
+ BeforeAll({ timeout: 120_000 }, async function () {
7
+ await startBrokers()
8
+ })
4
9
 
5
10
  After(
6
11
  /**
7
12
  * @this {comq.features.Context}
8
13
  */
9
14
  async function () {
15
+ await Promise.all(
16
+ Array.from({ length: BROKERS_AMOUNT }, (_, n) => actions.up(n))
17
+ )
18
+
10
19
  await this.disconnect()
11
20
  })
21
+
22
+ AfterAll(async function () {
23
+ await stopBrokers()
24
+ })