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/helper.js CHANGED
@@ -1,119 +1,397 @@
1
- 'use strict'
1
+ import { setTimeout as delay } from 'node:timers/promises'
2
+ import { duplexPair, Transform } from 'node:stream'
3
+ import { platform } from 'node:os'
4
+ import mqtt from 'mqtt-packet'
5
+ import { Aedes } from '../aedes.js'
2
6
 
3
- const duplexify = require('duplexify')
4
- const mqtt = require('mqtt-connection')
5
- const { through } = require('../lib/utils')
6
- const util = require('util')
7
- const aedes = require('../')
8
-
9
- const parseStream = mqtt.parseStream
10
- const generateStream = mqtt.generateStream
11
7
  let clients = 0
12
8
 
13
- function setup (broker) {
14
- const inStream = generateStream()
15
- const outStream = parseStream()
16
- const conn = duplexify(outStream, inStream)
9
+ /**
10
+ * Check if tests should be skipped on Windows and macOS platforms
11
+ * These platforms often lack proper support for certain network features
12
+ * like socket.readStop() or have issues with Docker/Testcontainers
13
+ * @returns {boolean} true if tests should be skipped on this platform
14
+ */
15
+ export function shouldSkipOnWindowsAndMac () {
16
+ const os = platform()
17
+ return os === 'win32' || os === 'darwin'
18
+ }
19
+
20
+ export function setup (broker) {
21
+ const [client, server] = duplexPair()
22
+ const inStream = new Transform(
23
+ {
24
+ objectMode: true,
25
+ transform (chunk, enc, callback) {
26
+ this.push(mqtt.generate(chunk))
27
+ callback()
28
+ }
29
+ })
30
+ inStream.pipe(client)
31
+ const outStream = packetGenerator(mqtt.parser(), client)
32
+
33
+ const serverClosed = () => {
34
+ client.destroy()
35
+ }
36
+
37
+ const clientClosed = () => {
38
+ server.destroy()
39
+ }
17
40
 
18
- broker = broker || aedes()
41
+ server.on('close', serverClosed)
42
+ server.on('end', serverClosed)
43
+ client.on('close', clientClosed)
44
+ client.on('end', clientClosed)
19
45
 
20
46
  return {
21
- client: broker.handle(conn),
22
- conn,
23
- inStream,
47
+ client: broker.handle(server),
48
+ conn: client,
24
49
  outStream,
50
+ inStream,
25
51
  broker
26
52
  }
27
53
  }
28
54
 
29
- function connect (s, opts, connected) {
30
- s = Object.create(s)
31
- s.outStream = s.outStream.pipe(through(filter))
55
+ /**
56
+ * Establishes an MQTT connection with the specified options
57
+ * @param {Object} s - The connection state object
58
+ * @param {Object} opts - MQTT connection options
59
+ * @returns {Object} the connack packet
60
+ */
32
61
 
33
- opts = opts || {}
62
+ export async function connect (s, opts = {}) {
63
+ const connect = opts.connect || {}
64
+ connect.cmd = 'connect'
65
+ connect.protocolId = connect.protocolId || 'MQTT'
66
+ connect.protocolVersion = connect.protocolVersion || 4
67
+ connect.clean = connect.clean !== false
68
+ connect.clientId = connect.clientId || 'my-client'
69
+ connect.username = connect.username || 'my username'
70
+ connect.password = connect.password || 'my pass'
71
+ connect.keepalive = connect.keepalive || 0
72
+ const expectedReturnCode = opts.expectedReturnCode || 0
73
+ const verifyIsConnack = opts.verifyIsConnack !== false
74
+ const verifyReturnedOk = verifyIsConnack ? opts.verifyReturnedOk !== false : false
75
+ const noWait = opts.noWait
34
76
 
35
- opts.cmd = 'connect'
36
- opts.protocolId = opts.protocolId || 'MQTT'
37
- opts.protocolVersion = opts.protocolVersion || 4
38
- opts.clean = !!opts.clean
39
- opts.clientId = opts.clientId || 'my-client-' + clients++
40
- opts.keepalive = opts.keepalive || 0
77
+ if (opts.autoClientId) {
78
+ connect.clientId = 'my-client-' + clients++
79
+ }
80
+ if (opts.noCredentials) {
81
+ connect.username = undefined
82
+ connect.password = undefined
83
+ }
41
84
 
42
- s.inStream.write(opts)
85
+ if (opts.noClientId) {
86
+ connect.clientId = undefined
87
+ }
88
+
89
+ s.inStream.write(connect)
90
+ if (noWait) {
91
+ return
92
+ }
93
+ const { value: connack } = await s.outStream.next()
94
+ if (verifyIsConnack && connack?.cmd !== 'connack') {
95
+ throw new Error('Expected connack')
96
+ }
97
+ if (verifyReturnedOk && (connack.returnCode !== expectedReturnCode)) {
98
+ throw new Error(`Expected connack return code ${expectedReturnCode} but received ${connack.returnCode}`)
99
+ }
100
+ return connack
101
+ }
43
102
 
103
+ /**
104
+ * Creates a new MQTT connection and establishes it
105
+ * @param {Object} t - Test assertion object
106
+ * @returns {Object} Connection state
107
+ */
108
+ export async function createAndConnect (t, opts = {}) {
109
+ const broker = await Aedes.createBroker(opts.broker)
110
+ t.after(() => broker.close())
111
+ const s = setup(broker)
112
+ await connect(s, opts)
44
113
  return s
114
+ }
45
115
 
46
- function filter (packet, enc, cb) {
47
- if (packet.cmd !== 'publish') {
48
- delete packet.topic
49
- delete packet.payload
50
- }
116
+ /**
117
+ * Creates a new MQTT connection and establishes it for publisher and subscriber
118
+ * @param {Object} t - Test assertion object
119
+ * @returns {Object} Connection state { broker, publisher, subscriber }
120
+ */
121
+ export async function createPubSub (t, opts = {}) {
122
+ const publisherOpts = { clientId: 'publisher', ...opts.publisher }
123
+ const subscriberOpts = { clientId: 'subscriber', ...opts.subscriber }
51
124
 
52
- // using setImmediate to wait for connected to be fired
53
- // setup also needs to return first
54
- if (packet.cmd !== 'connack') {
55
- setImmediate(this.push.bind(this, packet))
56
- } else if (connected && packet.returnCode === 0) {
57
- setImmediate(connected, packet)
58
- }
59
- cb()
60
- }
125
+ const broker = await Aedes.createBroker()
126
+ t.after(() => broker.close())
127
+
128
+ const publisher = setup(broker)
129
+ const subscriber = setup(broker)
130
+ await connect(publisher, { connect: publisherOpts })
131
+ await connect(subscriber, { connect: subscriberOpts })
132
+ return { broker, publisher, subscriber }
61
133
  }
62
134
 
63
- function noError (s, t) {
64
- s.broker.on('clientError', function (client, err) {
135
+ /**
136
+ * Sets up error handling for the broker connection
137
+ * @param {Object} s - The connection state object
138
+ * @param {Object} t - Test assertion object
139
+ * @returns {Object} Connection state with error handling
140
+ */
141
+ export function noError (s, t) {
142
+ s.broker.on('clientError', (client, err) => {
65
143
  if (err) throw err
66
- t.notOk(err, 'must not error')
144
+ t.assert.equal(err, undefined, 'must not error')
67
145
  })
68
-
69
146
  return s
70
147
  }
71
148
 
72
- function subscribe (t, subscriber, topic, qos, done) {
149
+ /**
150
+ * @param {Object} s - The connection state object
151
+ * @param {Object} packet - The packet to publish
152
+ * @returns {Promise} - Promise that resolves when the packet is published
153
+ */
154
+ export async function brokerPublish (s, packet) {
155
+ return new Promise((resolve) => {
156
+ s.broker.publish(packet, () => {
157
+ setImmediate(resolve)
158
+ })
159
+ })
160
+ }
161
+
162
+ /**
163
+ * publish a packet to the broker
164
+ * @param {Object} t - Test assertion object
165
+ * @param {Object} s - The connection state object
166
+ * @param {Object} packet - The packet to publish
167
+ * @returns {Promise} - Promise that resolves when the packet is published
168
+ */
169
+ export async function publish (t, s, packet) {
170
+ s.inStream.write(packet)
171
+ const msgId = packet.messageId
172
+ if (packet.qos === 1) {
173
+ const { value: puback } = await s.outStream.next()
174
+ t.assert.equal(puback.cmd, 'puback')
175
+ return puback
176
+ }
177
+ if (packet.qos === 2) {
178
+ const { value: pubrec } = await s.outStream.next()
179
+ t.assert.deepStrictEqual(structuredClone(pubrec), {
180
+ cmd: 'pubrec',
181
+ messageId: msgId,
182
+ length: 2,
183
+ dup: false,
184
+ retain: false,
185
+ qos: 0,
186
+ payload: null,
187
+ topic: null
188
+ }, 'pubrec must match')
189
+ s.inStream.write({
190
+ cmd: 'pubrel',
191
+ messageId: pubrec.messageId
192
+ })
193
+ const { value: pubcomp } = await s.outStream.next()
194
+ t.assert.deepEqual(structuredClone(pubcomp), {
195
+ cmd: 'pubcomp',
196
+ messageId: msgId,
197
+ length: 2,
198
+ dup: false,
199
+ retain: false,
200
+ qos: 0,
201
+ payload: null,
202
+ topic: null
203
+ }, 'pubcomp must match')
204
+ return pubcomp
205
+ }
206
+ return null
207
+ }
208
+ /**
209
+ * Subscribes to a single MQTT topic
210
+ * @param {Object} t - Test assertion object
211
+ * @param {Object} subscriber - The subscriber client
212
+ * @param {string} topic - Topic to subscribe to
213
+ * @param {number} qos - Quality of Service level
214
+ * @param {number} messageId - Message ID for the subscription
215
+ * @returns {Object} The subscription packet
216
+ */
217
+ export async function subscribe (t, subscriber, topic, qos, messageId = 24) {
73
218
  subscriber.inStream.write({
74
219
  cmd: 'subscribe',
75
- messageId: 24,
220
+ messageId,
76
221
  subscriptions: [{
77
222
  topic,
78
223
  qos
79
224
  }]
80
225
  })
81
226
 
82
- subscriber.outStream.once('data', function (packet) {
83
- t.equal(packet.cmd, 'suback')
84
- t.same(packet.granted, [qos])
85
- t.equal(packet.messageId, 24)
86
-
87
- if (done) {
88
- done(null, packet)
89
- }
90
- })
227
+ const { value: packet } = await subscriber.outStream.next()
228
+ t.assert.equal(packet.cmd, 'suback')
229
+ t.assert.equal(packet.granted[0], qos)
230
+ t.assert.equal(packet.messageId, messageId)
231
+ return packet
91
232
  }
92
233
 
93
- // subs: [{topic:, qos:}]
94
- function subscribeMultiple (t, subscriber, subs, expectedGranted, done) {
234
+ /**
235
+ * Subscribes to multiple MQTT topics
236
+ * @param {Object} t - Test assertion object
237
+ * @param {Object} subscriber - The subscriber client
238
+ * @param {Array<Object>} subs - Array of subscription objects with topic and qos
239
+ * @param {Array<number>} expectedGranted - Expected QoS levels granted
240
+ */
241
+ export async function subscribeMultiple (t, subscriber, subs, expectedGranted) {
95
242
  subscriber.inStream.write({
96
243
  cmd: 'subscribe',
97
244
  messageId: 24,
98
245
  subscriptions: subs
99
246
  })
100
247
 
101
- subscriber.outStream.once('data', function (packet) {
102
- t.equal(packet.cmd, 'suback')
103
- t.same(packet.granted, expectedGranted)
104
- t.equal(packet.messageId, 24)
248
+ const { value: packet } = await subscriber.outStream.next()
249
+ t.assert.equal(packet.cmd, 'suback')
250
+ t.assert.deepEqual(packet.granted, expectedGranted)
251
+ t.assert.equal(packet.messageId, 24)
252
+ return packet
253
+ }
254
+
255
+ /**
256
+ * Wraps a promise with a timeout
257
+ * @param {Promise} promise - Promise to wrap with timeout
258
+ * @param {number} timeoutMs - Timeout in milliseconds
259
+ * @param {Error} err - Error to throw on timeout
260
+ * @returns {Promise} Promise that rejects if timeout occurs
261
+ */
262
+ export async function withTimeout (promise, timeoutMs, timeoutResult) {
263
+ const timeoutPromise = delay(timeoutMs, timeoutResult)
264
+ return Promise.race([promise, timeoutPromise])
265
+ }
105
266
 
106
- if (done) {
107
- done(null, packet)
267
+ /**
268
+ * Asynchronously yields MQTT packets parsed from a source stream, with backpressure management.
269
+ *
270
+ * Backpressure is controlled using a queue and the `highWaterMark`/`lowWaterMark` parameters:
271
+ * - When the number of buffered packets in the queue reaches `highWaterMark`, the source stream is paused to prevent overload.
272
+ * - When the queue size drops to or below `lowWaterMark`, the source stream is resumed to allow more data to flow.
273
+ *
274
+ * @param {Object} parser - The emitter object that parses MQTT packets and emits 'packet' events.
275
+ * @param {Readable} sourceStream - The source stream to read MQTT packets from.
276
+ * @param {Object} [opts] - Options for backpressure control.
277
+ * @param {number} [opts.highWaterMark=2] - Maximum number of packets to buffer before pausing the source stream.
278
+ * @param {number} [opts.lowWaterMark=0] - Minimum number of packets in the buffer to resume the source stream.
279
+ * @returns {AsyncGenerator<Object|null, void, unknown>} An async generator that yields MQTT packets, or null when done.
280
+ */
281
+ async function * packetGenerator (parser, sourceStream, opts = {
282
+ highWaterMark: 2,
283
+ lowWaterMark: 0
284
+ }) {
285
+ const { highWaterMark, lowWaterMark } = opts
286
+ if (!Number.isInteger(highWaterMark) || highWaterMark < 1) {
287
+ throw new Error('highWaterMark must be a positive integer')
288
+ }
289
+ if (!Number.isInteger(lowWaterMark) || lowWaterMark < 0) {
290
+ throw new Error('lowWaterMark must be a non-negative integer')
291
+ }
292
+ if (lowWaterMark >= highWaterMark) {
293
+ throw new Error('lowWaterMark must be less than highWaterMark')
294
+ }
295
+ const queue = []
296
+ let waiting = null
297
+ let done = false
298
+
299
+ const onPacket = packet => {
300
+ if (waiting) {
301
+ waiting(packet)
302
+ waiting = null
303
+ } else {
304
+ queue.push(packet)
108
305
  }
109
- })
306
+ // Pause source if queue is full
307
+ if (queue.length >= highWaterMark) {
308
+ sourceStream.pause()
309
+ }
310
+ }
311
+
312
+ parser.on('packet', onPacket)
313
+ sourceStream.on('data', parser.parse.bind(parser))
314
+ const endGeneration = (value) => {
315
+ if (waiting) {
316
+ waiting(value)
317
+ waiting = null
318
+ }
319
+ if (value) {
320
+ queue.push(value)
321
+ }
322
+ done = true
323
+ }
324
+ sourceStream.on('error', (err) => endGeneration(err))
325
+ sourceStream.on('end', () => endGeneration(null))
326
+ sourceStream.on('close', () => endGeneration(null))
327
+
328
+ try {
329
+ while (true) {
330
+ if (done) {
331
+ yield null
332
+ } else {
333
+ if (queue.length === 0) {
334
+ // Wait for next packet
335
+ const packet = await new Promise(resolve => (waiting = resolve))
336
+ yield packet
337
+ } else {
338
+ // There is buffered data ready
339
+ // If we're emptying the queue, resume source
340
+ if (queue.length <= lowWaterMark) {
341
+ sourceStream.resume()
342
+ }
343
+ yield queue.shift()
344
+ }
345
+ }
346
+ }
347
+ } finally {
348
+ sourceStream.pause()
349
+ parser.off('packet', onPacket)
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Reads next packet from subscriber
355
+ * @param {Object} s - The connection state object
356
+ * @returns
357
+ */
358
+ export async function nextPacket (s) {
359
+ const { value: packet } = await s.outStream.next()
360
+ return packet
361
+ }
362
+
363
+ /**
364
+ * Reads next packet from subscriber with timeout
365
+ * @param {Object} s - The connection state object
366
+ * @param {number} timeoutMs - Timeout in milliseconds
367
+ * @returns
368
+ */
369
+ export async function nextPacketWithTimeOut (s, timeoutMs) {
370
+ return withTimeout(nextPacket(s), timeoutMs, null)
371
+ }
372
+
373
+ /**
374
+ * Checks if there is no packet received within the specified timeout
375
+ * @param {Object} t - Test assertion object
376
+ * @param {Object} s - s The connection state object
377
+ * @param {number} timeoutMs - Timeout in milliseconds
378
+ * @returns
379
+ */
380
+ export async function checkNoPacket (t, s, timeoutMs = 10) {
381
+ const result = await nextPacketWithTimeOut(s, timeoutMs)
382
+ t.assert.equal(result, null, 'no packet received')
110
383
  }
111
384
 
112
- module.exports = {
113
- setup,
114
- connect,
115
- noError,
116
- subscribe,
117
- subscribeMultiple,
118
- delay: util.promisify(setTimeout)
385
+ /**
386
+ *
387
+ * @param {Object} s - s The connection state object
388
+ * @param {string} rawPacket - space separated string of hex values
389
+ * @example rawWrite(s, "10 0C 00 04 4D 51 54 54 04 00 00 00 00 00")
390
+ */
391
+ export function rawWrite (s, rawPacket) {
392
+ s.conn.write(Buffer.from(rawPacket.replace(/ /g, ''), 'hex'))
119
393
  }
394
+ /**
395
+ * sleep
396
+ */
397
+ export { delay }
@@ -1,92 +1,65 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const eos = require('end-of-stream')
5
- const Faketimers = require('@sinonjs/fake-timers')
6
- const { setup, connect, noError } = require('./helper')
7
- const aedes = require('../')
8
-
9
- test('supports pingreq/pingresp', function (t) {
1
+ import { test } from 'node:test'
2
+ import { once } from 'node:events'
3
+ import { Aedes } from '../aedes.js'
4
+ import {
5
+ createAndConnect,
6
+ delay,
7
+ nextPacket,
8
+ setup,
9
+ } from './helper.js'
10
+
11
+ test('supports pingreq/pingresp', async (t) => {
10
12
  t.plan(1)
11
13
 
12
- const s = noError(connect(setup()))
13
- t.teardown(s.broker.close.bind(s.broker))
14
+ const s = await createAndConnect(t)
14
15
 
15
- s.broker.on('keepaliveTimeout', function (client) {
16
- t.fail('keep alive should not timeout')
16
+ s.broker.on('keepaliveTimeout', client => {
17
+ t.assert.fail('keep alive should not timeout')
17
18
  })
18
19
 
19
20
  s.inStream.write({
20
21
  cmd: 'pingreq'
21
22
  })
22
23
 
23
- s.outStream.on('data', function (packet) {
24
- t.equal(packet.cmd, 'pingresp', 'the response is a pingresp')
25
- })
24
+ const packet = await nextPacket(s)
25
+ t.assert.equal(packet.cmd, 'pingresp', 'the response is a pingresp')
26
26
  })
27
27
 
28
- test('supports keep alive disconnections', function (t) {
28
+ test('supports keep alive disconnections', async (t) => {
29
29
  t.plan(2)
30
30
 
31
- const clock = Faketimers.install()
32
- const s = connect(setup(), { keepalive: 1 })
33
- t.teardown(s.broker.close.bind(s.broker))
34
-
35
- s.broker.on('keepaliveTimeout', function (client) {
36
- t.pass('keep alive timeout')
37
- })
38
- eos(s.conn, function () {
39
- t.pass('waits 1 and a half the keepalive timeout')
40
- })
41
-
42
- setTimeout(() => {
43
- clock.uninstall()
44
- }, 1.5)
45
- clock.tick(1.5)
31
+ const s = await createAndConnect(t, { connect: { keepalive: 1 } })
32
+ await once(s.broker, 'keepaliveTimeout')
33
+ t.assert.ok(true, 'keep alive timeout', 'timeout was triggered')
34
+ t.assert.ok(s.client.conn.closed, 'waits 1 and a half the keepalive timeout')
46
35
  })
47
36
 
48
- test('supports keep alive disconnections after a pingreq', function (t) {
37
+ test('supports keep alive disconnections after a pingreq', async (t) => {
49
38
  t.plan(3)
50
39
 
51
- const clock = Faketimers.install()
52
- const s = connect(setup(), { keepalive: 1 })
53
- t.teardown(s.broker.close.bind(s.broker))
54
-
55
- eos(s.conn, function () {
56
- t.pass('waits 1 and a half the keepalive timeout')
57
- })
58
- s.broker.on('keepaliveTimeout', function (client) {
59
- t.pass('keep alive timeout')
60
- })
61
- s.outStream.on('data', function (packet) {
62
- t.equal(packet.cmd, 'pingresp', 'the response is a pingresp')
40
+ const s = await createAndConnect(t, { connect: { keepalive: 1 } })
41
+ await delay(1)
42
+ s.inStream.write({
43
+ cmd: 'pingreq'
63
44
  })
64
- setTimeout(() => {
65
- s.inStream.write({
66
- cmd: 'pingreq'
67
- })
68
- clock.uninstall()
69
- }, 1)
70
- clock.tick(3)
45
+ const packet = await nextPacket(s)
46
+ t.assert.equal(packet.cmd, 'pingresp', 'the response is a pingresp')
47
+ await once(s.broker, 'keepaliveTimeout')
48
+ t.assert.ok(true, 'keep alive timeout', 'timeout was triggered')
49
+ t.assert.ok(s.client.conn.closed, 'waits 1 and a half the keepalive timeout')
71
50
  })
72
51
 
73
- test('disconnect if a connect does not arrive in time', function (t) {
52
+ test('disconnect if a connect does not arrive in time', async (t) => {
74
53
  t.plan(2)
75
54
 
76
- const clock = Faketimers.install()
77
- const s = setup(aedes({
55
+ const broker = await Aedes.createBroker({
78
56
  connectTimeout: 500
79
- }))
80
- t.teardown(s.broker.close.bind(s.broker))
81
-
82
- s.client.on('error', function (err) {
83
- t.equal(err.message, 'connect did not arrive in time')
84
- })
85
- eos(s.conn, function () {
86
- t.pass('waits waitConnectTimeout before ending')
87
57
  })
88
- setTimeout(() => {
89
- clock.uninstall()
90
- }, 1000)
91
- clock.tick(1000)
58
+
59
+ const s = setup(broker)
60
+ t.after(() => broker.close())
61
+
62
+ const [err] = await once(s.client, 'error')
63
+ t.assert.equal(err.message, 'connect did not arrive in time')
64
+ t.assert.ok(s.client.conn.closed, 'waits waitConnectTimeout before ending')
92
65
  })