comq 0.10.1 → 0.10.3

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.
@@ -1,285 +0,0 @@
1
- 'use strict'
2
-
3
- const { AssertionError } = require('node:assert')
4
- const { generate } = require('randomstring')
5
- const { random, promex } = require('@toa.io/generic')
6
- const tomato = require('@toa.io/tomato')
7
- const { comq } = require('./comq.mock')
8
- const world = require('./context.mock')
9
- const mock = { tomato, comq }
10
-
11
- jest.mock('@cucumber/cucumber', () => mock.tomato)
12
- jest.mock('comq', () => mock.comq)
13
-
14
- require('../connection')
15
-
16
- /** @type {jest.MockedObject<comq.features.Context>} */
17
- let context
18
-
19
- beforeEach(() => {
20
- jest.clearAllMocks()
21
-
22
- context = world.context()
23
- })
24
-
25
- describe('Given an active connection to the broker', () => {
26
- const step = tomato.steps.Gi('an active connection to the broker')
27
-
28
- it('should be', async () => undefined)
29
-
30
- beforeEach(async () => {
31
- await step.call(context)
32
- })
33
-
34
- it('should connect', async () => {
35
- expect(context.connect).toHaveBeenCalled()
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('When I attempt to connect to the broker for {number} second(s)', () => {
66
- const step = tomato.steps.Wh('I attempt to connect to the broker for {number} second(s)')
67
-
68
- it('should be', async () => undefined)
69
-
70
- const interval = (random(2) + 1) / 10
71
-
72
- describe('broker available', () => {
73
- beforeEach(async () => {
74
- await step.call(context, interval)
75
- })
76
-
77
- it('should call connect', async () => {
78
- expect(context.connect).toHaveBeenCalled()
79
- })
80
- })
81
-
82
- describe('broker unavailable', () => {
83
- /** @type {toa.generic.Promex} */
84
- let connection
85
-
86
- beforeEach(() => {
87
- connection = promex()
88
-
89
- context.connect.mockImplementationOnce(async () => connection)
90
- })
91
-
92
- it('should quit after given interval', async () => {
93
- const start = new Date().getTime()
94
-
95
- await step.call(context, interval)
96
-
97
- const end = new Date().getTime()
98
- const inaccuracy = 1 // setTimeout is inaccurate
99
-
100
- expect(end - start + inaccuracy).toBeGreaterThanOrEqual(interval * 1000)
101
- })
102
- })
103
-
104
- describe('exceptions', () => {
105
- it('should catch exception', async () => {
106
- const exception = new Error(generate())
107
-
108
- context.connect.mockImplementationOnce(async () => { throw exception })
109
-
110
- await step.call(context, interval)
111
-
112
- expect(context.exception).toStrictEqual(exception)
113
- })
114
- })
115
- })
116
-
117
- describe('When I attempt to connect to the broker', () => {
118
- const step = tomato.steps.Wh('I attempt to connect to the broker')
119
-
120
- it('should be', async () => undefined)
121
-
122
- it('should connect', async () => {
123
- await step.call(context)
124
-
125
- expect(context.connect).toHaveBeenCalled()
126
- })
127
-
128
- it('should store exception', async () => {
129
- const exception = new Error(generate())
130
-
131
- context.connect.mockImplementationOnce(async () => { throw exception })
132
-
133
- await step.call(context)
134
-
135
- expect(context.exception).toStrictEqual(exception)
136
- })
137
- })
138
-
139
- describe('When I attempt to connect to the broker as {string} with password {string}', () => {
140
- const step = tomato.steps.Wh('I attempt to connect to the broker as {string} with password {string}')
141
-
142
- it('should be', async () => undefined)
143
-
144
- const user = generate()
145
- const password = generate()
146
-
147
- it('should connect with credentials', async () => {
148
- await step.call(context, user, password)
149
-
150
- expect(context.connect).toHaveBeenCalledWith(user, password)
151
- })
152
- })
153
-
154
- describe('When I attempt to establish sharded connection', () => {
155
- const step = tomato.steps.Wh('I attempt to establish sharded connection')
156
-
157
- it('should be', async () => undefined)
158
-
159
- it('should set context.sharded to true', async () => {
160
- await step.call(context)
161
-
162
- expect(context.sharded).toStrictEqual(true)
163
- })
164
-
165
- it('should connect', async () => {
166
- await step.call(context)
167
-
168
- expect(context.connect).toHaveBeenCalled()
169
- })
170
- })
171
-
172
- describe('When I attempt to establish a sharded connection as {string} with password {string}', () => {
173
- const step = tomato.steps.Wh('I attempt to establish a sharded connection as {string} with password {string}')
174
-
175
- it('should be', async () => undefined)
176
-
177
- const user = generate()
178
- const password = generate()
179
-
180
- it('should connect with credentials', async () => {
181
- await step.call(context, user, password)
182
-
183
- expect(context.sharded).toStrictEqual(true)
184
- expect(context.connect).toHaveBeenCalledWith(user, password)
185
- })
186
- })
187
-
188
- describe.each([['', true], [' not', false]])('Then the connection is%s established',
189
- (not, connected) => {
190
- const step = tomato.steps.Th(`the connection is${not} established`)
191
-
192
- it('should be', async () => undefined)
193
-
194
- it(`should fail if is${connected ? 'n\'t' : ''} connected`, async () => {
195
- context.connected = !connected
196
-
197
- await expect(step.call(context)).rejects.toThrow(AssertionError)
198
- })
199
-
200
- it(`should pass if is${connected ? '' : 'n\'t'} connected`, async () => {
201
- context.connected = connected
202
-
203
- await expect(step.call(context)).resolves.not.toThrow()
204
- })
205
- })
206
-
207
- describe('Then no exceptions are thrown', () => {
208
- const step = tomato.steps.Th('no exceptions are thrown')
209
-
210
- it('should be', async () => undefined)
211
-
212
- it(`should fail if exception is defined`, async () => {
213
- context.exception = new Error()
214
-
215
- expect(() => step.call(context)).toThrow(AssertionError)
216
- })
217
-
218
- it(`should pass if exception isn't defined`, async () => {
219
- expect(() => step.call(context)).not.toThrow()
220
- })
221
- })
222
-
223
- describe('Then an exception is thrown: {string}', () => {
224
- const step = tomato.steps.Th('an exception is thrown: {string}')
225
-
226
- const message = generate()
227
-
228
- it('should be', async () => undefined)
229
-
230
- it(`should pass if exception is defined and message matches`, async () => {
231
- context.exception = new Error(message)
232
-
233
- const slice = message.slice(2, 6)
234
-
235
- expect(() => step.call(context, slice)).not.toThrow()
236
- })
237
-
238
- it(`should fail if exception isn't defined`, async () => {
239
- expect(() => step.call(context)).toThrow(AssertionError)
240
- })
241
-
242
- it('should fail if message doesn\'t match', async () => {
243
- context.exception = new Error(generate())
244
-
245
- expect(() => step.call(context)).toThrow(AssertionError)
246
- })
247
- })
248
-
249
- describe('Given the connection has started sealing', () => {
250
- const step = tomato.steps.Gi('the connection has started sealing')
251
-
252
- it('should be', async () => undefined)
253
-
254
- it('should seal the connection', async () => {
255
- context.io = /** @type {jest.MockedObject<comq.IO>} */ {
256
- seal: jest.fn(async () => undefined)
257
- }
258
-
259
- await step.call(context)
260
-
261
- expect(context.io.seal).toHaveBeenCalled()
262
- expect(context.sealing).toBeInstanceOf(Promise)
263
- })
264
- })
265
-
266
- describe('Then the connection is sealed', () => {
267
- const step = tomato.steps.Th('the connection is sealed')
268
-
269
- it('should be', async () => undefined)
270
-
271
- it('should await for sealing', async () => {
272
- context.sealing = promex()
273
-
274
- let sealed = false
275
-
276
- setImmediate(() => {
277
- context.sealing.resolve()
278
- sealed = true
279
- })
280
-
281
- await step.call(context)
282
-
283
- expect(sealed).toStrictEqual(true)
284
- })
285
- })
@@ -1,14 +0,0 @@
1
- 'use strict'
2
-
3
- const { comq } = require('./comq.mock')
4
-
5
- const connect = jest.fn(() => comq.connect())
6
- const disconnect = jest.fn(async () => undefined)
7
-
8
- /**
9
- * @return {jest.MockedObject<comq.features.Context>}
10
- */
11
- exports.context = () => (/** @type {jest.MockedObject<comq.features.Context>} */{
12
- connect,
13
- disconnect
14
- })
@@ -1,74 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const { comq } = require('./comq.mock')
5
- const mock = { comq }
6
-
7
- jest.mock('comq', () => mock.comq)
8
-
9
- const { Context } = require('../context')
10
-
11
- it('should be', async () => {
12
- expect(Context).toBeDefined()
13
- })
14
-
15
- /** @type {comq.features.Context} */
16
- let context
17
-
18
- beforeAll(async () => {
19
- const _ = {}
20
-
21
- // noinspection JSValidateTypes
22
- context = new Context(_)
23
- })
24
-
25
- describe.each([false, true])('connect (shards: %s)', (sharded) => {
26
- it('should be', async () => {
27
- expect(context.connect).toBeDefined()
28
- })
29
-
30
- /** @type {jest.MockedObject<comq.IO>} */
31
- let io
32
-
33
- beforeEach(async () => {
34
- context.sharded = sharded
35
-
36
- await context.connect()
37
-
38
- io = await comq.connect.mock.results[0].value
39
- })
40
-
41
- it('should connect', async () => {
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])
46
- })
47
-
48
- it('should connect with credentials', async () => {
49
- const user = generate()
50
- const password = generate()
51
-
52
- await context.connect(user, password)
53
-
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])
58
- })
59
-
60
- it.each(['open', 'close', 'flow', 'discard'])('should store %s event',
61
- /**
62
- * @param {comq.diagnostics.Event} event
63
- */
64
- async (event) => {
65
- expect(io.diagnose).toHaveBeenCalledWith(event, expect.any(Function))
66
- expect(context.events[event]).not.toStrictEqual(true)
67
-
68
- const listener = io.diagnose.mock.calls.find((call) => call[0] === event)[1]
69
-
70
- listener()
71
-
72
- expect(context.events[event]).toStrictEqual(true)
73
- })
74
- })
@@ -1,279 +0,0 @@
1
- 'use strict'
2
-
3
- const { AssertionError } = require('node:assert')
4
- const { randomBytes } = require('node:crypto')
5
- const { generate } = require('randomstring')
6
- const { promex, timeout, random } = require('@toa.io/generic')
7
- const tomato = require('@toa.io/tomato')
8
- const { dump } = require('@toa.io/yaml')
9
- const { io } = require('./io.mock')
10
- const mock = { tomato }
11
-
12
- jest.mock('@cucumber/cucumber', () => mock.tomato)
13
-
14
- require('../events')
15
-
16
- /** @type {comq.features.Context} */
17
- let context
18
-
19
- beforeEach(() => {
20
- jest.clearAllMocks()
21
-
22
- context = /** @type {comq.features.Context} */ { io }
23
- })
24
-
25
- describe('Given (that ){token} is consuming events from the {token} exchange', () => {
26
- const step = tomato.steps.Gi('(that ){token} is consuming events from the {token} exchange')
27
-
28
- it('should be', async () => undefined)
29
-
30
- const exchange = generate()
31
- const group = generate()
32
-
33
- let consumer
34
-
35
- beforeEach(async () => {
36
- await step.call(context, group, exchange)
37
-
38
- consumer = io.consume.mock.calls[0]?.[2]
39
-
40
- expect(consumer).toBeInstanceOf(Function)
41
- })
42
-
43
- it('should consume', async () => {
44
- expect(io.consume).toHaveBeenCalledTimes(1)
45
- })
46
-
47
- it('should store event in context', async () => {
48
- const payload = generate()
49
-
50
- await consumer(payload)
51
-
52
- expect(context.consumed[group]).toStrictEqual(expect.objectContaining({ payload }))
53
- })
54
-
55
- it('should store event properties', async () => {
56
- const payload = generate()
57
- const properties = generate()
58
-
59
- await consumer(payload, properties)
60
-
61
- expect(context.consumed[group]).toStrictEqual({ payload, properties })
62
- })
63
- })
64
-
65
- describe('Given {token} consuming events from the {token} exchange is expected', () => {
66
- tomato.steps.Gi('{token} consuming events from the {token} exchange is expected')
67
-
68
- it('should be', async () => undefined)
69
- })
70
-
71
- describe('Given (that )events are exclusively consumed from the {token} exchange', () => {
72
- tomato.steps.Gi('(that )events are exclusively consumed from the {token} exchange')
73
-
74
- it('should be', async () => undefined)
75
- })
76
-
77
- describe('When an event is emitted to the {token} exchange', () => {
78
- const step = tomato.steps.Wh('an event is emitted to the {token} exchange')
79
-
80
- it('should be', async () => undefined)
81
-
82
- const exchange = generate()
83
-
84
- beforeEach(async () => {
85
- await step.call(context, exchange)
86
- })
87
-
88
- it('should store event', async () => {
89
- expect(context.published).toBeDefined()
90
- })
91
-
92
- it('should emit event', async () => {
93
- expect(io.emit.mock.calls[0][0]).toStrictEqual(exchange)
94
- })
95
-
96
- it('should wait for context.expected', async () => {
97
- jest.clearAllMocks()
98
-
99
- context.consumptionPromise = promex()
100
-
101
- let resolved = false
102
-
103
- setImmediate(() => {
104
- expect(io.emit).not.toHaveBeenCalled()
105
-
106
- resolved = true
107
- context.consumptionPromise.resolve()
108
- })
109
-
110
- await step.call(context, exchange)
111
-
112
- expect(io.emit).toHaveBeenCalled()
113
- expect(resolved).toStrictEqual(true)
114
- })
115
- })
116
-
117
- describe('When an event is emitted to the {token} exchange with properties:', () => {
118
- const step = tomato.steps.Wh('an event is emitted to the {token} exchange with properties:')
119
-
120
- it('should be', async () => undefined)
121
-
122
- const exchange = generate()
123
- const properties = { [generate()]: generate() }
124
- const yaml = dump(properties)
125
-
126
- beforeEach(async () => {
127
- await step.call(context, exchange, yaml)
128
- })
129
-
130
- it('should store event', async () => {
131
- expect(context.published).toBeDefined()
132
- })
133
-
134
- it('should emit event', async () => {
135
- expect(io.emit).toHaveBeenCalledWith(exchange, expect.anything(), properties)
136
- })
137
- })
138
-
139
- describe('Then {token} receives the event', () => {
140
- const step = tomato.steps.Th('{token} receives the event')
141
-
142
- const payload = randomBytes(8)
143
- const group = generate()
144
-
145
- beforeEach(() => {
146
- context.consumed = { [group]: { payload } }
147
- })
148
-
149
- it('should be', async () => undefined)
150
-
151
- it('should throw if not consumed', async () => {
152
- context.published = randomBytes(8)
153
-
154
- await expect(step.call(context, group)).rejects.toThrow(AssertionError)
155
- })
156
-
157
- it('should pass if consumed', async () => {
158
- context.published = payload
159
-
160
- await expect(step.call(context, group)).resolves.not.toThrow()
161
- })
162
- })
163
-
164
- describe('Then {token} receives the event with properties:', () => {
165
- const step = tomato.steps.Th('{token} receives the event with properties:')
166
-
167
- it('should be', async () => undefined)
168
-
169
- const payload = randomBytes(8)
170
- const properties = { headers: { [generate()]: generate() } }
171
- const yaml = dump(properties)
172
- const group = generate()
173
-
174
- beforeEach(() => {
175
- context.consumed = { [group]: { payload, properties } }
176
- context.published = payload
177
- })
178
-
179
- it('should throw if does not match', async () => {
180
- const properties = { headers: { [generate()]: generate() } }
181
- const yaml = dump(properties)
182
-
183
- await expect(step.call(context, group, yaml)).rejects.toThrow(AssertionError)
184
- })
185
-
186
- it('should pass if matches', async () => {
187
- await expect(step.call(context, group, yaml)).resolves.not.toThrow()
188
- })
189
- })
190
-
191
- describe('Then the event is received', () => {
192
- const step = tomato.steps.Th('the event is received')
193
-
194
- const payload = randomBytes(8)
195
- const group = undefined
196
-
197
- beforeEach(() => {
198
- context.consumed = { [group]: { payload } }
199
- })
200
-
201
- it('should be', async () => undefined)
202
-
203
- it('should throw if not consumed', async () => {
204
- context.published = randomBytes(8)
205
-
206
- await expect(step.call(context, group)).rejects.toThrow(AssertionError)
207
- })
208
-
209
- it('should pass if consumed', async () => {
210
- context.published = payload
211
-
212
- await expect(step.call(context, group)).resolves.not.toThrow()
213
- })
214
- })
215
-
216
-
217
- describe('Given I\'m publishing {quantity}B events to the {token} exchange at {quantity}Hz', () => {
218
- const step = tomato.steps.Gi('I\'m publishing {quantity}B events to the {token} exchange at {quantity}Hz')
219
-
220
- it('should be', async () => undefined)
221
-
222
- it('should publish events continuously', async () => {
223
- context = /** @type {comq.features.Context} */ {
224
- io: /** @type {jest.MockedObject<comq.IO>} */ { emit: jest.fn() },
225
- requestsSent: [],
226
- eventsPublishedCount: 0
227
- }
228
-
229
- const bytesQ = '1k'
230
- const exchange = generate()
231
- const frequencyQ = '100'
232
-
233
- step.call(context, bytesQ, exchange, frequencyQ)
234
-
235
- await timeout(100)
236
-
237
- const callsAmountExpected = [8, 9, 10, 11]
238
-
239
- expect(callsAmountExpected).toContain(context.io.emit.mock.calls.length)
240
- expect(context.publishing).toBeDefined()
241
- expect(callsAmountExpected).toContain(context.eventsPublishedCount)
242
-
243
- clearInterval(context.publishing)
244
- })
245
- })
246
-
247
- describe('Then all events have been received', () => {
248
- const step = tomato.steps.Th('all events have been received')
249
-
250
- /** @type {jest.MockedObject<comq.features.Context>} */
251
- let context
252
-
253
- it('should be', async () => undefined)
254
-
255
- beforeEach(async () => {
256
- context = /** @type {jest.MockedObject<comq.features.Context>} */ {
257
- eventsPublishedCount: random(100),
258
- eventsConsumedCount: 100 + random()
259
- }
260
- })
261
-
262
- it('should pass if events received', async () => {
263
- context.eventsConsumedCount = context.eventsPublishedCount
264
-
265
- await expect(step.call(context)).resolves.not.toThrow()
266
- })
267
-
268
- it('should fail if events are not received', async () => {
269
- await expect(step.call(context)).rejects.toThrow(AssertionError)
270
- })
271
-
272
- it('should wait for last events (50ms)', async () => {
273
- setTimeout(() => {
274
- context.eventsConsumedCount = context.eventsPublishedCount
275
- }, 49)
276
-
277
- await expect(step.call(context)).resolves.not.toThrow()
278
- })
279
- })
@@ -1,14 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const io = /** @type {jest.MockedObject<comq.IO>} */ {
6
- reply: jest.fn(async () => undefined),
7
- request: jest.fn(async () => generate()),
8
- consume: jest.fn(async () => generate()),
9
- emit: jest.fn(async () => generate()),
10
- close: jest.fn(async () => undefined),
11
- diagnose: jest.fn(() => undefined)
12
- }
13
-
14
- exports.io = io
@@ -1,40 +0,0 @@
1
- 'use strict'
2
-
3
- const { AssertionError } = require('node:assert')
4
- const tomato = require('@toa.io/tomato')
5
- const mock = { tomato }
6
-
7
- jest.mock('@cucumber/cucumber', () => mock.tomato)
8
-
9
- require('../poison')
10
-
11
- /** @type {comq.features.Context} */
12
- let context
13
-
14
- beforeEach(() => {
15
- context = /** @type {comq.features.Context} */ { events: {} }
16
- })
17
-
18
- describe('Then the message is discarded', () => {
19
- const step = tomato.steps.Th('the message is discarded')
20
-
21
- it('should be', async () => undefined)
22
-
23
- it('should throw if not discarded', async () => {
24
- await expect(step.call(context)).rejects.toThrow(AssertionError)
25
- })
26
-
27
- it('should not throw if discarded', async () => {
28
- context.events.discard = true
29
-
30
- await expect(step.call(context)).resolves.not.toThrow()
31
- })
32
-
33
- it('should wait for discard event', async () => {
34
- setTimeout(() => {
35
- context.events.discard = true
36
- }, 10)
37
-
38
- await expect(step.call(context)).resolves.not.toThrow()
39
- })
40
- })