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/retain.js CHANGED
@@ -1,13 +1,21 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const { through } = require('../lib/utils')
5
- const Faketimers = require('@sinonjs/fake-timers')
6
- const { setup, connect, subscribe, noError } = require('./helper')
7
- const aedes = require('../')
1
+ import { test } from 'node:test'
2
+ import { once } from 'node:events'
3
+ import {
4
+ brokerPublish,
5
+ checkNoPacket,
6
+ connect,
7
+ createAndConnect,
8
+ createPubSub,
9
+ delay,
10
+ nextPacket,
11
+ publish,
12
+ setup,
13
+ subscribe,
14
+ } from './helper.js'
15
+ import { Aedes } from '../aedes.js'
8
16
 
9
17
  // [MQTT-3.3.1-9]
10
- test('live retain packets', function (t) {
18
+ test('live retain packets', async (t) => {
11
19
  t.plan(5)
12
20
  const expected = {
13
21
  cmd: 'publish',
@@ -19,36 +27,28 @@ test('live retain packets', function (t) {
19
27
  qos: 0
20
28
  }
21
29
 
22
- const s = noError(connect(setup()), t)
23
- t.teardown(s.broker.close.bind(s.broker))
30
+ const s = await createAndConnect(t)
24
31
 
25
- subscribe(t, s, 'hello', 0, function () {
26
- s.outStream.on('data', function (packet) {
27
- t.same(packet, expected)
28
- })
32
+ await subscribe(t, s, 'hello', 0)
29
33
 
30
- s.broker.publish({
31
- cmd: 'publish',
32
- topic: 'hello',
33
- payload: Buffer.from('world'),
34
- retain: true,
35
- dup: false,
36
- length: 12,
37
- qos: 0
38
- }, function () {
39
- t.pass('publish finished')
40
- })
34
+ await brokerPublish(s, {
35
+ cmd: 'publish',
36
+ topic: 'hello',
37
+ payload: Buffer.from('world'),
38
+ retain: true,
39
+ dup: false,
40
+ length: 12,
41
+ qos: 0
41
42
  })
43
+ t.assert.ok(true, 'publish finished')
44
+ const packet = await nextPacket(s)
45
+ t.assert.deepEqual(structuredClone(packet), expected)
42
46
  })
43
47
 
44
- test('retain messages', function (t) {
48
+ test('retain messages', async (t) => {
45
49
  t.plan(4)
46
50
 
47
- const broker = aedes()
48
- t.teardown(broker.close.bind(broker))
49
-
50
- const publisher = connect(setup(broker))
51
- const subscriber = connect(setup(broker))
51
+ const { broker, publisher, subscriber } = await createPubSub(t)
52
52
  const expected = {
53
53
  cmd: 'publish',
54
54
  topic: 'hello',
@@ -59,28 +59,31 @@ test('retain messages', function (t) {
59
59
  retain: true
60
60
  }
61
61
 
62
- broker.subscribe('hello', function (packet, cb) {
63
- cb()
64
-
65
- // defer this or it will receive the message which
66
- // is being published
67
- setImmediate(function () {
68
- subscribe(t, subscriber, 'hello', 0, function () {
69
- subscriber.outStream.once('data', function (packet) {
70
- t.same(packet, expected, 'packet must match')
71
- })
62
+ const doSubscribe = async () => {
63
+ await new Promise((resolve) => {
64
+ broker.subscribe('hello', (packet, cb) => {
65
+ cb()
66
+ resolve()
72
67
  })
73
68
  })
74
- })
69
+ await subscribe(t, subscriber, 'hello', 0)
70
+ }
75
71
 
76
- publisher.inStream.write(expected)
72
+ // run parallel
73
+ await Promise.all([
74
+ doSubscribe(),
75
+ publisher.inStream.write(expected)
76
+ ])
77
+
78
+ const packet = await nextPacket(subscriber)
79
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
77
80
  })
78
81
 
79
- test('retain messages propagates through broker subscriptions', function (t) {
82
+ test('retain messages propagates through broker subscriptions', async (t) => {
80
83
  t.plan(1)
81
84
 
82
- const broker = aedes()
83
- t.teardown(broker.close.bind(broker))
85
+ const broker = await Aedes.createBroker()
86
+ t.after(() => broker.close())
84
87
 
85
88
  const expected = {
86
89
  cmd: 'publish',
@@ -91,29 +94,28 @@ test('retain messages propagates through broker subscriptions', function (t) {
91
94
  retain: true
92
95
  }
93
96
 
94
- const subscriberFunc = function (packet, cb) {
95
- packet = Object.assign({}, packet)
96
- delete packet.brokerId
97
- delete packet.brokerCounter
98
- cb()
99
- setImmediate(function () {
100
- t.same(packet, expected, 'packet must not have been modified')
101
- })
102
- }
97
+ await new Promise((resolve) => {
98
+ const subscriberFunc = (packet, cb) => {
99
+ packet = Object.assign({}, packet)
100
+ delete packet.brokerId
101
+ delete packet.brokerCounter
102
+ cb()
103
+ setImmediate(() => {
104
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must not have been modified')
105
+ resolve()
106
+ })
107
+ }
103
108
 
104
- broker.subscribe('hello', subscriberFunc, function () {
105
- broker.publish(expected)
109
+ broker.subscribe('hello', subscriberFunc, () => {
110
+ broker.publish(expected)
111
+ })
106
112
  })
107
113
  })
108
114
 
109
- test('avoid wrong deduping of retain messages', function (t) {
110
- t.plan(7)
111
-
112
- const broker = aedes()
113
- t.teardown(broker.close.bind(broker))
115
+ test('avoid wrong deduping of retain messages', async (t) => {
116
+ t.plan(8)
114
117
 
115
- const publisher = connect(setup(broker))
116
- const subscriber = connect(setup(broker))
118
+ const { broker, publisher, subscriber } = await createPubSub(t)
117
119
  const expected = {
118
120
  cmd: 'publish',
119
121
  topic: 'hello',
@@ -124,41 +126,46 @@ test('avoid wrong deduping of retain messages', function (t) {
124
126
  retain: true
125
127
  }
126
128
 
127
- broker.subscribe('hello', function (packet, cb) {
128
- cb()
129
- // subscribe and publish another topic
130
- subscribe(t, subscriber, 'hello2', 0, function () {
131
- cb()
132
-
133
- publisher.inStream.write({
134
- cmd: 'publish',
135
- topic: 'hello2',
136
- payload: Buffer.from('world'),
137
- qos: 0,
138
- dup: false
139
- })
140
-
141
- subscriber.outStream.once('data', function (packet) {
142
- subscribe(t, subscriber, 'hello', 0, function () {
143
- subscriber.outStream.once('data', function (packet) {
144
- t.same(packet, expected, 'packet must match')
145
- })
146
- })
129
+ const doSubscribe = async () => {
130
+ await new Promise((resolve) => {
131
+ broker.subscribe('hello', (packet, cb) => {
132
+ cb()
133
+ resolve()
147
134
  })
148
135
  })
149
- })
150
-
151
- publisher.inStream.write(expected)
136
+ // subscribe and publish another topic
137
+ await subscribe(t, subscriber, 'hello2', 0, 10)
138
+ publisher.inStream.write({
139
+ cmd: 'publish',
140
+ topic: 'hello2',
141
+ payload: Buffer.from('world'),
142
+ qos: 0,
143
+ dup: false
144
+ })
145
+ // receive publish and verify topic
146
+ const packet = await nextPacket(subscriber)
147
+ t.assert.deepEqual(packet.topic, 'hello2', 'packet must match')
148
+ }
149
+ // run parallel
150
+ await Promise.all([
151
+ doSubscribe(),
152
+ publisher.inStream.write(expected)
153
+ ])
154
+
155
+ // get the retained message
156
+ await subscribe(t, subscriber, 'hello', 0)
157
+ const packet = await nextPacket(subscriber)
158
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
152
159
  })
153
160
 
154
- test('reconnected subscriber will not receive retained messages when QoS 0 and clean', function (t) {
155
- t.plan(4)
161
+ test('reconnected subscriber will not receive retained messages when QoS 0 and clean', async (t) => {
162
+ t.plan(5)
156
163
 
157
- const broker = aedes()
158
- t.teardown(broker.close.bind(broker))
164
+ const { broker, publisher, subscriber } = await createPubSub(t, {
165
+ publisher: { clean: true },
166
+ subscriber: { clean: true }
167
+ })
159
168
 
160
- const publisher = connect(setup(broker), { clean: true })
161
- let subscriber = connect(setup(broker), { clean: true })
162
169
  const expected = {
163
170
  cmd: 'publish',
164
171
  topic: 'hello',
@@ -168,37 +175,37 @@ test('reconnected subscriber will not receive retained messages when QoS 0 and c
168
175
  dup: false,
169
176
  length: 12
170
177
  }
171
- subscribe(t, subscriber, 'hello', 0, function () {
172
- publisher.inStream.write({
173
- cmd: 'publish',
174
- topic: 'hello',
175
- payload: 'world',
176
- qos: 0,
177
- retain: false
178
- })
179
- subscriber.outStream.once('data', function (packet) {
180
- t.same(packet, expected, 'packet must match')
181
- subscriber.inStream.end()
182
- publisher.inStream.write({
183
- cmd: 'publish',
184
- topic: 'hello',
185
- payload: 'foo',
186
- qos: 0,
187
- retain: true
188
- })
189
- subscriber = connect(setup(broker), { clean: true })
190
- subscriber.outStream.on('data', function (packet) {
191
- t.fail('should not received retain message')
192
- })
193
- })
178
+
179
+ await subscribe(t, subscriber, 'hello', 0)
180
+ publisher.inStream.write({
181
+ cmd: 'publish',
182
+ topic: 'hello',
183
+ payload: 'world',
184
+ qos: 0,
185
+ retain: false
194
186
  })
187
+ const packet = await nextPacket(subscriber)
188
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
189
+ subscriber.inStream.end()
190
+ publisher.inStream.write({
191
+ cmd: 'publish',
192
+ topic: 'hello',
193
+ payload: 'foo',
194
+ qos: 0,
195
+ retain: true
196
+ })
197
+ const subscriber2 = setup(broker)
198
+ await connect(subscriber2, { connect: { clean: true } })
199
+ await checkNoPacket(t, subscriber2, 10)
195
200
  })
196
201
 
197
- test('subscriber will not receive retained messages when QoS is 128', function (t) {
198
- t.plan(3)
202
+ test('subscriber will not receive retained messages when QoS is 128', async (t) => {
203
+ t.plan(5)
199
204
 
200
- const broker = aedes()
201
- t.teardown(broker.close.bind(broker))
205
+ const { broker, publisher, subscriber } = await createPubSub(t, {
206
+ publisher: { clean: true },
207
+ subscriber: { clean: true }
208
+ })
202
209
 
203
210
  const pubPacket = {
204
211
  cmd: 'publish',
@@ -209,7 +216,7 @@ test('subscriber will not receive retained messages when QoS is 128', function (
209
216
  messageId: 42
210
217
  }
211
218
 
212
- broker.authorizeSubscribe = function (client, sub, callback) {
219
+ broker.authorizeSubscribe = (client, sub, callback) => {
213
220
  if (sub.topic === pubPacket.topic) {
214
221
  callback(null, null)
215
222
  } else {
@@ -217,32 +224,20 @@ test('subscriber will not receive retained messages when QoS is 128', function (
217
224
  }
218
225
  }
219
226
 
220
- const publisher = connect(setup(broker), { clean: true })
221
-
222
- publisher.inStream.write(pubPacket)
223
-
224
- publisher.outStream.on('data', function (packet) {
225
- const subscriber = connect(setup(broker), { clean: true })
226
- subscribe(t, subscriber, pubPacket.topic, 128, function () {
227
- subscriber.outStream.on('data', function (packet) {
228
- t.fail('should not received retain message')
229
- })
230
- })
231
- })
227
+ await publish(t, publisher, pubPacket)
228
+ await subscribe(t, subscriber, pubPacket.topic, 128)
229
+ await checkNoPacket(t, subscriber, 10)
232
230
  })
233
231
 
234
232
  // [MQTT-3.3.1-6]
235
- test('new QoS 0 subscribers receive QoS 0 retained messages when clean', function (t) {
233
+ test('new QoS 0 subscribers receive QoS 0 retained messages when clean', async (t) => {
236
234
  t.plan(9)
237
235
 
238
- const clock = Faketimers.createClock()
239
- const broker = aedes()
240
- t.teardown(function () {
241
- clock.reset()
242
- broker.close()
236
+ const { broker, publisher, subscriber: subscriber1 } = await createPubSub(t, {
237
+ publisher: { clean: true },
238
+ subscriber: { clean: true }
243
239
  })
244
240
 
245
- const publisher = connect(setup(broker), { clean: true })
246
241
  const expected = {
247
242
  cmd: 'publish',
248
243
  topic: 'hello/world',
@@ -259,33 +254,28 @@ test('new QoS 0 subscribers receive QoS 0 retained messages when clean', functio
259
254
  qos: 0,
260
255
  retain: true
261
256
  })
262
- const subscriber1 = connect(setup(broker), { clean: true })
263
- subscribe(t, subscriber1, 'hello/world', 0, function () {
264
- subscriber1.outStream.on('data', function (packet) {
265
- t.same(packet, expected, 'packet must match')
266
- clock.tick(100)
267
- })
268
- })
269
- const subscriber2 = connect(setup(broker), { clean: true })
270
- subscribe(t, subscriber2, 'hello/+', 0, function () {
271
- subscriber2.outStream.on('data', function (packet) {
272
- t.same(packet, expected, 'packet must match')
273
- clock.tick(100)
274
- })
275
- })
276
257
 
277
- clock.setTimeout(() => {
278
- t.equal(broker.counter, 9)
279
- }, 200)
258
+ await subscribe(t, subscriber1, 'hello/world', 0)
259
+ const packet1 = await nextPacket(subscriber1)
260
+ t.assert.deepEqual(structuredClone(packet1), expected, 'packet must match')
261
+
262
+ const subscriber2 = setup(broker)
263
+ await connect(subscriber2, { connect: { clean: true } })
264
+ await subscribe(t, subscriber2, 'hello/+', 0)
265
+ const packet2 = await nextPacket(subscriber2)
266
+ t.assert.deepEqual(structuredClone(packet2), expected, 'packet must match')
267
+ t.assert.equal(broker.counter, 9)
280
268
  })
281
269
 
282
270
  // [MQTT-3.3.1-5]
283
- test('new QoS 0 subscribers receive downgraded QoS 1 retained messages when clean', function (t) {
284
- t.plan(6)
271
+ test('new QoS 0 subscribers receive downgraded QoS 1 retained messages when clean', async (t) => {
272
+ t.plan(7)
285
273
 
286
- const broker = aedes()
274
+ const publisher = await createAndConnect(t, {
275
+ connect: { clean: true },
276
+ })
277
+ const broker = publisher.broker
287
278
 
288
- const publisher = connect(setup(broker), { clean: true })
289
279
  const expected = {
290
280
  cmd: 'publish',
291
281
  topic: 'hello',
@@ -295,7 +285,7 @@ test('new QoS 0 subscribers receive downgraded QoS 1 retained messages when clea
295
285
  dup: false,
296
286
  length: 12
297
287
  }
298
- publisher.inStream.write({
288
+ await publish(t, publisher, {
299
289
  cmd: 'publish',
300
290
  topic: 'hello',
301
291
  payload: 'world',
@@ -303,30 +293,29 @@ test('new QoS 0 subscribers receive downgraded QoS 1 retained messages when clea
303
293
  retain: true,
304
294
  messageId: 42
305
295
  })
306
- publisher.outStream.on('data', function (packet) {
307
- const subscriber = connect(setup(broker), { clean: true })
308
- subscribe(t, subscriber, 'hello', 0, function () {
309
- subscriber.outStream.on('data', function (packet) {
310
- t.not(packet.messageId, 42, 'messageId should not be the same')
311
- delete packet.messageId
312
- t.same(packet, expected, 'packet must match')
313
- broker.close()
314
- })
315
- })
316
- })
317
- broker.on('closed', function () {
318
- t.equal(broker.counter, 8)
319
- })
296
+
297
+ const subscriber = setup(broker)
298
+ await connect(subscriber, { connect: { clean: true } })
299
+ await subscribe(t, subscriber, 'hello', 0)
300
+ const packet = await nextPacket(subscriber)
301
+ t.assert.ok(packet.messageId !== 42, 'messageId should not be the same')
302
+ delete packet.messageId
303
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
304
+ broker.close()
305
+
306
+ await once(broker, 'closed')
307
+ t.assert.equal(broker.counter, 9)
320
308
  })
321
309
 
322
310
  // [MQTT-3.3.1-10]
323
- test('clean retained messages', function (t) {
324
- t.plan(3)
311
+ test('clean retained messages', async (t) => {
312
+ t.plan(4)
325
313
 
326
- const broker = aedes()
327
- t.teardown(broker.close.bind(broker))
314
+ const publisher = await createAndConnect(t, {
315
+ connect: { clean: true },
316
+ })
317
+ const broker = publisher.broker
328
318
 
329
- const publisher = connect(setup(broker), { clean: true })
330
319
  publisher.inStream.write({
331
320
  cmd: 'publish',
332
321
  topic: 'hello',
@@ -341,47 +330,48 @@ test('clean retained messages', function (t) {
341
330
  qos: 0,
342
331
  retain: true
343
332
  })
344
- const subscriber = connect(setup(broker), { clean: true })
345
- subscribe(t, subscriber, 'hello', 0, function () {
346
- subscriber.outStream.once('data', function (packet) {
347
- t.fail('should not received retain message')
348
- })
349
- })
333
+ const subscriber = setup(broker)
334
+ await connect(subscriber, { connect: { clean: true } })
335
+ await subscribe(t, subscriber, 'hello', 0)
336
+ await checkNoPacket(t, subscriber, 10)
350
337
  })
351
338
 
352
339
  // [MQTT-3.3.1-11]
353
- test('broker not store zero-byte retained messages', function (t) {
354
- t.plan(0)
355
-
356
- const broker = aedes()
357
- t.teardown(broker.close.bind(broker))
358
-
359
- const s = connect(setup(broker))
340
+ test('broker not store zero-byte retained messages', async (t) => {
341
+ t.plan(1)
360
342
 
343
+ const s = await createAndConnect(t)
361
344
  s.inStream.write({
362
345
  cmd: 'publish',
363
346
  topic: 'hello',
364
347
  payload: '',
365
348
  retain: true
366
349
  })
367
- s.broker.on('publish', function (packet, client) {
368
- if (packet.topic.startsWith('$SYS/')) {
369
- return
370
- }
371
- const stream = s.broker.persistence.createRetainedStream(packet.topic)
372
- stream.pipe(through(function sendRetained (packet, enc, cb) {
373
- t.fail('not store zero-byte retained messages')
374
- }))
350
+
351
+ await new Promise(resolve => {
352
+ s.broker.on('publish', async (packet, client) => {
353
+ if (packet.topic.startsWith('$SYS/')) {
354
+ return
355
+ }
356
+ const stream = s.broker.persistence.createRetainedStream(packet.topic)
357
+ const result = await stream.toArray()
358
+ if (result?.[0] === undefined) {
359
+ t.assert.ok(true, 'no zero-byte retained messages stored')
360
+ } else {
361
+ t.assert.fail('zero-byte retained messages should not be stored')
362
+ }
363
+ resolve()
364
+ })
375
365
  })
376
366
  })
377
367
 
378
- test('fail to clean retained messages without retain flag', function (t) {
368
+ test('fail to clean retained messages without retain flag', async (t) => {
379
369
  t.plan(4)
380
370
 
381
- const broker = aedes()
382
- t.teardown(broker.close.bind(broker))
383
-
384
- const publisher = connect(setup(broker), { clean: true })
371
+ const publisher = await createAndConnect(t, {
372
+ connect: { clean: true },
373
+ })
374
+ const broker = publisher.broker
385
375
  const expected = {
386
376
  cmd: 'publish',
387
377
  topic: 'hello',
@@ -405,21 +395,20 @@ test('fail to clean retained messages without retain flag', function (t) {
405
395
  qos: 0,
406
396
  retain: false
407
397
  })
408
- const subscriber = connect(setup(broker), { clean: true })
409
- subscribe(t, subscriber, 'hello', 0, function () {
410
- subscriber.outStream.on('data', function (packet) {
411
- t.same(packet, expected, 'packet must match')
412
- })
413
- })
398
+ const subscriber = setup(broker)
399
+ await connect(subscriber, { connect: { clean: true } })
400
+ await subscribe(t, subscriber, 'hello', 0)
401
+ const packet = await nextPacket(subscriber)
402
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
414
403
  })
415
404
 
416
- test('only get the last retained messages in same topic', function (t) {
405
+ test('only get the last retained messages in same topic', async (t) => {
417
406
  t.plan(4)
418
407
 
419
- const broker = aedes()
420
- t.teardown(broker.close.bind(broker))
421
-
422
- const publisher = connect(setup(broker), { clean: true })
408
+ const publisher = await createAndConnect(t, {
409
+ connect: { clean: true },
410
+ })
411
+ const broker = publisher.broker
423
412
  const expected = {
424
413
  cmd: 'publish',
425
414
  topic: 'hello',
@@ -443,22 +432,21 @@ test('only get the last retained messages in same topic', function (t) {
443
432
  qos: 0,
444
433
  retain: true
445
434
  })
446
- const subscriber = connect(setup(broker), { clean: true })
447
- subscribe(t, subscriber, 'hello', 0, function () {
448
- subscriber.outStream.on('data', function (packet) {
449
- t.same(packet, expected, 'packet must match')
450
- })
451
- })
435
+ const subscriber = setup(broker)
436
+ await connect(subscriber, { connect: { clean: true } })
437
+ await subscribe(t, subscriber, 'hello', 0)
438
+ const packet = await nextPacket(subscriber)
439
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
452
440
  })
453
441
 
454
- test('deliver QoS 1 retained messages to new subscriptions', function (t) {
455
- t.plan(4)
442
+ test('deliver QoS 1 retained messages to new subscriptions', async (t) => {
443
+ t.plan(5)
456
444
 
457
- const broker = aedes()
458
- t.teardown(broker.close.bind(broker))
445
+ const { publisher, subscriber } = await createPubSub(t, {
446
+ publisher: { clean: true },
447
+ subscriber: { clean: true }
448
+ })
459
449
 
460
- const publisher = connect(setup(broker))
461
- const subscriber = connect(setup(broker))
462
450
  const expected = {
463
451
  cmd: 'publish',
464
452
  topic: 'hello',
@@ -469,7 +457,7 @@ test('deliver QoS 1 retained messages to new subscriptions', function (t) {
469
457
  retain: true
470
458
  }
471
459
 
472
- publisher.inStream.write({
460
+ await publish(t, publisher, {
473
461
  cmd: 'publish',
474
462
  topic: 'hello',
475
463
  payload: 'world',
@@ -478,24 +466,17 @@ test('deliver QoS 1 retained messages to new subscriptions', function (t) {
478
466
  retain: true
479
467
  })
480
468
 
481
- publisher.outStream.on('data', function (packet) {
482
- subscribe(t, subscriber, 'hello', 1, function () {
483
- subscriber.outStream.once('data', function (packet) {
484
- delete packet.messageId
485
- t.same(packet, expected, 'packet must match')
486
- })
487
- })
488
- })
469
+ await delay(10) // give Aedes some time to process the publish
470
+ await subscribe(t, subscriber, 'hello', 1)
471
+ const packet = await nextPacket(subscriber)
472
+ delete packet.messageId
473
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
489
474
  })
490
475
 
491
- test('deliver QoS 1 retained messages to established subscriptions', function (t) {
476
+ test('deliver QoS 1 retained messages to established subscriptions', async (t) => {
492
477
  t.plan(4)
493
478
 
494
- const broker = aedes()
495
- t.teardown(broker.close.bind(broker))
496
-
497
- const publisher = connect(setup(broker))
498
- const subscriber = connect(setup(broker))
479
+ const { publisher, subscriber } = await createPubSub(t)
499
480
  const expected = {
500
481
  cmd: 'publish',
501
482
  topic: 'hello',
@@ -506,30 +487,25 @@ test('deliver QoS 1 retained messages to established subscriptions', function (t
506
487
  retain: false
507
488
  }
508
489
 
509
- subscribe(t, subscriber, 'hello', 1, function () {
510
- subscriber.outStream.once('data', function (packet) {
511
- delete packet.messageId
512
- t.same(packet, expected, 'packet must match')
513
- })
514
- publisher.inStream.write({
515
- cmd: 'publish',
516
- topic: 'hello',
517
- payload: 'world',
518
- qos: 1,
519
- messageId: 42,
520
- retain: true
521
- })
490
+ await subscribe(t, subscriber, 'hello', 1)
491
+
492
+ publisher.inStream.write({
493
+ cmd: 'publish',
494
+ topic: 'hello',
495
+ payload: 'world',
496
+ qos: 1,
497
+ messageId: 42,
498
+ retain: true
522
499
  })
500
+ const packet = await nextPacket(subscriber)
501
+ delete packet.messageId
502
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
523
503
  })
524
504
 
525
- test('deliver QoS 0 retained message with QoS 1 subscription', function (t) {
505
+ test('deliver QoS 0 retained message with QoS 1 subscription', async (t) => {
526
506
  t.plan(4)
527
507
 
528
- const broker = aedes()
529
- t.teardown(broker.close.bind(broker))
530
-
531
- const publisher = connect(setup(broker))
532
- const subscriber = connect(setup(broker))
508
+ const { broker, publisher, subscriber } = await createPubSub(t)
533
509
  const expected = {
534
510
  cmd: 'publish',
535
511
  topic: 'hello',
@@ -540,37 +516,45 @@ test('deliver QoS 0 retained message with QoS 1 subscription', function (t) {
540
516
  retain: true
541
517
  }
542
518
 
543
- broker.mq.on('hello', function (msg, cb) {
544
- cb()
545
-
546
- // defer this or it will receive the message which
547
- // is being published
548
- setImmediate(function () {
549
- subscribe(t, subscriber, 'hello', 1, function () {
550
- subscriber.outStream.once('data', function (packet) {
551
- t.same(packet, expected, 'packet must match')
552
- })
519
+ const checkOnBroker = async () => {
520
+ await new Promise(resolve => {
521
+ broker.mq.on('hello', (msg, cb) => {
522
+ cb()
523
+ // defer this or it will receive the message which
524
+ // is being published
525
+ resolve()
553
526
  })
554
527
  })
555
- })
528
+ await subscribe(t, subscriber, 'hello', 1)
529
+ const packet = await nextPacket(subscriber)
530
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
531
+ }
556
532
 
557
- publisher.inStream.write({
558
- cmd: 'publish',
559
- topic: 'hello',
560
- payload: Buffer.from('world'),
561
- qos: 0,
562
- messageId: 42,
563
- retain: true
564
- })
533
+ const doPublish = () => {
534
+ publisher.inStream.write({
535
+ cmd: 'publish',
536
+ topic: 'hello',
537
+ payload: Buffer.from('world'),
538
+ qos: 0,
539
+ messageId: 42,
540
+ retain: true
541
+ })
542
+ }
543
+ // run parallel
544
+ await Promise.all([
545
+ checkOnBroker(),
546
+ doPublish()
547
+ ])
565
548
  })
566
549
 
567
- test('disconnect and retain messages with QoS 1 [clean=false]', function (t) {
568
- t.plan(7)
550
+ test('disconnect and retain messages with QoS 1 [clean=false]', async (t) => {
551
+ t.plan(8)
569
552
 
570
- const broker = aedes()
571
- t.teardown(broker.close.bind(broker))
553
+ const { broker, publisher, subscriber } = await createPubSub(t, {
554
+ publisher: { clean: false },
555
+ subscriber: { clean: false, clientId: 'abcde' }
556
+ })
572
557
 
573
- let subscriber = noError(connect(setup(broker), { clean: false, clientId: 'abcde' }), t)
574
558
  const expected = {
575
559
  cmd: 'publish',
576
560
  topic: 'hello',
@@ -581,56 +565,41 @@ test('disconnect and retain messages with QoS 1 [clean=false]', function (t) {
581
565
  retain: true
582
566
  }
583
567
 
584
- subscribe(t, subscriber, 'hello', 1, function () {
585
- subscriber.inStream.write({
586
- cmd: 'disconnect'
587
- })
588
-
589
- subscriber.outStream.on('data', function (packet) {
590
- console.log('original', packet)
591
- })
592
-
593
- const publisher = noError(connect(setup(broker)), t)
594
-
595
- publisher.inStream.write({
596
- cmd: 'publish',
597
- topic: 'hello',
598
- payload: 'world',
599
- qos: 1,
600
- messageId: 42,
601
- retain: true
602
- })
603
-
604
- publisher.outStream.once('data', function (packet) {
605
- t.equal(packet.cmd, 'puback')
606
-
607
- subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' }, function (connect) {
608
- t.equal(connect.sessionPresent, true, 'session present is set to true')
609
- })
568
+ await subscribe(t, subscriber, 'hello', 1)
569
+ subscriber.inStream.write({
570
+ cmd: 'disconnect'
571
+ })
610
572
 
611
- subscriber.outStream.once('data', function (packet) {
612
- // receive any queued messages (no matter they are retained messages) at the disconnected time
613
- t.not(packet.messageId, 42, 'messageId must differ')
614
- delete packet.messageId
615
- packet.length = 14
616
- t.same(packet, expected, 'packet must match')
617
-
618
- // there should be no messages come from restored subscriptions
619
- subscriber.outStream.once('data', function (packet) {
620
- t.fail('should not receive any more messages')
621
- })
622
- })
623
- })
573
+ await publish(t, publisher, {
574
+ cmd: 'publish',
575
+ topic: 'hello',
576
+ payload: 'world',
577
+ qos: 1,
578
+ messageId: 42,
579
+ retain: true
624
580
  })
581
+
582
+ const subscriber2 = setup(broker)
583
+ const connack = await connect(subscriber2, { connect: { clean: false, clientId: 'abcde' } })
584
+ t.assert.equal(connack.sessionPresent, true, 'session present is set to true')
585
+
586
+ const packet = await nextPacket(subscriber2)
587
+ // receive any queued messages (no matter they are retained messages) at the disconnected time
588
+ t.assert.ok(packet.messageId !== 42, 'messageId must differ')
589
+ delete packet.messageId
590
+ packet.length = 14
591
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
592
+ // expect no more packets
593
+ await checkNoPacket(t, subscriber2, 10)
625
594
  })
626
595
 
627
- test('disconnect and two retain messages with QoS 1 [clean=false]', function (t) {
596
+ test('disconnect and two retain messages with QoS 1 [clean=false]', async (t) => {
628
597
  t.plan(15)
629
598
 
630
- const broker = aedes()
631
- t.teardown(broker.close.bind(broker))
599
+ const { broker, publisher, subscriber } = await createPubSub(t, {
600
+ subscriber: { clean: false, clientId: 'abcde' }
601
+ })
632
602
 
633
- let subscriber = noError(connect(setup(broker), { clean: false, clientId: 'abcde' }), t)
634
603
  const expected = {
635
604
  cmd: 'publish',
636
605
  topic: 'hello',
@@ -640,74 +609,56 @@ test('disconnect and two retain messages with QoS 1 [clean=false]', function (t)
640
609
  retain: true
641
610
  }
642
611
 
643
- subscribe(t, subscriber, 'hello', 1, function () {
644
- subscriber.inStream.write({
645
- cmd: 'disconnect'
646
- })
647
-
648
- subscriber.outStream.on('data', function (packet) {
649
- console.log('original', packet)
650
- })
651
-
652
- const publisher = noError(connect(setup(broker)), t)
653
-
654
- publisher.inStream.write({
655
- cmd: 'publish',
656
- topic: 'hello',
657
- payload: 'world',
658
- qos: 1,
659
- messageId: 41,
660
- retain: true
661
- })
662
-
663
- publisher.outStream.once('data', function (packet) {
664
- t.equal(packet.cmd, 'puback')
612
+ await subscribe(t, subscriber, 'hello', 1)
613
+ await delay(10)
614
+ subscriber.inStream.write({
615
+ cmd: 'disconnect'
616
+ })
665
617
 
666
- publisher.inStream.write({
667
- cmd: 'publish',
668
- topic: 'hello',
669
- payload: 'world2',
670
- qos: 1,
671
- messageId: 42,
672
- retain: true
673
- })
618
+ await publish(t, publisher, {
619
+ cmd: 'publish',
620
+ topic: 'hello',
621
+ payload: 'world',
622
+ qos: 1,
623
+ messageId: 41,
624
+ retain: true
625
+ })
674
626
 
675
- publisher.outStream.once('data', function (packet) {
676
- t.equal(packet.cmd, 'puback')
677
-
678
- subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' }, function (connect) {
679
- t.equal(connect.sessionPresent, true, 'session present is set to true')
680
- })
681
-
682
- subscriber.outStream.once('data', function (packet) {
683
- // receive any queued messages (included retained messages) at the disconnected time
684
- t.not(packet.messageId, 41, 'messageId must differ')
685
- delete packet.messageId
686
- packet.length = 14
687
- expected.payload = Buffer.from('world')
688
- t.same(packet, expected, 'packet must match')
689
-
690
- // receive any queued messages (included retained messages) at the disconnected time
691
- subscriber.outStream.once('data', function (packet) {
692
- t.not(packet.messageId, 42, 'messageId must differ')
693
- delete packet.messageId
694
- packet.length = 14
695
- expected.payload = Buffer.from('world2')
696
- t.same(packet, expected, 'packet must match')
697
-
698
- // should get the last retained message when we do a subscribe
699
- subscribe(t, subscriber, 'hello', 1, function () {
700
- subscriber.outStream.on('data', function (packet) {
701
- t.not(packet.messageId, 42, 'messageId must differ')
702
- delete packet.messageId
703
- packet.length = 14
704
- expected.payload = Buffer.from('world2')
705
- t.same(packet, expected, 'packet must match')
706
- })
707
- })
708
- })
709
- })
710
- })
711
- })
627
+ await publish(t, publisher, {
628
+ cmd: 'publish',
629
+ topic: 'hello',
630
+ payload: 'world2',
631
+ qos: 1,
632
+ messageId: 42,
633
+ retain: true
712
634
  })
635
+
636
+ const subscriber2 = setup(broker)
637
+ const connack = await connect(subscriber2, { connect: { clean: false, clientId: 'abcde' } })
638
+ t.assert.equal(connack.sessionPresent, true, 'session present is set to true')
639
+
640
+ const packet = await nextPacket(subscriber2)
641
+ // receive any queued messages (included retained messages) at the disconnected time
642
+ t.assert.ok(packet.messageId !== 41, 'messageId must differ')
643
+ delete packet.messageId
644
+ packet.length = 14
645
+ expected.payload = Buffer.from('world')
646
+ t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
647
+
648
+ // receive any queued messages (included retained messages) at the disconnected time
649
+ const packet2 = await nextPacket(subscriber2)
650
+ t.assert.ok(packet2.messageId !== 42, 'messageId must differ')
651
+ delete packet2.messageId
652
+ packet2.length = 14
653
+ expected.payload = Buffer.from('world2')
654
+ t.assert.deepEqual(structuredClone(packet2), expected, 'packet must match')
655
+
656
+ // should get the last retained message when we do a subscribe
657
+ await subscribe(t, subscriber2, 'hello', 1)
658
+ const packet3 = await nextPacket(subscriber2)
659
+ t.assert.ok(packet3.messageId !== 42, 'messageId must differ')
660
+ delete packet3.messageId
661
+ packet3.length = 14
662
+ expected.payload = Buffer.from('world2')
663
+ t.assert.deepEqual(structuredClone(packet3), expected, 'packet must match')
713
664
  })