aedes 0.51.3 → 1.0.1

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 (68) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.github/actions/sticky-pr-comment/action.yml +55 -0
  3. package/.github/workflows/benchmark-compare-serial.yml +60 -0
  4. package/.github/workflows/ci.yml +12 -17
  5. package/.release-it.json +18 -0
  6. package/.taprc +15 -6
  7. package/README.md +6 -4
  8. package/aedes.d.ts +0 -6
  9. package/aedes.js +270 -242
  10. package/benchmarks/README.md +33 -0
  11. package/benchmarks/pingpong.js +94 -25
  12. package/benchmarks/receiver.js +77 -0
  13. package/benchmarks/report.js +150 -0
  14. package/benchmarks/runBenchmarks.js +118 -0
  15. package/benchmarks/sender.js +86 -0
  16. package/benchmarks/server.js +19 -18
  17. package/checkVersion.js +20 -0
  18. package/docs/Aedes.md +66 -8
  19. package/docs/Client.md +3 -4
  20. package/docs/Examples.md +39 -22
  21. package/docs/MIGRATION.md +50 -0
  22. package/eslint.config.js +8 -0
  23. package/example.js +51 -40
  24. package/examples/clusters/index.js +28 -23
  25. package/examples/clusters/package.json +10 -6
  26. package/lib/client.js +405 -306
  27. package/lib/handlers/connect.js +42 -38
  28. package/lib/handlers/index.js +9 -11
  29. package/lib/handlers/ping.js +2 -3
  30. package/lib/handlers/puback.js +5 -5
  31. package/lib/handlers/publish.js +29 -14
  32. package/lib/handlers/pubrec.js +9 -17
  33. package/lib/handlers/pubrel.js +34 -25
  34. package/lib/handlers/subscribe.js +47 -43
  35. package/lib/handlers/unsubscribe.js +16 -19
  36. package/lib/qos-packet.js +14 -17
  37. package/lib/utils.js +5 -12
  38. package/lib/write.js +4 -5
  39. package/package.json +139 -136
  40. package/test/auth.js +468 -804
  41. package/test/basic.js +613 -575
  42. package/test/bridge.js +44 -40
  43. package/test/client-pub-sub.js +531 -504
  44. package/test/close_socket_by_other_party.js +137 -102
  45. package/test/connect.js +487 -484
  46. package/test/drain-timeout.js +593 -0
  47. package/test/drain-toxiproxy.js +620 -0
  48. package/test/events.js +173 -145
  49. package/test/helper.js +351 -73
  50. package/test/keep-alive.js +40 -67
  51. package/test/meta.js +257 -210
  52. package/test/not-blocking.js +93 -197
  53. package/test/qos1.js +464 -554
  54. package/test/qos2.js +308 -393
  55. package/test/regr-21.js +39 -21
  56. package/test/require.cjs +22 -0
  57. package/test/retain.js +349 -398
  58. package/test/topics.js +176 -183
  59. package/test/types/aedes.test-d.ts +4 -8
  60. package/test/will.js +310 -428
  61. package/types/instance.d.ts +40 -35
  62. package/types/packet.d.ts +10 -10
  63. package/.coveralls.yml +0 -1
  64. package/benchmarks/bombing.js +0 -34
  65. package/benchmarks/bombingQoS1.js +0 -36
  66. package/benchmarks/throughputCounter.js +0 -23
  67. package/benchmarks/throughputCounterQoS1.js +0 -33
  68. package/types/.eslintrc.json +0 -47
