comq 0.10.0 → 0.10.2

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,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
- })
@@ -1,261 +0,0 @@
1
- 'use strict'
2
-
3
- const { AssertionError } = require('node:assert')
4
- const { generate } = require('randomstring')
5
- const { promex, timeout } = require('@toa.io/generic')
6
- const { dump } = require('@toa.io/yaml')
7
- const tomato = require('@toa.io/tomato')
8
- const { io } = require('./io.mock')
9
- const mock = { tomato }
10
-
11
- jest.mock('@cucumber/cucumber', () => mock.tomato)
12
-
13
- require('../rpc')
14
-
15
- /** @type {comq.features.Context} */
16
- let context
17
-
18
- const queue = generate()
19
-
20
- beforeEach(() => {
21
- jest.clearAllMocks()
22
-
23
- context = /** @type {comq.features.Context} */ { io }
24
- })
25
-
26
- describe('Given function replying {token} queue:', () => {
27
- const step = tomato.steps.Gi('function replying {token} queue:')
28
-
29
- it('should be', () => undefined)
30
-
31
- it('should assign given function as producer', async () => {
32
- const javascript = '({ a, b }) => a + b'
33
-
34
- await step.call(context, queue, javascript)
35
-
36
- expect(io.reply).toHaveBeenCalledWith(queue, expect.any(Function))
37
-
38
- const producer = io.reply.mock.calls[0][1]
39
- const sum = producer({ a: 1, b: 2 })
40
-
41
- expect(sum).toStrictEqual(3)
42
- })
43
- })
44
-
45
- describe('Given a producer replying {token} queue', () => {
46
- const step = tomato.steps.Gi('a producer replying {token} queue')
47
-
48
- it('should be', async () => undefined)
49
-
50
- it('should reply', async () => {
51
- await step.call(context, queue)
52
-
53
- expect(io.reply).toHaveBeenCalledWith(queue, expect.any(Function))
54
-
55
- const producer = io.reply.mock.calls[0][1]
56
- const message = generate()
57
- const reply = await producer(message)
58
-
59
- expect(reply).toStrictEqual(message)
60
- })
61
- })
62
-
63
- describe('Given function replying {token} queue is expected:', () => {
64
- tomato.steps.Gi('function replying {token} queue is expected:')
65
-
66
- it('should be', () => undefined)
67
- })
68
-
69
- describe('When the consumer sends the following request to the {token} queue:', () => {
70
- const step = tomato.steps.Wh('the consumer sends the following request to the {token} queue:')
71
-
72
- it('should be', async () => undefined)
73
-
74
- const payload = { [generate()]: generate() }
75
- const yaml = dump(payload)
76
-
77
- beforeEach(async () => {
78
- await step.call(context, queue, yaml)
79
- })
80
-
81
- it('should send request', async () => {
82
- expect(io.request).toHaveBeenCalledWith(queue, payload)
83
- })
84
-
85
- it('should store reply promise', async () => {
86
- const reply = await io.request.mock.results[0].value
87
-
88
- expect(await context.reply).toStrictEqual(reply)
89
- })
90
-
91
- it('should wait for context.expected', async () => {
92
- context.expected = promex()
93
-
94
- let completed = false
95
-
96
- setImmediate(() => {
97
- expect(completed).toStrictEqual(false)
98
-
99
- completed = true
100
-
101
- context.expected.resolve()
102
- })
103
-
104
- await step.call(context, queue, yaml)
105
-
106
- expect(completed).toStrictEqual(true)
107
- })
108
- })
109
-
110
- describe('When the consumer sends a request to the {token} queue', () => {
111
- const step = tomato.steps.Wh('the consumer sends a request to the {token} queue')
112
-
113
- it('should be', async () => undefined)
114
-
115
- it('should send request', async () => {
116
- await step.call(context, queue)
117
-
118
- expect(io.request).toHaveBeenCalledWith(queue, expect.any(Buffer))
119
- })
120
- })
121
-
122
- describe('Then the consumer receives the reply:', () => {
123
- const step = tomato.steps.Th('the consumer receives the reply:')
124
-
125
- it('should be', async () => undefined)
126
-
127
- const replies = [
128
- ['primitive', generate()],
129
- ['object', { ok: 1 }],
130
- ['array', [1, 'foo']]
131
- ]
132
-
133
- describe.each(replies)('%s reply comparison', (type, value) => {
134
- let yaml
135
-
136
- beforeEach(() => {
137
- yaml = dump(value)
138
- })
139
-
140
- it('should throw if differ', async () => {
141
- const promise = promex()
142
-
143
- context.reply = promise
144
- promise.resolve(generate())
145
-
146
- await expect(step.call(context, yaml)).rejects.toThrow(AssertionError)
147
- })
148
-
149
- it('should pass if equals', async () => {
150
- const promise = promex()
151
-
152
- context.reply = promise
153
-
154
- promise.resolve(value)
155
-
156
- await expect(step.call(context, yaml)).resolves.not.toThrow()
157
- })
158
- })
159
- })
160
-
161
- describe('Then the consumer receives the reply', () => {
162
- const step = tomato.steps.Th('the consumer receives the reply')
163
-
164
- it('should be', async () => undefined)
165
-
166
- it('should await reply', async () => {
167
- context.reply = promex()
168
-
169
- let completed = false
170
-
171
- setImmediate(() => {
172
- expect(completed).toStrictEqual(false)
173
-
174
- context.reply.resolve()
175
-
176
- completed = true
177
- })
178
-
179
- await step.call(context)
180
-
181
- expect(completed).toStrictEqual(true)
182
- })
183
- })
184
-
185
- describe('Then the consumer does not receive the reply', () => {
186
- const step = tomato.steps.Th('the consumer does not receive the reply')
187
-
188
- it('should be', async () => undefined)
189
-
190
- it('should throw if reply is received within 50ms', async () => {
191
- context.reply = promex()
192
-
193
- setTimeout(() => {
194
- context.reply.resolve(generate())
195
- }, 10)
196
-
197
- await expect(step.call(context)).rejects.toThrow(AssertionError)
198
- })
199
-
200
- it('should not throw if reply is not received', async () => {
201
- await expect(step.call(context)).resolves.not.toThrow(AssertionError)
202
- })
203
- })
204
-
205
- describe('Given I\'m sending {quantity}B requests to the {token} queue at {quantity}Hz', () => {
206
- const step = tomato.steps.Gi('I\'m sending {quantity}B requests to the {token} queue at {quantity}Hz')
207
-
208
- it('should be', async () => undefined)
209
-
210
- it('should send requests continuously', async () => {
211
- context = /** @type {comq.features.Context} */ {
212
- io: /** @type {jest.MockedObject<comq.IO>} */ { request: jest.fn() },
213
- requestsSent: []
214
- }
215
-
216
- const bytesQ = '1k'
217
- const queue = generate()
218
- const frequencyQ = '100'
219
-
220
- step.call(context, bytesQ, queue, frequencyQ)
221
-
222
- await timeout(100 + 12)
223
-
224
- const callsAmountExpected = [9, 10, 11, 12]
225
-
226
- expect(callsAmountExpected).toContain(context.io.request.mock.calls.length)
227
- expect(context.sending).toBeDefined()
228
- expect(callsAmountExpected).toContain(context.requestsSent.length)
229
-
230
- clearInterval(context.sending)
231
- })
232
- })
233
-
234
- describe('Then all replies have been received', () => {
235
- const step = tomato.steps.Th('all replies have been received')
236
-
237
- /** @type {jest.MockedObject<comq.features.Context>} */
238
- let context
239
-
240
- it('should be', async () => undefined)
241
-
242
- beforeEach(async () => {
243
- context = /** @type {jest.MockedObject<comq.features.Context>} */ {
244
- requestsSent: [promex(), promex()]
245
- }
246
- })
247
-
248
- it('should pass if replies received', async () => {
249
- let completed = false
250
-
251
- setImmediate(() => {
252
- completed = true
253
-
254
- for (const promise of context.requestsSent) promise.resolve()
255
- })
256
-
257
- await step.call(context)
258
-
259
- expect(completed).toStrictEqual(true)
260
- })
261
- })