package/test/qos2.js CHANGED
@@ -1,87 +1,53 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const concat = require('concat-stream')
5
- const { setup, connect, subscribe } = require('./helper')
6
- const aedes = require('../')
7
-
8
- function publish (t, s, packet, done) {
1
+ import { test } from 'node:test'
2
+ import { once } from 'node:events'
3
+ import {
4
+ checkNoPacket,
5
+ connect,
6
+ createAndConnect,
7
+ createPubSub,
8
+ nextPacket,
9
+ publish,
10
+ setup,
11
+ subscribe,
12
+ } from './helper.js'
13
+ import { Aedes } from '../aedes.js'
14
+ import handle from '../lib/handlers/pubrec.js'
15
+
16
+ async function receive (t, subscriber, expected) {
17
+ const packet = await nextPacket(subscriber)
18
+ t.assert.ok(packet.messageId !== expected.messageId, 'messageId must differ')
9
19
  const msgId = packet.messageId
20
+ delete packet.messageId
21
+ delete expected.messageId
22
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
10
23
 
11
- s.inStream.write(packet)
12
-
13
- s.outStream.once('data', function (packet) {
14
- t.same(packet, {
15
- cmd: 'pubrec',
16
- messageId: msgId,
17
- length: 2,
18
- dup: false,
19
- retain: false,
20
- qos: 0
21
- }, 'pubrec must match')
22
-
23
- s.inStream.write({
24
- cmd: 'pubrel',
25
- messageId: msgId
26
- })
27
-
28
- s.outStream.once('data', function (packet) {
29
- t.same(packet, {
30
- cmd: 'pubcomp',
31
- messageId: msgId,
32
- length: 2,
33
- dup: false,
34
- retain: false,
35
- qos: 0
36
- }, 'pubcomp must match')
37
-
38
- if (done) {
39
- done()
40
- }
41
- })
24
+ subscriber.inStream.write({
25
+ cmd: 'pubrec',
26
+ messageId: msgId
42
27
  })
43
- }
44
-
45
- function receive (t, subscriber, expected, done) {
46
- subscriber.outStream.once('data', function (packet) {
47
- t.not(packet.messageId, expected.messageId, 'messageId must differ')
48
-
49
- const msgId = packet.messageId
50
- delete packet.messageId
51
- delete expected.messageId
52
- t.same(packet, expected, 'packet must match')
53
28
 
54
- subscriber.inStream.write({
55
- cmd: 'pubrec',
56
- messageId: msgId
57
- })
29
+ const pubRel = await nextPacket(subscriber)
30
+ t.assert.deepStrictEqual(structuredClone(pubRel), {
31
+ cmd: 'pubrel',
32
+ messageId: msgId,
33
+ length: 2,
34
+ qos: 1,
35
+ retain: false,
36
+ dup: false,
37
+ payload: null,
38
+ topic: null
39
+ }, 'pubrel must match')
58
40
 
59
- subscriber.outStream.once('data', function (packet) {
60
- subscriber.inStream.write({
61
- cmd: 'pubcomp',
62
- messageId: msgId
63
- })
64
- t.same(packet, {
65
- cmd: 'pubrel',
66
- messageId: msgId,
67
- length: 2,
68
- qos: 1,
69
- retain: false,
70
- dup: false
71
- }, 'pubrel must match')
72
-
73
- if (done) {
74
- done()
75
- }
76
- })
41
+ subscriber.inStream.write({
42
+ cmd: 'pubcomp',
43
+ messageId: msgId
77
44
  })
78
45
  }
79
46
 
80
- test('publish QoS 2', function (t) {
47
+ test('publish QoS 2', async (t) => {
81
48
  t.plan(2)
82
49
 
83
- const s = connect(setup())
84
- t.teardown(s.broker.close.bind(s.broker))
50
+ const s = await createAndConnect(t)
85
51
 
86
52
  const packet = {
87
53
  cmd: 'publish',
@@ -90,17 +56,13 @@ test('publish QoS 2', function (t) {
90
56
  qos: 2,
91
57
  messageId: 42
92
58
  }
93
- publish(t, s, packet)
59
+ await publish(t, s, packet)
94
60
  })
95
61
 
96
- test('subscribe QoS 2', function (t) {
62
+ test('subscribe QoS 2', async (t) => {
97
63
  t.plan(8)
98
64
 
99
- const broker = aedes()
100
- t.teardown(broker.close.bind(broker))
101
-
102
- const publisher = connect(setup(broker))
103
- const subscriber = connect(setup(broker))
65
+ const { publisher, subscriber } = await createPubSub(t)
104
66
  const toPublish = {
105
67
  cmd: 'publish',
106
68
  topic: 'hello',
@@ -112,20 +74,19 @@ test('subscribe QoS 2', function (t) {
112
74
  retain: false
113
75
  }
114
76
 
115
- subscribe(t, subscriber, 'hello', 2, function () {
116
- publish(t, publisher, toPublish)
117
-
118
- receive(t, subscriber, toPublish)
119
- })
77
+ await subscribe(t, subscriber, 'hello', 2)
78
+ await publish(t, publisher, toPublish)
79
+ await receive(t, subscriber, toPublish)
120
80
  })
121
81
 
122
- test('publish QoS 2 throws error on write', function (t) {
123
- t.plan(1)
82
+ test('publish QoS 2 throws error on write', async (t) => {
83
+ t.plan(2)
124
84
 
125
- const s = connect(setup())
126
- t.teardown(s.broker.close.bind(s.broker))
85
+ const broker = await Aedes.createBroker()
86
+ t.after(() => broker.close())
87
+ const s = setup(broker)
127
88
 
128
- s.broker.on('client', function (client) {
89
+ s.broker.on('client', (client) => {
129
90
  client.connected = false
130
91
  client.connecting = false
131
92
 
@@ -138,34 +99,34 @@ test('publish QoS 2 throws error on write', function (t) {
138
99
  })
139
100
  })
140
101
 
141
- s.broker.on('clientError', function (client, err) {
142
- t.equal(err.message, 'connection closed', 'throws error')
143
- })
102
+ connect(s, { noWait: true }) // don't wait for connect to complete
103
+
104
+ const [client, err] = await once(broker, 'clientError')
105
+ t.assert.ok(client)
106
+ t.assert.equal(err.message, 'connection closed', 'throws error')
144
107
  })
145
108
 
146
- test('pubrec handler calls done when outgoingUpdate fails (clean=false)', function (t) {
109
+ test('pubrec handler calls done when outgoingUpdate fails (clean=false)', async (t) => {
147
110
  t.plan(1)
148
111
 
149
- const s = connect(setup(), { clean: false })
150
- t.teardown(s.broker.close.bind(s.broker))
151
-
152
- const handle = require('../lib/handlers/pubrec.js')
112
+ const s = await createAndConnect(t)
153
113
 
154
- s.broker.persistence.outgoingUpdate = function (client, pubrel, done) {
155
- done(Error('throws error'))
114
+ s.broker.persistence.outgoingUpdate = async () => {
115
+ throw Error('throws error')
156
116
  }
157
117
 
158
- handle(s.client, { messageId: 42 }, function done () {
159
- t.pass('calls done on error')
118
+ await new Promise((resolve) => {
119
+ handle(s.client, { messageId: 42 }, function done () {
120
+ t.assert.ok(true, 'calls done on error')
121
+ resolve()
122
+ })
160
123
  })
161
124
  })
162
125
 
163
- test('client.publish with clean=true subscribption QoS 2', function (t) {
126
+ test('client.publish with clean=true subscribption QoS 2', async (t) => {
164
127
  t.plan(8)
165
128
 
166
- const broker = aedes()
167
- t.teardown(broker.close.bind(broker))
168
-
129
+ const s = await createAndConnect(t, { connect: { clean: true } })
169
130
  const toPublish = {
170
131
  cmd: 'publish',
171
132
  topic: 'hello',
@@ -176,35 +137,27 @@ test('client.publish with clean=true subscribption QoS 2', function (t) {
176
137
  length: 14,
177
138
  retain: false
178
139
  }
179
- let brokerClient = null
180
140
 
181
- broker.on('client', function (client) {
182
- brokerClient = client
183
-
184
- brokerClient.on('error', function (err) {
185
- t.error(err)
186
- })
141
+ s.client.on('error', err => {
142
+ t.assert.ok(!err)
187
143
  })
188
144
 
189
- const subscriber = connect(setup(broker), { clean: true })
145
+ await subscribe(t, s, 'hello', 2)
146
+ t.assert.ok(true, 'subscribed')
190
147
 
191
- subscribe(t, subscriber, 'hello', 2, function () {
192
- t.pass('subscribed')
193
- receive(t, subscriber, toPublish)
194
- brokerClient.publish(toPublish, function (err) {
195
- t.error(err)
148
+ await new Promise((resolve) => {
149
+ s.client.publish(toPublish, err => {
150
+ t.assert.ok(!err)
151
+ resolve()
196
152
  })
197
153
  })
154
+ await receive(t, s, toPublish)
198
155
  })
199
156
 
200
- test('call published method with client with QoS 2', function (t) {
157
+ test('call published method with client with QoS 2', async (t) => {
201
158
  t.plan(9)
202
159
 
203
- const broker = aedes()
204
- t.teardown(broker.close.bind(broker))
205
-
206
- const publisher = connect(setup(broker))
207
- const subscriber = connect(setup(broker))
160
+ const { broker, publisher, subscriber } = await createPubSub(t)
208
161
  const toPublish = {
209
162
  cmd: 'publish',
210
163
  topic: 'hello',
@@ -216,31 +169,28 @@ test('call published method with client with QoS 2', function (t) {
216
169
  retain: false
217
170
  }
218
171
 
219
- broker.published = function (packet, client, cb) {
172
+ broker.published = (packet, client, cb) => {
220
173
  // Client is null for all server publishes
221
174
  if (packet.topic.split('/')[0] !== '$SYS') {
222
- t.ok(client, 'client must be passed to published method')
175
+ t.assert.ok(client, 'client must be passed to published method')
223
176
  cb()
224
177
  }
225
178
  }
226
179
 
227
- subscribe(t, subscriber, 'hello', 2, function () {
228
- publish(t, publisher, toPublish)
229
-
230
- receive(t, subscriber, toPublish)
231
- })
180
+ await subscribe(t, subscriber, 'hello', 2)
181
+ await publish(t, publisher, toPublish)
182
+ await receive(t, subscriber, toPublish)
232
183
  })
233
184
 
234
- ;[true, false].forEach(function (cleanSession) {
235
- test(`authorized forward publish packets in QoS 2 [clean=${cleanSession}]`, function (t) {
185
+ for (const cleanSession of [true, false]) {
186
+ test(`authorized forward publish packets in QoS 2 [clean=${cleanSession}]`, async (t) => {
236
187
  t.plan(9)
237
188
 
238
- const broker = aedes()
239
- t.teardown(broker.close.bind(broker))
189
+ const { broker, publisher, subscriber } = await createPubSub(t, {
190
+ publisher: { clientId: 'my-client-xyz-8' },
191
+ subscriber: { clean: cleanSession, clientId: 'abcde' }
192
+ })
240
193
 
241
- const opts = { clean: cleanSession }
242
- const publisher = connect(setup(broker), { clientId: 'my-client-xyz-8' })
243
- const subscriber = connect(setup(broker), { ...opts, clientId: 'abcde' })
244
194
  const forwarded = {
245
195
  cmd: 'publish',
246
196
  topic: 'hello',
@@ -260,90 +210,74 @@ test('call published method with client with QoS 2', function (t) {
260
210
  length: 14,
261
211
  dup: false
262
212
  }
263
- broker.authorizeForward = function (client, packet) {
213
+ broker.authorizeForward = (client, packet) => {
264
214
  forwarded.brokerId = broker.id
265
215
  forwarded.brokerCounter = broker.counter
266
216
  delete packet.nl
267
- t.same(packet, forwarded, 'forwarded packet must match')
217
+ t.assert.deepEqual(structuredClone(packet), forwarded, 'forwarded packet must match')
268
218
  return packet
269
219
  }
270
220
 
271
- subscribe(t, subscriber, 'hello', 2, function () {
272
- subscriber.outStream.once('data', function (packet) {
273
- t.not(packet.messageId, 42)
274
- delete packet.messageId
275
- t.same(packet, expected, 'packet must match')
276
- })
277
-
278
- publish(t, publisher, {
279
- cmd: 'publish',
280
- topic: 'hello',
281
- payload: Buffer.from('world'),
282
- qos: 2,
283
- retain: false,
284
- messageId: 42,
285
- dup: false
286
- }, function () {
287
- const stream = broker.persistence.outgoingStream({ id: 'abcde' })
288
- stream.pipe(concat(function (list) {
289
- if (cleanSession) {
290
- t.equal(list.length, 0, 'should have empty item in queue')
291
- } else {
292
- t.equal(list.length, 1, 'should have one item in queue')
293
- }
294
- }))
295
- })
296
- })
297
- })
298
- })
299
-
300
- ;[true, false].forEach(function (cleanSession) {
301
- test(`unauthorized forward publish packets in QoS 2 [clean=${cleanSession}]`, function (t) {
302
- t.plan(6)
221
+ await subscribe(t, subscriber, 'hello', 2)
303
222
 
304
- const broker = aedes()
305
- t.teardown(broker.close.bind(broker))
223
+ await publish(t, publisher, {
224
+ cmd: 'publish',
225
+ topic: 'hello',
226
+ payload: Buffer.from('world'),
227
+ qos: 2,
228
+ retain: false,
229
+ messageId: 42,
230
+ dup: false
231
+ })
306
232
 
307
- const opts = { clean: cleanSession }
233
+ const packet = await nextPacket(subscriber)
234
+ t.assert.ok(packet.messageId !== 42, 'messageId must differ')
235
+ delete packet.messageId
236
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
237
+
238
+ const stream = broker.persistence.outgoingStream({ id: 'abcde' })
239
+ const list = await stream.toArray()
240
+ if (cleanSession) {
241
+ t.assert.equal(list.length, 0, 'should have empty item in queue')
242
+ } else {
243
+ t.assert.equal(list.length, 1, 'should have one item in queue')
244
+ }
245
+ })
246
+ }
308
247
 
309
- const publisher = connect(setup(broker))
310
- const subscriber = connect(setup(broker), { ...opts, clientId: 'abcde' })
248
+ for (const cleanSession of [true, false]) {
249
+ test(`unauthorized forward publish packets in QoS 2 [clean=${cleanSession}]`, async (t) => {
250
+ t.plan(7)
311
251
 
312
- broker.authorizeForward = function (client, packet) {
252
+ const { broker, publisher, subscriber } = await createPubSub(t, {
253
+ publisher: { clientId: 'my-client-xyz-8' },
254
+ subscriber: { clean: cleanSession, clientId: 'abcde' }
255
+ })
313
256
 
314
- }
257
+ broker.authorizeForward = (client, packet) => { }
315
258
 
316
- subscribe(t, subscriber, 'hello', 2, function () {
317
- subscriber.outStream.once('data', function (packet) {
318
- t.fail('should not receive any packets')
319
- })
320
-
321
- publish(t, publisher, {
322
- cmd: 'publish',
323
- topic: 'hello',
324
- payload: Buffer.from('world'),
325
- qos: 2,
326
- retain: false,
327
- messageId: 42,
328
- dup: false
329
- }, function () {
330
- const stream = broker.persistence.outgoingStream({ id: 'abcde' })
331
- stream.pipe(concat(function (list) {
332
- t.equal(list.length, 0, 'should empty in queue')
333
- }))
334
- })
259
+ await subscribe(t, subscriber, 'hello', 2)
260
+ await publish(t, publisher, {
261
+ cmd: 'publish',
262
+ topic: 'hello',
263
+ payload: Buffer.from('world'),
264
+ qos: 2,
265
+ retain: false,
266
+ messageId: 42,
267
+ dup: false
335
268
  })
269
+ await checkNoPacket(t, subscriber, 10)
270
+
271
+ const stream = broker.persistence.outgoingStream({ id: 'abcde' })
272
+ const list = await stream.toArray()
273
+ t.assert.equal(list.length, 0, 'should empty in queue')
336
274
  })
337
- })
275
+ }
338
276
 
339
- test('subscribe QoS 0, but publish QoS 2', function (t) {
277
+ test('subscribe QoS 0, but publish QoS 2', async (t) => {
340
278
  t.plan(6)
341
279
 
342
- const broker = aedes()
343
- t.teardown(broker.close.bind(broker))
344
-
345
- const publisher = connect(setup(broker))
346
- const subscriber = connect(setup(broker))
280
+ const { publisher, subscriber } = await createPubSub(t)
347
281
  const expected = {
348
282
  cmd: 'publish',
349
283
  topic: 'hello',
@@ -354,31 +288,26 @@ test('subscribe QoS 0, but publish QoS 2', function (t) {
354
288
  retain: false
355
289
  }
356
290
 
357
- subscribe(t, subscriber, 'hello', 0, function () {
358
- subscriber.outStream.once('data', function (packet) {
359
- t.same(packet, expected, 'packet must match')
360
- })
291
+ await subscribe(t, subscriber, 'hello', 0)
361
292
 
362
- publish(t, publisher, {
363
- cmd: 'publish',
364
- topic: 'hello',
365
- payload: Buffer.from('world'),
366
- qos: 2,
367
- retain: false,
368
- messageId: 42,
369
- dup: false
370
- })
293
+ await publish(t, publisher, {
294
+ cmd: 'publish',
295
+ topic: 'hello',
296
+ payload: Buffer.from('world'),
297
+ qos: 2,
298
+ retain: false,
299
+ messageId: 42,
300
+ dup: false
371
301
  })
302
+
303
+ const packet = await nextPacket(subscriber)
304
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
372
305
  })
373
306
 
374
- test('subscribe QoS 1, but publish QoS 2', function (t) {
307
+ test('subscribe QoS 1, but publish QoS 2', async (t) => {
375
308
  t.plan(6)
376
309
 
377
- const broker = aedes()
378
- t.teardown(broker.close.bind(broker))
379
-
380
- const publisher = connect(setup(broker))
381
- const subscriber = connect(setup(broker))
310
+ const { publisher, subscriber } = await createPubSub(t)
382
311
  const expected = {
383
312
  cmd: 'publish',
384
313
  topic: 'hello',
@@ -389,31 +318,31 @@ test('subscribe QoS 1, but publish QoS 2', function (t) {
389
318
  retain: false
390
319
  }
391
320
 
392
- subscribe(t, subscriber, 'hello', 1, function () {
393
- subscriber.outStream.once('data', function (packet) {
394
- delete packet.messageId
395
- t.same(packet, expected, 'packet must match')
396
- })
321
+ await subscribe(t, subscriber, 'hello', 1)
397
322
 
398
- publish(t, publisher, {
399
- cmd: 'publish',
400
- topic: 'hello',
401
- payload: Buffer.from('world'),
402
- qos: 2,
403
- retain: false,
404
- messageId: 42,
405
- dup: false
406
- })
323
+ await publish(t, publisher, {
324
+ cmd: 'publish',
325
+ topic: 'hello',
326
+ payload: Buffer.from('world'),
327
+ qos: 2,
328
+ retain: false,
329
+ messageId: 42,
330
+ dup: false
407
331
  })
332
+
333
+ const packet = await nextPacket(subscriber)
334
+ delete packet.messageId
335
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
408
336
  })
409
337
 
410
- test('restore QoS 2 subscriptions not clean', function (t) {
338
+ test('restore QoS 2 subscriptions not clean', async (t) => {
411
339
  t.plan(9)
412
340
 
413
- const broker = aedes()
414
- t.teardown(broker.close.bind(broker))
341
+ const opts = { clean: false, clientId: 'abcde' }
342
+ const { broker, publisher, subscriber } = await createPubSub(t, {
343
+ subscriber: opts
344
+ })
415
345
 
416
- let subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' })
417
346
  const expected = {
418
347
  cmd: 'publish',
419
348
  topic: 'hello',
@@ -425,28 +354,25 @@ test('restore QoS 2 subscriptions not clean', function (t) {
425
354
  retain: false
426
355
  }
427
356
 
428
- subscribe(t, subscriber, 'hello', 2, function () {
429
- subscriber.inStream.end()
357
+ await subscribe(t, subscriber, 'hello', 2)
358
+ subscriber.inStream.end()
430
359
 
431
- const publisher = connect(setup(broker))
432
-
433
- subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' }, function (connect) {
434
- t.equal(connect.sessionPresent, true, 'session present is set to true')
435
- publish(t, publisher, expected)
436
- })
437
-
438
- receive(t, subscriber, expected)
439
- })
360
+ const subscriber2 = setup(broker)
361
+ const connack = await connect(subscriber2, { connect: opts })
362
+ t.assert.equal(connack.sessionPresent, true, 'session present is set to true')
363
+ // getting a connack does not mean that the client setup has completed in Aedes
364
+ await once(broker, 'clientReady')
365
+ await publish(t, publisher, expected)
366
+ await receive(t, subscriber2, expected)
440
367
  })
441
368
 
442
- test('resend publish on non-clean reconnect QoS 2', function (t) {
369
+ test('resend publish on non-clean reconnect QoS 2', async (t) => {
443
370
  t.plan(8)
444
371
 
445
- const broker = aedes()
446
- t.teardown(broker.close.bind(broker))
447
-
448
372
  const opts = { clean: false, clientId: 'abcde' }
449
- let subscriber = connect(setup(broker), opts)
373
+ const { broker, publisher, subscriber } = await createPubSub(t, {
374
+ subscriber: opts
375
+ })
450
376
  const expected = {
451
377
  cmd: 'publish',
452
378
  topic: 'hello',
@@ -458,27 +384,22 @@ test('resend publish on non-clean reconnect QoS 2', function (t) {
458
384
  retain: false
459
385
  }
460
386
 
461
- subscribe(t, subscriber, 'hello', 2, function () {
462
- subscriber.inStream.end()
463
-
464
- const publisher = connect(setup(broker))
465
-
466
- publish(t, publisher, expected, function () {
467
- subscriber = connect(setup(broker), opts)
387
+ await subscribe(t, subscriber, 'hello', 2)
388
+ subscriber.inStream.end()
468
389
 
469
- receive(t, subscriber, expected)
470
- })
471
- })
390
+ await publish(t, publisher, expected)
391
+ const subscriber2 = setup(broker)
392
+ await connect(subscriber2, { connect: opts })
393
+ await receive(t, subscriber2, expected)
472
394
  })
473
395
 
474
- test('resend pubrel on non-clean reconnect QoS 2', function (t) {
396
+ test('resend pubrel on non-clean reconnect QoS 2', async (t) => {
475
397
  t.plan(9)
476
398
 
477
- const broker = aedes()
478
- t.teardown(broker.close.bind(broker))
479
-
480
399
  const opts = { clean: false, clientId: 'abcde' }
481
- let subscriber = connect(setup(broker), opts)
400
+ const { broker, publisher, subscriber } = await createPubSub(t, {
401
+ subscriber: opts
402
+ })
482
403
  const expected = {
483
404
  cmd: 'publish',
484
405
  topic: 'hello',
@@ -490,70 +411,66 @@ test('resend pubrel on non-clean reconnect QoS 2', function (t) {
490
411
  retain: false
491
412
  }
492
413
 
493
- subscribe(t, subscriber, 'hello', 2, function () {
494
- subscriber.inStream.end()
495
-
496
- const publisher = connect(setup(broker))
497
-
498
- publish(t, publisher, expected, function () {
499
- subscriber = connect(setup(broker), opts)
500
-
501
- subscriber.outStream.once('data', function (packet) {
502
- t.not(packet.messageId, expected.messageId, 'messageId must differ')
503
-
504
- const msgId = packet.messageId
505
- delete packet.messageId
506
- delete expected.messageId
507
- t.same(packet, expected, 'packet must match')
508
-
509
- subscriber.inStream.write({
510
- cmd: 'pubrec',
511
- messageId: msgId
512
- })
513
-
514
- subscriber.outStream.once('data', function (packet) {
515
- t.same(packet, {
516
- cmd: 'pubrel',
517
- messageId: msgId,
518
- length: 2,
519
- qos: 1,
520
- retain: false,
521
- dup: false
522
- }, 'pubrel must match')
523
-
524
- subscriber.inStream.end()
525
-
526
- subscriber = connect(setup(broker), opts)
527
-
528
- subscriber.outStream.once('data', function (packet) {
529
- t.same(packet, {
530
- cmd: 'pubrel',
531
- messageId: msgId,
532
- length: 2,
533
- qos: 1,
534
- retain: false,
535
- dup: false
536
- }, 'pubrel must match')
537
-
538
- subscriber.inStream.write({
539
- cmd: 'pubcomp',
540
- messageId: msgId
541
- })
542
- })
543
- })
544
- })
545
- })
414
+ await subscribe(t, subscriber, 'hello', 2)
415
+ subscriber.inStream.end()
416
+
417
+ await publish(t, publisher, expected)
418
+
419
+ const subscriber2 = setup(broker)
420
+ await connect(subscriber2, { connect: opts })
421
+
422
+ const packet = await nextPacket(subscriber2)
423
+ t.assert.ok(packet.messageId !== expected.messageId, 'messageId must differ')
424
+ const msgId = packet.messageId
425
+ delete packet.messageId
426
+ delete expected.messageId
427
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
428
+
429
+ subscriber2.inStream.write({
430
+ cmd: 'pubrec',
431
+ messageId: msgId
432
+ })
433
+
434
+ const pubRel = await nextPacket(subscriber2)
435
+ t.assert.deepEqual(structuredClone(pubRel), {
436
+ cmd: 'pubrel',
437
+ messageId: msgId,
438
+ length: 2,
439
+ qos: 1,
440
+ retain: false,
441
+ dup: false,
442
+ payload: null,
443
+ topic: null
444
+ }, 'pubrel must match')
445
+
446
+ subscriber2.inStream.end()
447
+ const subscriber3 = setup(broker)
448
+ await connect(subscriber3, { connect: opts })
449
+
450
+ const pubRel2 = await nextPacket(subscriber3)
451
+ t.assert.deepEqual(structuredClone(pubRel2), {
452
+ cmd: 'pubrel',
453
+ messageId: msgId,
454
+ length: 2,
455
+ qos: 1,
456
+ retain: false,
457
+ dup: false,
458
+ payload: null,
459
+ topic: null
460
+ }, 'pubrel must match')
461
+
462
+ subscriber3.inStream.write({
463
+ cmd: 'pubcomp',
464
+ messageId: msgId
546
465
  })
547
466
  })
548
467
 
549
- test('publish after disconnection', function (t) {
468
+ // this test does the same as it did before conversion from Tap
469
+ // but it does not seem to do what the title says
470
+ test('publish after disconnection', async (t) => {
550
471
  t.plan(10)
551
472
 
552
- const broker = aedes()
553
- t.teardown(broker.close.bind(broker))
554
-
555
- const publisher = connect(setup(broker))
556
- const subscriber = connect(setup(broker))
473
+ const { publisher, subscriber } = await createPubSub(t)
557
474
  const toPublish = {
558
475
  cmd: 'publish',
559
476
  topic: 'hello',
@@ -575,24 +492,20 @@ test('publish after disconnection', function (t) {
575
492
  retain: false
576
493
  }
577
494
 
578
- subscribe(t, subscriber, 'hello', 2, function () {
579
- publish(t, publisher, toPublish)
580
-
581
- receive(t, subscriber, toPublish, function () {
582
- publish(t, publisher, toPublish2)
583
- })
584
- })
495
+ await subscribe(t, subscriber, 'hello', 2)
496
+ await publish(t, publisher, toPublish)
497
+ await receive(t, subscriber, toPublish)
498
+ await publish(t, publisher, toPublish2)
585
499
  })
586
500
 
587
- test('multiple publish and store one', function (t) {
588
- t.plan(2)
589
-
590
- const broker = aedes()
501
+ test('multiple publish and store one', async (t) => {
502
+ t.plan(1)
591
503
 
592
504
  const sid = {
593
505
  id: 'abcde'
594
506
  }
595
- const s = connect(setup(broker), { clientId: sid.id })
507
+ const s = await createAndConnect(t, { connect: { clientId: sid.id } })
508
+
596
509
  const toPublish = {
597
510
  cmd: 'publish',
598
511
  topic: 'hello',
@@ -606,37 +519,25 @@ test('multiple publish and store one', function (t) {
606
519
  let count = 5
607
520
  while (count--) {
608
521
  s.inStream.write(toPublish)
522
+ await nextPacket(s)
609
523
  }
610
- let recvcnt = 0
611
- s.outStream.on('data', function (packet) {
612
- if (++recvcnt < 5) return
613
- broker.close(function () {
614
- broker.persistence.incomingGetPacket(sid, toPublish, function (err, origPacket) {
615
- delete origPacket.brokerId
616
- delete origPacket.brokerCounter
617
- t.same(origPacket, toPublish, 'packet must match')
618
- t.error(err)
619
- })
620
- })
524
+
525
+ await new Promise((resolve) => {
526
+ s.broker.close(resolve)
621
527
  })
528
+ const origPacket = await s.broker.persistence.incomingGetPacket(sid, toPublish)
529
+ delete origPacket.brokerId
530
+ delete origPacket.brokerCounter
531
+ t.assert.deepEqual(origPacket, toPublish, 'packet must match')
622
532
  })
623
533
 
624
- test('packet is written to stream after being stored', function (t) {
625
- const s = connect(setup())
626
-
627
- const broker = s.broker
534
+ test('packet is written to stream after being stored', async (t) => {
535
+ t.plan(3)
628
536
 
629
- t.teardown(broker.close.bind(s.broker))
537
+ const s = await createAndConnect(t)
538
+ const persistence = s.broker.persistence
630
539
 
631
- let packetStored = false
632
-
633
- const fn = broker.persistence.incomingStorePacket.bind(broker.persistence)
634
-
635
- s.broker.persistence.incomingStorePacket = function (client, packet, done) {
636
- packetStored = true
637
- t.pass('packet stored')
638
- fn(client, packet, done)
639
- }
540
+ t.mock.method(persistence, 'incomingStorePacket')
640
541
 
641
542
  const packet = {
642
543
  cmd: 'publish',
@@ -646,30 +547,19 @@ test('packet is written to stream after being stored', function (t) {
646
547
  messageId: 42
647
548
  }
648
549
 
649
- publish(t, s, packet)
650
-
651
- s.outStream.once('data', function (packet) {
652
- t.equal(packet.cmd, 'pubrec', 'pubrec received')
653
- t.equal(packetStored, true, 'after packet store')
654
- t.end()
655
- })
550
+ await publish(t, s, packet)
551
+ t.assert.equal(persistence.incomingStorePacket.mock.callCount(), 1, 'after packet store')
656
552
  })
657
553
 
658
- test('not send pubrec when persistence fails to store packet', function (t) {
659
- t.plan(2)
660
-
661
- const s = connect(setup())
662
- const broker = s.broker
554
+ test('not send pubrec when persistence fails to store packet', async (t) => {
555
+ t.plan(3)
663
556
 
664
- t.teardown(broker.close.bind(s.broker))
557
+ const s = await createAndConnect(t)
665
558
 
666
- s.broker.persistence.incomingStorePacket = function (client, packet, done) {
667
- t.pass('packet stored')
668
- done(new Error('store error'))
559
+ s.broker.persistence.incomingStorePacket = async () => {
560
+ t.assert.ok(true, 'packet stored')
561
+ throw new Error('store error')
669
562
  }
670
- s.broker.on('clientError', function (client, err) {
671
- t.equal(err.message, 'store error')
672
- })
673
563
 
674
564
  const packet = {
675
565
  cmd: 'publish',
@@ -680,7 +570,32 @@ test('not send pubrec when persistence fails to store packet', function (t) {
680
570
  }
681
571
 
682
572
  s.inStream.write(packet)
683
- s.outStream.once('data', function (packet) {
684
- t.fail('should not have pubrec')
685
- })
573
+ const [client, err] = await once(s.broker, 'clientError')
574
+ t.assert.ok(client, 'client exists')
575
+ t.assert.equal(err.message, 'store error')
576
+ })
577
+
578
+ test('send pubcomp when receiving pubrel even if incomingDelPacket throws (no packet in store)', async (t) => {
579
+ t.plan(2)
580
+
581
+ const s = await createAndConnect(t)
582
+
583
+ // Mock incomingDelPacket to throw an error (simulating no packet in store)
584
+ s.broker.persistence.incomingDelPacket = async () => {
585
+ throw new Error('packet not found in store')
586
+ }
587
+
588
+ // Send a PUBREL packet directly
589
+ const pubrelPacket = {
590
+ cmd: 'pubrel',
591
+ messageId: 42,
592
+ dup: false,
593
+ }
594
+
595
+ s.inStream.write(pubrelPacket)
596
+
597
+ // Should receive a PUBCOMP response despite the error in incomingDelPacket
598
+ const pubcompPacket = await nextPacket(s)
599
+ t.assert.equal(pubcompPacket.cmd, 'pubcomp', 'should send pubcomp')
600
+ t.assert.equal(pubcompPacket.messageId, 42, 'messageId should match')
686
601
  })