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.
- package/.claude/settings.local.json +12 -0
- package/.github/actions/sticky-pr-comment/action.yml +55 -0
- package/.github/workflows/benchmark-compare-serial.yml +60 -0
- package/.github/workflows/ci.yml +12 -17
- package/.release-it.json +18 -0
- package/.taprc +15 -6
- package/README.md +6 -4
- package/aedes.d.ts +0 -6
- package/aedes.js +270 -242
- package/benchmarks/README.md +33 -0
- package/benchmarks/pingpong.js +94 -25
- package/benchmarks/receiver.js +77 -0
- package/benchmarks/report.js +150 -0
- package/benchmarks/runBenchmarks.js +118 -0
- package/benchmarks/sender.js +86 -0
- package/benchmarks/server.js +19 -18
- package/checkVersion.js +20 -0
- package/docs/Aedes.md +66 -8
- package/docs/Client.md +3 -4
- package/docs/Examples.md +39 -22
- package/docs/MIGRATION.md +50 -0
- package/eslint.config.js +8 -0
- package/example.js +51 -40
- package/examples/clusters/index.js +28 -23
- package/examples/clusters/package.json +10 -6
- package/lib/client.js +405 -306
- package/lib/handlers/connect.js +42 -38
- package/lib/handlers/index.js +9 -11
- package/lib/handlers/ping.js +2 -3
- package/lib/handlers/puback.js +5 -5
- package/lib/handlers/publish.js +29 -14
- package/lib/handlers/pubrec.js +9 -17
- package/lib/handlers/pubrel.js +34 -25
- package/lib/handlers/subscribe.js +47 -43
- package/lib/handlers/unsubscribe.js +16 -19
- package/lib/qos-packet.js +14 -17
- package/lib/utils.js +5 -12
- package/lib/write.js +4 -5
- package/package.json +139 -136
- package/test/auth.js +468 -804
- package/test/basic.js +613 -575
- package/test/bridge.js +44 -40
- package/test/client-pub-sub.js +531 -504
- package/test/close_socket_by_other_party.js +137 -102
- package/test/connect.js +487 -484
- package/test/drain-timeout.js +593 -0
- package/test/drain-toxiproxy.js +620 -0
- package/test/events.js +173 -145
- package/test/helper.js +351 -73
- package/test/keep-alive.js +40 -67
- package/test/meta.js +257 -210
- package/test/not-blocking.js +93 -197
- package/test/qos1.js +464 -554
- package/test/qos2.js +308 -393
- package/test/regr-21.js +39 -21
- package/test/require.cjs +22 -0
- package/test/retain.js +349 -398
- package/test/topics.js +176 -183
- package/test/types/aedes.test-d.ts +4 -8
- package/test/will.js +310 -428
- package/types/instance.d.ts +40 -35
- package/types/packet.d.ts +10 -10
- package/.coveralls.yml +0 -1
- package/benchmarks/bombing.js +0 -34
- package/benchmarks/bombingQoS1.js +0 -36
- package/benchmarks/throughputCounter.js +0 -23
- package/benchmarks/throughputCounterQoS1.js +0 -33
- package/types/.eslintrc.json +0 -47
package/test/basic.js
CHANGED
|
@@ -1,28 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import { once } from 'node:events'
|
|
3
|
+
import { Duplex } from 'node:stream'
|
|
4
|
+
import {
|
|
5
|
+
brokerPublish,
|
|
6
|
+
checkNoPacket,
|
|
7
|
+
connect,
|
|
8
|
+
createAndConnect,
|
|
9
|
+
delay,
|
|
10
|
+
nextPacket,
|
|
11
|
+
setup,
|
|
12
|
+
subscribe,
|
|
13
|
+
subscribeMultiple,
|
|
14
|
+
withTimeout
|
|
15
|
+
} from './helper.js'
|
|
16
|
+
import defaultExport, { Aedes } from '../aedes.js'
|
|
17
|
+
import write from '../lib/write.js'
|
|
18
|
+
|
|
19
|
+
test('test Aedes constructor', (t) => {
|
|
20
|
+
t.plan(1)
|
|
21
|
+
const aedes = new Aedes()
|
|
22
|
+
t.assert.equal(aedes instanceof Aedes, true, 'Aedes constructor works')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('test warning on default export', (t) => {
|
|
26
|
+
t.plan(1)
|
|
27
|
+
t.assert.throws(defaultExport, 'received expected error')
|
|
28
|
+
})
|
|
2
29
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
30
|
+
test('test aedes.createBroker', async (t) => {
|
|
31
|
+
t.plan(1)
|
|
32
|
+
const broker = await Aedes.createBroker()
|
|
33
|
+
t.after(() => broker.close())
|
|
34
|
+
await connect(setup(broker))
|
|
35
|
+
t.assert.ok(true, 'connected')
|
|
36
|
+
})
|
|
8
37
|
|
|
9
|
-
test('test
|
|
38
|
+
test('test non-async persistence.setup throws error', async (t) => {
|
|
10
39
|
t.plan(1)
|
|
11
40
|
|
|
12
|
-
|
|
13
|
-
|
|
41
|
+
class P {
|
|
42
|
+
setup () {
|
|
43
|
+
console.log('I am a synchronous setup function')
|
|
44
|
+
}
|
|
45
|
+
}
|
|
14
46
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
47
|
+
const p = new P()
|
|
48
|
+
const broker = new Aedes({ persistence: p })
|
|
49
|
+
t.after(() => broker.close())
|
|
50
|
+
try {
|
|
51
|
+
await broker.listen()
|
|
52
|
+
} catch (_err) {
|
|
53
|
+
t.assert.ok(true, 'receiving expected error')
|
|
54
|
+
}
|
|
18
55
|
})
|
|
19
56
|
|
|
20
|
-
test('publish QoS 0',
|
|
57
|
+
test('publish QoS 0', async (t) => {
|
|
21
58
|
t.plan(2)
|
|
22
59
|
|
|
23
|
-
const s =
|
|
24
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
25
|
-
|
|
60
|
+
const s = await createAndConnect(t)
|
|
26
61
|
const expected = {
|
|
27
62
|
cmd: 'publish',
|
|
28
63
|
topic: 'hello',
|
|
@@ -30,29 +65,31 @@ test('publish QoS 0', function (t) {
|
|
|
30
65
|
qos: 0,
|
|
31
66
|
retain: false,
|
|
32
67
|
dup: false,
|
|
33
|
-
clientId: 'my-client
|
|
68
|
+
clientId: 'my-client'
|
|
34
69
|
}
|
|
35
70
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
await new Promise(resolve => {
|
|
72
|
+
s.broker.mq.on('hello', (packet, cb) => {
|
|
73
|
+
expected.brokerId = s.broker.id
|
|
74
|
+
expected.brokerCounter = s.broker.counter
|
|
75
|
+
t.assert.equal(packet.messageId, undefined, 'MUST not contain a packet identifier in QoS 0')
|
|
76
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet matches')
|
|
77
|
+
cb()
|
|
78
|
+
resolve()
|
|
79
|
+
})
|
|
43
80
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
s.inStream.write({
|
|
82
|
+
cmd: 'publish',
|
|
83
|
+
topic: 'hello',
|
|
84
|
+
payload: 'world'
|
|
85
|
+
})
|
|
48
86
|
})
|
|
49
87
|
})
|
|
50
88
|
|
|
51
|
-
test('messageId shoud reset to 1 if it reached 65535',
|
|
89
|
+
test('messageId shoud reset to 1 if it reached 65535', async (t) => {
|
|
52
90
|
t.plan(7)
|
|
53
91
|
|
|
54
|
-
const s =
|
|
55
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
92
|
+
const s = await createAndConnect(t)
|
|
56
93
|
|
|
57
94
|
const publishPacket = {
|
|
58
95
|
cmd: 'publish',
|
|
@@ -62,63 +99,75 @@ test('messageId shoud reset to 1 if it reached 65535', function (t) {
|
|
|
62
99
|
messageId: 42
|
|
63
100
|
}
|
|
64
101
|
let count = 0
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
let pubacks = 0
|
|
103
|
+
let published = 0
|
|
104
|
+
|
|
105
|
+
const [client] = await once(s.broker, 'clientReady')
|
|
106
|
+
await subscribe(t, s, 'hello', 1)
|
|
107
|
+
client._nextId = 65535
|
|
108
|
+
|
|
109
|
+
const checkResults = async () => {
|
|
110
|
+
for await (const packet of s.outStream) {
|
|
111
|
+
if (packet.cmd === 'puback') {
|
|
112
|
+
t.assert.equal(packet.messageId, 42)
|
|
113
|
+
pubacks++
|
|
114
|
+
}
|
|
115
|
+
if (packet.cmd === 'publish') {
|
|
116
|
+
t.assert.equal(packet.messageId, count++ === 0 ? 65535 : 1)
|
|
117
|
+
published++
|
|
118
|
+
}
|
|
119
|
+
if (pubacks === 2 && published === 2) {
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const publishPackets = () => {
|
|
125
|
+
s.inStream.write(publishPacket)
|
|
126
|
+
s.inStream.write(publishPacket)
|
|
127
|
+
}
|
|
128
|
+
// run parallel
|
|
129
|
+
await Promise.all([publishPackets(), checkResults()])
|
|
80
130
|
})
|
|
81
131
|
|
|
82
|
-
test('publish empty topic throws error',
|
|
83
|
-
t.plan(
|
|
84
|
-
|
|
85
|
-
const s = connect(setup())
|
|
86
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
87
|
-
|
|
132
|
+
test('publish empty topic throws error', async (t) => {
|
|
133
|
+
t.plan(2)
|
|
134
|
+
const s = await createAndConnect(t)
|
|
88
135
|
s.inStream.write({
|
|
89
136
|
cmd: 'publish',
|
|
90
137
|
topic: '',
|
|
91
138
|
payload: 'world'
|
|
92
139
|
})
|
|
93
140
|
|
|
94
|
-
s.broker
|
|
95
|
-
|
|
96
|
-
|
|
141
|
+
const [client, err] = await once(s.broker, 'clientError')
|
|
142
|
+
t.assert.ok(client)
|
|
143
|
+
t.assert.ok(err, 'should emit error')
|
|
97
144
|
})
|
|
98
145
|
|
|
99
|
-
test('publish to $SYS topic throws error',
|
|
100
|
-
t.plan(
|
|
101
|
-
|
|
102
|
-
const s = connect(setup())
|
|
103
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
146
|
+
test('publish to $SYS topic throws error', async (t) => {
|
|
147
|
+
t.plan(2)
|
|
104
148
|
|
|
149
|
+
const s = await createAndConnect(t)
|
|
105
150
|
s.inStream.write({
|
|
106
151
|
cmd: 'publish',
|
|
107
152
|
topic: '$SYS/not/allowed',
|
|
108
153
|
payload: 'world'
|
|
109
154
|
})
|
|
110
155
|
|
|
111
|
-
s.broker
|
|
112
|
-
|
|
113
|
-
|
|
156
|
+
const [client, err] = await once(s.broker, 'clientError')
|
|
157
|
+
t.assert.ok(client)
|
|
158
|
+
t.assert.ok(err, 'should emit error')
|
|
114
159
|
})
|
|
115
160
|
|
|
116
|
-
|
|
117
|
-
|
|
161
|
+
for (const ele of [
|
|
162
|
+
{ qos: 0, clean: false },
|
|
163
|
+
{ qos: 0, clean: true },
|
|
164
|
+
{ qos: 1, clean: false },
|
|
165
|
+
{ qos: 1, clean: true }
|
|
166
|
+
]) {
|
|
167
|
+
test('subscribe a single topic in QoS ' + ele.qos + ' [clean=' + ele.clean + ']', async (t) => {
|
|
118
168
|
t.plan(5)
|
|
119
169
|
|
|
120
|
-
const s =
|
|
121
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
170
|
+
const s = await createAndConnect(t, { connect: { clean: ele.clean } })
|
|
122
171
|
|
|
123
172
|
const expected = {
|
|
124
173
|
cmd: 'publish',
|
|
@@ -129,56 +178,62 @@ test('publish to $SYS topic throws error', function (t) {
|
|
|
129
178
|
qos: 0,
|
|
130
179
|
retain: false
|
|
131
180
|
}
|
|
132
|
-
const expectedSubs = ele.clean
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
181
|
+
const expectedSubs = ele.clean
|
|
182
|
+
? []
|
|
183
|
+
: [
|
|
184
|
+
{
|
|
185
|
+
topic: 'hello',
|
|
186
|
+
qos: ele.qos,
|
|
187
|
+
rh: undefined,
|
|
188
|
+
rap: undefined,
|
|
189
|
+
nl: undefined
|
|
190
|
+
}
|
|
191
|
+
]
|
|
138
192
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
193
|
+
await subscribe(t, s, 'hello', ele.qos)
|
|
194
|
+
const subs = await s.broker.persistence.subscriptionsByClient(s.client)
|
|
195
|
+
t.assert.deepEqual(subs, expectedSubs, 'subs match')
|
|
142
196
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
})
|
|
197
|
+
s.broker.publish({
|
|
198
|
+
cmd: 'publish',
|
|
199
|
+
topic: 'hello',
|
|
200
|
+
payload: 'world'
|
|
148
201
|
})
|
|
202
|
+
|
|
203
|
+
const packet = await nextPacket(s)
|
|
204
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet matches')
|
|
149
205
|
})
|
|
150
|
-
}
|
|
206
|
+
}
|
|
151
207
|
|
|
152
208
|
// Catch invalid packet writeToStream errors
|
|
153
|
-
test('return write errors to callback',
|
|
209
|
+
test('return write errors to callback', async (t) => {
|
|
154
210
|
t.plan(1)
|
|
155
211
|
|
|
156
|
-
const write = proxyquire('../lib/write.js', {
|
|
157
|
-
'mqtt-packet': {
|
|
158
|
-
writeToStream: () => {
|
|
159
|
-
throw Error('error')
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
})
|
|
163
|
-
|
|
164
212
|
const client = {
|
|
165
213
|
conn: {
|
|
166
|
-
writable: true
|
|
214
|
+
writable: true,
|
|
215
|
+
write: () => { throw new Error('error') }
|
|
167
216
|
},
|
|
168
217
|
connecting: true
|
|
169
218
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
219
|
+
await new Promise(resolve => {
|
|
220
|
+
write(client, {}, err => {
|
|
221
|
+
t.assert.equal(err.message, 'packet received not valid', 'should return the error to callback')
|
|
222
|
+
resolve()
|
|
223
|
+
})
|
|
173
224
|
})
|
|
174
225
|
})
|
|
175
226
|
|
|
176
|
-
|
|
177
|
-
|
|
227
|
+
for (const ele of [
|
|
228
|
+
{ qos: 0, clean: false },
|
|
229
|
+
{ qos: 0, clean: true },
|
|
230
|
+
{ qos: 1, clean: false },
|
|
231
|
+
{ qos: 1, clean: true }
|
|
232
|
+
]) {
|
|
233
|
+
test('subscribe multipe topics in QoS ' + ele.qos + ' [clean=' + ele.clean + ']', async (t) => {
|
|
178
234
|
t.plan(5)
|
|
179
235
|
|
|
180
|
-
const s =
|
|
181
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
236
|
+
const s = await createAndConnect(t, { connect: { clean: ele.clean } })
|
|
182
237
|
|
|
183
238
|
const expected = {
|
|
184
239
|
cmd: 'publish',
|
|
@@ -189,86 +244,76 @@ test('return write errors to callback', function (t) {
|
|
|
189
244
|
qos: 0,
|
|
190
245
|
retain: false
|
|
191
246
|
}
|
|
192
|
-
|
|
247
|
+
|
|
248
|
+
const subsToSubscribe = [
|
|
193
249
|
{ topic: 'hello', qos: ele.qos, rh: undefined, rap: undefined, nl: undefined },
|
|
194
250
|
{ topic: 'world', qos: ele.qos, rh: undefined, rap: undefined, nl: undefined }
|
|
195
251
|
]
|
|
196
|
-
const expectedSubs = ele.clean ?
|
|
197
|
-
|
|
198
|
-
subscribeMultiple(t, s, subs, [ele.qos, ele.qos], function () {
|
|
199
|
-
s.outStream.on('data', function (packet) {
|
|
200
|
-
t.same(packet, expected, 'packet matches')
|
|
201
|
-
})
|
|
252
|
+
const expectedSubs = ele.clean ? [] : subsToSubscribe
|
|
202
253
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
payload: 'world'
|
|
211
|
-
})
|
|
254
|
+
await subscribeMultiple(t, s, subsToSubscribe, [ele.qos, ele.qos])
|
|
255
|
+
const subs = await s.broker.persistence.subscriptionsByClient(s.client)
|
|
256
|
+
t.assert.deepEqual(subs, expectedSubs, 'subs match')
|
|
257
|
+
s.broker.publish({
|
|
258
|
+
cmd: 'publish',
|
|
259
|
+
topic: 'hello',
|
|
260
|
+
payload: 'world'
|
|
212
261
|
})
|
|
262
|
+
const packet = await nextPacket(s)
|
|
263
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet matches')
|
|
213
264
|
})
|
|
214
|
-
}
|
|
265
|
+
}
|
|
215
266
|
|
|
216
|
-
test('does not die badly on connection error',
|
|
217
|
-
t.plan(
|
|
267
|
+
test('does not die badly on connection error', async (t) => {
|
|
268
|
+
t.plan(6)
|
|
269
|
+
const s = await createAndConnect(t)
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
271
|
+
await subscribe(t, s, 'hello', 0)
|
|
221
272
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
qos: 0
|
|
228
|
-
}]
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
s.broker.on('clientError', function (client, err) {
|
|
232
|
-
t.ok(client, 'client is passed')
|
|
233
|
-
t.ok(err, 'err is passed')
|
|
234
|
-
})
|
|
273
|
+
const clientError = async () => {
|
|
274
|
+
const [client, err] = await once(s.broker, 'clientError')
|
|
275
|
+
t.assert.ok(client, 'client is passed')
|
|
276
|
+
t.assert.ok(err, 'err is passed')
|
|
277
|
+
}
|
|
235
278
|
|
|
236
|
-
|
|
279
|
+
const serverWorking = new Promise(resolve => {
|
|
237
280
|
s.conn.destroy()
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
281
|
+
setImmediate(() => {
|
|
282
|
+
s.broker.publish({
|
|
283
|
+
cmd: 'publish',
|
|
284
|
+
topic: 'hello',
|
|
285
|
+
payload: Buffer.from('world')
|
|
286
|
+
}, () => {
|
|
287
|
+
t.assert.ok(true, 'calls the callback')
|
|
288
|
+
resolve()
|
|
289
|
+
})
|
|
244
290
|
})
|
|
245
291
|
})
|
|
292
|
+
await Promise.all([clientError(), serverWorking])
|
|
246
293
|
})
|
|
247
294
|
|
|
248
295
|
// Guarded in mqtt-packet
|
|
249
|
-
test('subscribe should have messageId',
|
|
296
|
+
test('subscribe should have messageId', async (t) => {
|
|
250
297
|
t.plan(1)
|
|
298
|
+
const s = await createAndConnect(t)
|
|
251
299
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
t.ok(err.message, 'Invalid messageId')
|
|
264
|
-
})
|
|
300
|
+
try {
|
|
301
|
+
s.inStream.write({
|
|
302
|
+
cmd: 'subscribe',
|
|
303
|
+
subscriptions: [{
|
|
304
|
+
topic: 'hello',
|
|
305
|
+
qos: 0
|
|
306
|
+
}]
|
|
307
|
+
})
|
|
308
|
+
} catch (err) {
|
|
309
|
+
t.assert.ok(err.message, 'Invalid messageId')
|
|
310
|
+
}
|
|
265
311
|
})
|
|
266
312
|
|
|
267
|
-
test('subscribe with messageId 0 should return suback',
|
|
313
|
+
test('subscribe with messageId 0 should return suback', async (t) => {
|
|
268
314
|
t.plan(1)
|
|
269
315
|
|
|
270
|
-
const s =
|
|
271
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
316
|
+
const s = await createAndConnect(t)
|
|
272
317
|
|
|
273
318
|
s.inStream.write({
|
|
274
319
|
cmd: 'subscribe',
|
|
@@ -278,64 +323,63 @@ test('subscribe with messageId 0 should return suback', function (t) {
|
|
|
278
323
|
}],
|
|
279
324
|
messageId: 0
|
|
280
325
|
})
|
|
281
|
-
s.outStream.once('data', function (packet) {
|
|
282
|
-
t.same(packet, {
|
|
283
|
-
cmd: 'suback',
|
|
284
|
-
messageId: 0,
|
|
285
|
-
dup: false,
|
|
286
|
-
length: 3,
|
|
287
|
-
qos: 0,
|
|
288
|
-
retain: false,
|
|
289
|
-
granted: [
|
|
290
|
-
0
|
|
291
|
-
]
|
|
292
|
-
}, 'packet matches')
|
|
293
|
-
})
|
|
294
|
-
})
|
|
295
326
|
|
|
296
|
-
|
|
297
|
-
t.
|
|
327
|
+
const packet = await nextPacket(s)
|
|
328
|
+
t.assert.deepEqual(structuredClone(packet), {
|
|
329
|
+
cmd: 'suback',
|
|
330
|
+
messageId: 0,
|
|
331
|
+
dup: false,
|
|
332
|
+
length: 3,
|
|
333
|
+
qos: 0,
|
|
334
|
+
retain: false,
|
|
335
|
+
payload: null,
|
|
336
|
+
topic: null,
|
|
337
|
+
granted: [
|
|
338
|
+
0
|
|
339
|
+
]
|
|
340
|
+
}, 'packet matches')
|
|
341
|
+
})
|
|
298
342
|
|
|
299
|
-
|
|
300
|
-
t.
|
|
343
|
+
test('unsubscribe', async (t) => {
|
|
344
|
+
t.plan(6)
|
|
301
345
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
346
|
+
const s = await createAndConnect(t)
|
|
347
|
+
await subscribe(t, s, 'hello', 0)
|
|
348
|
+
s.inStream.write({
|
|
349
|
+
cmd: 'unsubscribe',
|
|
350
|
+
messageId: 43,
|
|
351
|
+
unsubscriptions: ['hello']
|
|
352
|
+
})
|
|
308
353
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
t.fail('packet received')
|
|
321
|
-
})
|
|
354
|
+
const packet = await nextPacket(s)
|
|
355
|
+
t.assert.deepEqual(structuredClone(packet), {
|
|
356
|
+
cmd: 'unsuback',
|
|
357
|
+
messageId: 43,
|
|
358
|
+
dup: false,
|
|
359
|
+
length: 2,
|
|
360
|
+
qos: 0,
|
|
361
|
+
retain: false,
|
|
362
|
+
payload: null,
|
|
363
|
+
topic: null
|
|
364
|
+
}, 'packet matches')
|
|
322
365
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
366
|
+
await new Promise(resolve => {
|
|
367
|
+
s.broker.publish({
|
|
368
|
+
cmd: 'publish',
|
|
369
|
+
topic: 'hello',
|
|
370
|
+
payload: 'world'
|
|
371
|
+
}, () => {
|
|
372
|
+
t.assert.ok(true, 'publish finished')
|
|
373
|
+
resolve()
|
|
330
374
|
})
|
|
331
375
|
})
|
|
376
|
+
await checkNoPacket(t, s)
|
|
332
377
|
})
|
|
333
378
|
|
|
334
|
-
test('unsubscribe without subscribe',
|
|
379
|
+
test('unsubscribe without subscribe', async (t) => {
|
|
335
380
|
t.plan(1)
|
|
336
381
|
|
|
337
|
-
const s =
|
|
338
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
382
|
+
const s = await createAndConnect(t)
|
|
339
383
|
|
|
340
384
|
s.inStream.write({
|
|
341
385
|
cmd: 'unsubscribe',
|
|
@@ -343,306 +387,323 @@ test('unsubscribe without subscribe', function (t) {
|
|
|
343
387
|
unsubscriptions: ['hello']
|
|
344
388
|
})
|
|
345
389
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
390
|
+
const packet = await nextPacket(s)
|
|
391
|
+
t.assert.deepEqual(structuredClone(packet), {
|
|
392
|
+
cmd: 'unsuback',
|
|
393
|
+
messageId: 43,
|
|
394
|
+
dup: false,
|
|
395
|
+
length: 2,
|
|
396
|
+
qos: 0,
|
|
397
|
+
retain: false,
|
|
398
|
+
payload: null,
|
|
399
|
+
topic: null
|
|
400
|
+
}, 'packet matches')
|
|
356
401
|
})
|
|
357
402
|
|
|
358
|
-
test('unsubscribe on disconnect for a clean=true client',
|
|
359
|
-
t.plan(
|
|
403
|
+
test('unsubscribe on disconnect for a clean=true client', async (t) => {
|
|
404
|
+
t.plan(7)
|
|
360
405
|
|
|
361
|
-
const opts = { clean: true }
|
|
362
|
-
const s =
|
|
363
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
406
|
+
const opts = { connect: { clean: true } }
|
|
407
|
+
const s = await createAndConnect(t, opts)
|
|
364
408
|
|
|
365
|
-
subscribe(t, s, 'hello', 0
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
s
|
|
409
|
+
await subscribe(t, s, 'hello', 0)
|
|
410
|
+
s.conn.destroy(null)
|
|
411
|
+
t.assert.equal(s.conn.destroyed, true, 'closed streams')
|
|
412
|
+
|
|
413
|
+
const emittedUnsubscribe = async () => {
|
|
414
|
+
await once(s.broker, 'unsubscribe')
|
|
415
|
+
t.assert.ok(true, 'should emit unsubscribe')
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const publishPacket = async () => {
|
|
419
|
+
await brokerPublish(s, {
|
|
376
420
|
cmd: 'publish',
|
|
377
421
|
topic: 'hello',
|
|
378
422
|
payload: Buffer.from('world')
|
|
379
|
-
}, function () {
|
|
380
|
-
t.pass('calls the callback')
|
|
381
423
|
})
|
|
382
|
-
|
|
424
|
+
t.assert.ok(true, 'calls the callback')
|
|
425
|
+
}
|
|
426
|
+
// run parallel
|
|
427
|
+
await Promise.all([checkNoPacket(t, s), emittedUnsubscribe(), publishPacket()])
|
|
383
428
|
})
|
|
384
429
|
|
|
385
|
-
test('unsubscribe on disconnect for a clean=false client',
|
|
386
|
-
t.plan(
|
|
430
|
+
test('unsubscribe on disconnect for a clean=false client', async (t) => {
|
|
431
|
+
t.plan(7)
|
|
387
432
|
|
|
388
|
-
const opts = { clean: false }
|
|
389
|
-
const s =
|
|
390
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
433
|
+
const opts = { connect: { clean: false } }
|
|
434
|
+
const s = await createAndConnect(t, opts)
|
|
391
435
|
|
|
392
|
-
subscribe(t, s, 'hello', 0
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
s
|
|
436
|
+
await subscribe(t, s, 'hello', 0)
|
|
437
|
+
s.conn.destroy(null, () => {
|
|
438
|
+
t.assert.ok(true, 'closed streams')
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const emittedNoUnsubscribe = async () => {
|
|
442
|
+
const result = await withTimeout(once(s.broker, 'unsubscribe'), 10, null)
|
|
443
|
+
t.assert.equal(result, null, 'should not emit unsubscribe')
|
|
444
|
+
}
|
|
445
|
+
const publishPacket = async () => {
|
|
446
|
+
await brokerPublish(s, {
|
|
403
447
|
cmd: 'publish',
|
|
404
448
|
topic: 'hello',
|
|
405
449
|
payload: Buffer.from('world')
|
|
406
|
-
}, function () {
|
|
407
|
-
t.pass('calls the callback')
|
|
408
450
|
})
|
|
409
|
-
|
|
451
|
+
t.assert.ok(true, 'calls the callback')
|
|
452
|
+
}
|
|
453
|
+
// run parallel
|
|
454
|
+
await Promise.all([checkNoPacket(t, s), emittedNoUnsubscribe(), publishPacket()])
|
|
410
455
|
})
|
|
411
456
|
|
|
412
|
-
test('disconnect',
|
|
457
|
+
test('disconnect', async (t) => {
|
|
413
458
|
t.plan(1)
|
|
414
459
|
|
|
415
|
-
const s =
|
|
416
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
460
|
+
const s = await createAndConnect(t)
|
|
417
461
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
462
|
+
const checkDisconnect = async () => {
|
|
463
|
+
await once(s.broker, 'clientDisconnect')
|
|
464
|
+
t.assert.ok(true, 'closed stream')
|
|
465
|
+
}
|
|
421
466
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
467
|
+
const disconnect = () => {
|
|
468
|
+
s.inStream.write({
|
|
469
|
+
cmd: 'disconnect'
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
// run parallel
|
|
473
|
+
await Promise.all([checkDisconnect(), disconnect()])
|
|
425
474
|
})
|
|
426
475
|
|
|
427
|
-
test('disconnect client on wrong cmd',
|
|
476
|
+
test('disconnect client on wrong cmd', async (t) => {
|
|
428
477
|
t.plan(1)
|
|
429
478
|
|
|
430
|
-
const s =
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
t.pass('closed stream')
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
s.broker.on('clientReady', function (c) {
|
|
438
|
-
// don't use stream write here because it will throw an error on mqtt_packet genetete
|
|
439
|
-
c._parser.emit('packet', { cmd: 'pippo' })
|
|
440
|
-
})
|
|
479
|
+
const s = await createAndConnect(t)
|
|
480
|
+
s.client._parser.emit('packet', { cmd: 'pippo' })
|
|
481
|
+
await once(s.broker, 'clientDisconnect')
|
|
482
|
+
t.assert.ok(true, 'closed stream')
|
|
441
483
|
})
|
|
442
484
|
|
|
443
|
-
test('client closes',
|
|
485
|
+
test('client closes', async (t) => {
|
|
444
486
|
t.plan(5)
|
|
445
487
|
|
|
446
|
-
const broker =
|
|
447
|
-
|
|
448
|
-
broker
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
488
|
+
const { broker, client } = await createAndConnect(t, { connect: { clientId: 'abcde' } })
|
|
489
|
+
|
|
490
|
+
await once(broker, 'clientReady')
|
|
491
|
+
const brokerClient = broker.clients.abcde
|
|
492
|
+
t.assert.equal(brokerClient.connected, true, 'client connected')
|
|
493
|
+
|
|
494
|
+
const clientClosed = async () => {
|
|
495
|
+
await once(client.conn, 'close')
|
|
496
|
+
t.assert.ok(true, 'client disconnected')
|
|
497
|
+
}
|
|
498
|
+
const closeAll = new Promise(resolve => {
|
|
452
499
|
setImmediate(() => {
|
|
453
|
-
brokerClient.close(
|
|
454
|
-
t.equal(broker.clients.abcde, undefined, 'client instance is removed')
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
500
|
+
brokerClient.close(() => {
|
|
501
|
+
t.assert.equal(broker.clients.abcde, undefined, 'client instance is removed')
|
|
502
|
+
t.assert.equal(brokerClient.connected, false, 'client disconnected')
|
|
503
|
+
broker.close((err) => {
|
|
504
|
+
t.assert.ok(!err, 'no error')
|
|
505
|
+
resolve()
|
|
506
|
+
})
|
|
459
507
|
})
|
|
460
508
|
})
|
|
461
509
|
})
|
|
510
|
+
// run parallel
|
|
511
|
+
await Promise.all([clientClosed(), closeAll])
|
|
462
512
|
})
|
|
463
513
|
|
|
464
|
-
test('broker closes',
|
|
514
|
+
test('broker closes', async (t) => {
|
|
465
515
|
t.plan(4)
|
|
466
516
|
|
|
467
|
-
const broker =
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
517
|
+
const { broker, client } = await createAndConnect(t, { connect: { clientId: 'abcde' } })
|
|
518
|
+
|
|
519
|
+
const clientClosed = async () => {
|
|
520
|
+
await once(client.conn, 'close')
|
|
521
|
+
t.assert.ok(true, 'client closes')
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const brokerClose = new Promise(resolve => {
|
|
525
|
+
broker.close((err) => {
|
|
526
|
+
t.assert.ok(!err, 'no error')
|
|
527
|
+
t.assert.ok(broker.closed)
|
|
528
|
+
t.assert.equal(broker.clients.abcde, undefined, 'client instance is removed')
|
|
529
|
+
resolve()
|
|
476
530
|
})
|
|
477
|
-
})
|
|
531
|
+
})
|
|
532
|
+
// run parallel
|
|
533
|
+
await Promise.all([clientClosed(), brokerClose])
|
|
478
534
|
})
|
|
479
535
|
|
|
480
|
-
test('broker closes gracefully',
|
|
536
|
+
test('broker closes gracefully', async (t) => {
|
|
481
537
|
t.plan(7)
|
|
482
538
|
|
|
483
|
-
const broker =
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
539
|
+
const broker = await Aedes.createBroker()
|
|
540
|
+
t.after(() => broker.close())
|
|
541
|
+
const s1 = setup(broker)
|
|
542
|
+
await connect(s1, { connect: { clientId: 'my-client-1' } })
|
|
543
|
+
const s2 = setup(broker)
|
|
544
|
+
await connect(s2, { connect: { clientId: 'my-client-2' } })
|
|
545
|
+
t.assert.equal(broker.connectedClients, 2, '2 connected clients')
|
|
546
|
+
const client1Closed = async () => {
|
|
547
|
+
await once(s1.client.conn, 'close')
|
|
548
|
+
t.assert.ok(true, 'client1 closes')
|
|
549
|
+
}
|
|
550
|
+
const client2Closed = async () => {
|
|
551
|
+
await once(s2.client.conn, 'close')
|
|
552
|
+
t.assert.ok(true, 'client2 closes')
|
|
553
|
+
}
|
|
554
|
+
const brokerClose = new Promise(resolve => {
|
|
555
|
+
broker.close((err) => {
|
|
556
|
+
t.assert.ok(!err, 'no error')
|
|
557
|
+
t.assert.ok(broker.mq.closed, 'broker mq closes')
|
|
558
|
+
t.assert.ok(broker.closed, 'broker closes')
|
|
559
|
+
t.assert.equal(broker.connectedClients, 0, 'no connected clients')
|
|
560
|
+
resolve()
|
|
561
|
+
})
|
|
562
|
+
})
|
|
563
|
+
// run parallel
|
|
564
|
+
await Promise.all([client1Closed(), client2Closed(), brokerClose])
|
|
499
565
|
})
|
|
500
566
|
|
|
501
|
-
test('testing other event',
|
|
567
|
+
test('testing other event', async (t) => {
|
|
502
568
|
t.plan(1)
|
|
503
569
|
|
|
504
|
-
const broker =
|
|
505
|
-
t.
|
|
570
|
+
const broker = await Aedes.createBroker()
|
|
571
|
+
t.after(() => broker.close())
|
|
506
572
|
|
|
507
|
-
|
|
573
|
+
// can't use default setup because of errothandling on duplexPair
|
|
574
|
+
const server = new Duplex()
|
|
575
|
+
broker.handle(server)
|
|
508
576
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
577
|
+
const connectionError = async () => {
|
|
578
|
+
const [client] = await once(broker, 'connectionError')
|
|
579
|
+
t.assert.ok(!client.id, 'client not present')
|
|
580
|
+
}
|
|
581
|
+
const emitError = () => {
|
|
582
|
+
server.emit('error', 'Connect not yet arrived')
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// run parallel
|
|
586
|
+
await Promise.all([
|
|
587
|
+
connectionError(),
|
|
588
|
+
emitError()
|
|
589
|
+
])
|
|
513
590
|
})
|
|
514
591
|
|
|
515
|
-
test('connect without a clientId for MQTT 3.1.1',
|
|
592
|
+
test('connect without a clientId for MQTT 3.1.1', async (t) => {
|
|
516
593
|
t.plan(1)
|
|
517
594
|
|
|
518
|
-
const
|
|
519
|
-
t.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
length: 2,
|
|
534
|
-
qos: 0,
|
|
535
|
-
retain: false,
|
|
536
|
-
dup: false,
|
|
537
|
-
topic: null,
|
|
538
|
-
payload: null,
|
|
539
|
-
sessionPresent: false
|
|
540
|
-
}, 'successful connack')
|
|
541
|
-
})
|
|
595
|
+
const broker = await Aedes.createBroker()
|
|
596
|
+
t.after(() => broker.close())
|
|
597
|
+
const s1 = setup(broker)
|
|
598
|
+
const packet = await connect(s1, { noClientId: true })
|
|
599
|
+
t.assert.deepEqual(structuredClone(packet), {
|
|
600
|
+
cmd: 'connack',
|
|
601
|
+
returnCode: 0,
|
|
602
|
+
length: 2,
|
|
603
|
+
qos: 0,
|
|
604
|
+
retain: false,
|
|
605
|
+
dup: false,
|
|
606
|
+
topic: null,
|
|
607
|
+
payload: null,
|
|
608
|
+
sessionPresent: false
|
|
609
|
+
}, 'successful connack')
|
|
542
610
|
})
|
|
543
611
|
|
|
544
|
-
test('disconnect existing client with the same clientId',
|
|
612
|
+
test('disconnect existing client with the same clientId', async (t) => {
|
|
545
613
|
t.plan(2)
|
|
546
614
|
|
|
547
|
-
const broker =
|
|
548
|
-
t.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
connect(setup(broker), {
|
|
558
|
-
clientId: 'abcde'
|
|
559
|
-
}, function () {
|
|
560
|
-
t.pass('second client connected')
|
|
561
|
-
})
|
|
562
|
-
})
|
|
615
|
+
const broker = await Aedes.createBroker()
|
|
616
|
+
t.after(() => broker.close())
|
|
617
|
+
const s1 = setup(broker)
|
|
618
|
+
await connect(s1, { clientId: 'abcde' })
|
|
619
|
+
const s2 = setup(broker)
|
|
620
|
+
await connect(s2, { clientId: 'abcde' })
|
|
621
|
+
t.assert.equal(s2.conn.closed, false, 's2 is still connected')
|
|
622
|
+
t.assert.equal(s1.conn.closed, true, 's1 has been disconnected')
|
|
563
623
|
})
|
|
564
624
|
|
|
565
|
-
test('disconnect if another broker connects the same clientId',
|
|
566
|
-
t.plan(
|
|
567
|
-
|
|
568
|
-
const broker = aedes()
|
|
569
|
-
t.teardown(broker.close.bind(broker))
|
|
570
|
-
|
|
571
|
-
const c1 = connect(setup(broker), {
|
|
572
|
-
clientId: 'abcde'
|
|
573
|
-
}, function () {
|
|
574
|
-
eos(c1.conn, function () {
|
|
575
|
-
t.pass('disconnect first client')
|
|
576
|
-
})
|
|
625
|
+
test('disconnect if another broker connects the same clientId', async (t) => {
|
|
626
|
+
t.plan(1)
|
|
577
627
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
t.pass('second client connects to another broker')
|
|
583
|
-
})
|
|
628
|
+
const s = await createAndConnect(t, { connect: { clientId: 'abcde' } })
|
|
629
|
+
await brokerPublish(s, {
|
|
630
|
+
topic: '$SYS/anotherBroker/new/clients',
|
|
631
|
+
payload: Buffer.from('abcde')
|
|
584
632
|
})
|
|
633
|
+
t.assert.equal(s.conn.closed, true, 'client has been disconnected')
|
|
585
634
|
})
|
|
586
635
|
|
|
587
|
-
test('publish to $SYS/broker/new/clients',
|
|
636
|
+
test('publish to $SYS/broker/new/clients', async (t) => {
|
|
588
637
|
t.plan(1)
|
|
589
638
|
|
|
590
|
-
const broker =
|
|
591
|
-
t.
|
|
639
|
+
const broker = await Aedes.createBroker()
|
|
640
|
+
t.after(() => broker.close())
|
|
641
|
+
const s = setup(broker)
|
|
592
642
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
clientId: 'abcde'
|
|
643
|
+
const brokerBroadcasted = new Promise(resolve => {
|
|
644
|
+
broker.mq.on(`$SYS/${broker.id}/new/clients`, (packet, done) => {
|
|
645
|
+
t.assert.equal(packet.payload.toString(), 'abcde', 'clientId matches')
|
|
646
|
+
resolve()
|
|
647
|
+
done()
|
|
648
|
+
})
|
|
600
649
|
})
|
|
650
|
+
// run parallel
|
|
651
|
+
await Promise.all([
|
|
652
|
+
brokerBroadcasted,
|
|
653
|
+
connect(s, { connect: { clientId: 'abcde' } })
|
|
654
|
+
])
|
|
601
655
|
})
|
|
602
656
|
|
|
603
|
-
test('publish to $SYS/broker/new/
|
|
657
|
+
test('publish to $SYS/broker/new/subscribers and $SYS/broker/new/unsubscribers', async (t) => {
|
|
604
658
|
t.plan(7)
|
|
605
|
-
|
|
606
|
-
t
|
|
659
|
+
|
|
660
|
+
const subscriber = await createAndConnect(t, { connect: { clean: false, clientId: 'abcde' } })
|
|
661
|
+
const broker = subscriber.broker
|
|
607
662
|
|
|
608
663
|
const sub = {
|
|
609
664
|
topic: 'hello',
|
|
610
665
|
qos: 0
|
|
611
666
|
}
|
|
612
667
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
668
|
+
const subscribeBroadcasted = new Promise(resolve => {
|
|
669
|
+
broker.mq.on(`$SYS/${broker.id}/new/subscribes`, (packet, done) => {
|
|
670
|
+
const payload = JSON.parse(packet.payload.toString())
|
|
671
|
+
t.assert.equal(payload.clientId, 'abcde', 'clientId matches')
|
|
672
|
+
t.assert.deepEqual(payload.subs, [sub], 'subscriptions matches')
|
|
673
|
+
resolve()
|
|
674
|
+
done()
|
|
675
|
+
})
|
|
618
676
|
})
|
|
619
677
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
678
|
+
const unsubscribeBroadcasted = new Promise(resolve => {
|
|
679
|
+
broker.mq.on(`$SYS/${broker.id}/new/unsubscribes`, (packet, done) => {
|
|
680
|
+
const payload = JSON.parse(packet.payload.toString())
|
|
681
|
+
t.assert.equal(payload.clientId, 'abcde', 'clientId matches')
|
|
682
|
+
t.assert.deepEqual(payload.subs, [sub.topic], 'unsubscriptions matches')
|
|
683
|
+
resolve()
|
|
684
|
+
done()
|
|
685
|
+
})
|
|
625
686
|
})
|
|
626
687
|
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
messageId: 43,
|
|
634
|
-
unsubscriptions: ['hello']
|
|
635
|
-
})
|
|
688
|
+
const subUnsub = async () => {
|
|
689
|
+
await subscribe(t, subscriber, sub.topic, sub.qos)
|
|
690
|
+
subscriber.inStream.write({
|
|
691
|
+
cmd: 'unsubscribe',
|
|
692
|
+
messageId: 43,
|
|
693
|
+
unsubscriptions: ['hello']
|
|
636
694
|
})
|
|
637
|
-
}
|
|
695
|
+
}
|
|
696
|
+
// run parallel
|
|
697
|
+
await Promise.all([
|
|
698
|
+
subscribeBroadcasted,
|
|
699
|
+
unsubscribeBroadcasted,
|
|
700
|
+
subUnsub()
|
|
701
|
+
])
|
|
638
702
|
})
|
|
639
703
|
|
|
640
|
-
test('restore QoS 0 subscriptions not clean',
|
|
704
|
+
test('restore QoS 0 subscriptions not clean', async (t) => {
|
|
641
705
|
t.plan(5)
|
|
642
706
|
|
|
643
|
-
const broker = aedes()
|
|
644
|
-
t.teardown(broker.close.bind(broker))
|
|
645
|
-
|
|
646
707
|
const expected = {
|
|
647
708
|
cmd: 'publish',
|
|
648
709
|
topic: 'hello',
|
|
@@ -653,68 +714,57 @@ test('restore QoS 0 subscriptions not clean', function (t) {
|
|
|
653
714
|
retain: false
|
|
654
715
|
}
|
|
655
716
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
t.same(packet, expected, 'packet must match')
|
|
675
|
-
})
|
|
676
|
-
})
|
|
677
|
-
})
|
|
717
|
+
const subscriber = await createAndConnect(t, { connect: { clean: false, clientId: 'abcde' } })
|
|
718
|
+
const broker = subscriber.broker
|
|
719
|
+
await subscribe(t, subscriber, 'hello', 0)
|
|
720
|
+
// hangup
|
|
721
|
+
subscriber.inStream.end()
|
|
722
|
+
|
|
723
|
+
// start second connection
|
|
724
|
+
const publisher = setup(broker)
|
|
725
|
+
await connect(publisher)
|
|
726
|
+
// start third connection
|
|
727
|
+
const subscriber2 = setup(broker)
|
|
728
|
+
const connack = await connect(subscriber2, { connect: { clean: false, clientId: 'abcde' } })
|
|
729
|
+
t.assert.equal(connack.sessionPresent, true, 'session present is set to true')
|
|
730
|
+
publisher.inStream.write({
|
|
731
|
+
cmd: 'publish',
|
|
732
|
+
topic: 'hello',
|
|
733
|
+
payload: 'world',
|
|
734
|
+
qos: 0
|
|
678
735
|
})
|
|
736
|
+
const packet = await nextPacket(subscriber2)
|
|
737
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet must match')
|
|
679
738
|
})
|
|
680
739
|
|
|
681
|
-
test('do not restore QoS 0 subscriptions when clean',
|
|
682
|
-
t.plan(
|
|
683
|
-
|
|
684
|
-
const broker = aedes()
|
|
685
|
-
t.teardown(broker.close.bind(broker))
|
|
740
|
+
test('do not restore QoS 0 subscriptions when clean', async (t) => {
|
|
741
|
+
t.plan(6)
|
|
686
742
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
})
|
|
707
|
-
})
|
|
708
|
-
subscriber.outStream.once('data', function (packet) {
|
|
709
|
-
t.fail('packet received')
|
|
710
|
-
})
|
|
711
|
-
})
|
|
712
|
-
})
|
|
743
|
+
const subscriber = await createAndConnect(t, { connect: { clean: true, clientId: 'abcde' } })
|
|
744
|
+
const broker = subscriber.broker
|
|
745
|
+
await subscribe(t, subscriber, 'hello', 0)
|
|
746
|
+
// hangup
|
|
747
|
+
subscriber.inStream.end()
|
|
748
|
+
|
|
749
|
+
const subs = await subscriber.broker.persistence.subscriptionsByClient(broker.clients.abcde)
|
|
750
|
+
t.assert.deepEqual(subs, [], 'no previous subscriptions restored')
|
|
751
|
+
|
|
752
|
+
const publisher = setup(broker)
|
|
753
|
+
await connect(publisher)
|
|
754
|
+
const subscriber2 = setup(broker)
|
|
755
|
+
const connack = await connect(subscriber2, { connect: { clean: true, clientId: 'abcde' } })
|
|
756
|
+
t.assert.equal(connack.sessionPresent, false, 'session present is set to false')
|
|
757
|
+
publisher.inStream.write({
|
|
758
|
+
cmd: 'publish',
|
|
759
|
+
topic: 'hello',
|
|
760
|
+
payload: 'world',
|
|
761
|
+
qos: 0
|
|
713
762
|
})
|
|
763
|
+
await checkNoPacket(t, subscriber2)
|
|
714
764
|
})
|
|
715
765
|
|
|
716
|
-
test('double sub does not double deliver',
|
|
717
|
-
t.plan(
|
|
766
|
+
test('double sub does not double deliver', async (t) => {
|
|
767
|
+
t.plan(8)
|
|
718
768
|
|
|
719
769
|
const expected = {
|
|
720
770
|
cmd: 'publish',
|
|
@@ -725,30 +775,24 @@ test('double sub does not double deliver', function (t) {
|
|
|
725
775
|
qos: 0,
|
|
726
776
|
retain: false
|
|
727
777
|
}
|
|
728
|
-
const s = connect(setup(), {
|
|
729
|
-
}, function () {
|
|
730
|
-
subscribe(t, s, 'hello', 0, function () {
|
|
731
|
-
subscribe(t, s, 'hello', 0, function () {
|
|
732
|
-
s.outStream.once('data', function (packet) {
|
|
733
|
-
t.same(packet, expected, 'packet matches')
|
|
734
|
-
s.outStream.on('data', function () {
|
|
735
|
-
t.fail('double deliver')
|
|
736
|
-
})
|
|
737
|
-
})
|
|
738
778
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
779
|
+
const s = await createAndConnect(t, { connect: { clean: false, clientId: 'abcde' } })
|
|
780
|
+
await subscribe(t, s, 'hello', 0)
|
|
781
|
+
await subscribe(t, s, 'hello', 0)
|
|
782
|
+
|
|
783
|
+
s.broker.publish({
|
|
784
|
+
cmd: 'publish',
|
|
785
|
+
topic: 'hello',
|
|
786
|
+
payload: 'world'
|
|
746
787
|
})
|
|
747
|
-
|
|
788
|
+
|
|
789
|
+
const packet = await nextPacket(s)
|
|
790
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet matches')
|
|
791
|
+
await checkNoPacket(t, s)
|
|
748
792
|
})
|
|
749
793
|
|
|
750
|
-
test('overlapping sub does not double deliver',
|
|
751
|
-
t.plan(
|
|
794
|
+
test('overlapping sub does not double deliver', async (t) => {
|
|
795
|
+
t.plan(8)
|
|
752
796
|
|
|
753
797
|
const expected = {
|
|
754
798
|
cmd: 'publish',
|
|
@@ -759,107 +803,101 @@ test('overlapping sub does not double deliver', function (t) {
|
|
|
759
803
|
qos: 0,
|
|
760
804
|
retain: false
|
|
761
805
|
}
|
|
762
|
-
const s =
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
subscribe(t, s, 'hello/#', 0, function () {
|
|
766
|
-
s.outStream.once('data', function (packet) {
|
|
767
|
-
t.same(packet, expected, 'packet matches')
|
|
768
|
-
s.outStream.on('data', function () {
|
|
769
|
-
t.fail('double deliver')
|
|
770
|
-
})
|
|
771
|
-
})
|
|
806
|
+
const s = await createAndConnect(t, { connect: { clean: false, clientId: 'abcde' } })
|
|
807
|
+
await subscribe(t, s, 'hello', 0)
|
|
808
|
+
await subscribe(t, s, 'hello/#', 0)
|
|
772
809
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
})
|
|
778
|
-
})
|
|
779
|
-
})
|
|
810
|
+
s.broker.publish({
|
|
811
|
+
cmd: 'publish',
|
|
812
|
+
topic: 'hello',
|
|
813
|
+
payload: 'world'
|
|
780
814
|
})
|
|
781
|
-
|
|
815
|
+
|
|
816
|
+
const packet = await nextPacket(s)
|
|
817
|
+
t.assert.deepEqual(structuredClone(packet), expected, 'packet matches')
|
|
818
|
+
await checkNoPacket(t, s)
|
|
782
819
|
})
|
|
783
820
|
|
|
784
|
-
test('clear drain',
|
|
785
|
-
t.plan(
|
|
821
|
+
test('clear drain', async (t) => {
|
|
822
|
+
t.plan(5)
|
|
786
823
|
|
|
787
|
-
const s =
|
|
788
|
-
|
|
789
|
-
subscribe(t, s, 'hello', 0, function () {
|
|
790
|
-
// fake a busy socket
|
|
791
|
-
s.conn.write = function (chunk, enc, cb) {
|
|
792
|
-
return false
|
|
793
|
-
}
|
|
824
|
+
const s = await createAndConnect(t, { broker: { drainTimeout: 0 } }) // Disable timeout to test old behavior
|
|
825
|
+
await subscribe(t, s, 'hello', 0)
|
|
794
826
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
827
|
+
// fake a busy socket
|
|
828
|
+
let written = false // 1 packet requires multiple writes, just count 1
|
|
829
|
+
s.client.conn.write = (chunk, enc, cb) => {
|
|
830
|
+
if (!written) {
|
|
831
|
+
t.assert.ok(true, 'write called')
|
|
832
|
+
written = true
|
|
833
|
+
}
|
|
834
|
+
return false
|
|
835
|
+
}
|
|
802
836
|
|
|
803
|
-
|
|
837
|
+
const publish = async () => {
|
|
838
|
+
await brokerPublish(s, {
|
|
839
|
+
cmd: 'publish',
|
|
840
|
+
topic: 'hello',
|
|
841
|
+
payload: 'world'
|
|
804
842
|
})
|
|
805
|
-
})
|
|
806
843
|
|
|
807
|
-
|
|
844
|
+
t.assert.ok(true, 'published packet')
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// run parallel
|
|
848
|
+
await Promise.all([
|
|
849
|
+
publish(),
|
|
850
|
+
s.client.conn.destroy()
|
|
851
|
+
])
|
|
808
852
|
})
|
|
809
853
|
|
|
810
|
-
test('id option',
|
|
854
|
+
test('id option', (t) => {
|
|
811
855
|
t.plan(2)
|
|
812
856
|
|
|
813
|
-
const broker1 =
|
|
814
|
-
|
|
815
|
-
setup(broker1).conn.destroy()
|
|
816
|
-
t.ok(broker1.id, 'broker gets random id when id option not set')
|
|
857
|
+
const broker1 = new Aedes()
|
|
858
|
+
t.assert.ok(broker1.id, 'broker gets random id when id option not set')
|
|
817
859
|
|
|
818
|
-
const broker2 =
|
|
819
|
-
|
|
820
|
-
t.equal(broker2.id, 'abc', 'broker id equals id option when set')
|
|
821
|
-
|
|
822
|
-
t.teardown(() => {
|
|
823
|
-
broker1.close()
|
|
824
|
-
broker2.close()
|
|
825
|
-
})
|
|
860
|
+
const broker2 = new Aedes({ id: 'abc' })
|
|
861
|
+
t.assert.equal(broker2.id, 'abc', 'broker id equals id option when set')
|
|
826
862
|
})
|
|
827
863
|
|
|
828
|
-
test('not duplicate client close when client error occurs',
|
|
829
|
-
t.plan(
|
|
864
|
+
test('not duplicate client close when client error occurs', async (t) => {
|
|
865
|
+
t.plan(2)
|
|
830
866
|
|
|
831
|
-
const
|
|
832
|
-
|
|
867
|
+
const s = await createAndConnect(t)
|
|
868
|
+
const checkDoubleDrain = async () => {
|
|
869
|
+
await once(s.client.conn, 'drain')
|
|
870
|
+
t.assert.ok(true, 'client closed ok')
|
|
871
|
+
const result = await withTimeout(once(s.client.conn, 'drain'), 10, ['timeout'])
|
|
872
|
+
t.assert.equal(result, 'timeout', 'no double client close calls')
|
|
873
|
+
}
|
|
833
874
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
client.close()
|
|
840
|
-
// add back to test if there is duplicated close() call
|
|
841
|
-
client.conn.on('drain', () => {
|
|
842
|
-
t.fail('double client close calls')
|
|
843
|
-
})
|
|
844
|
-
})
|
|
875
|
+
// run parallel
|
|
876
|
+
await Promise.all([
|
|
877
|
+
checkDoubleDrain(),
|
|
878
|
+
s.client.close()
|
|
879
|
+
])
|
|
845
880
|
})
|
|
846
881
|
|
|
847
|
-
test('not duplicate client close when double close() called',
|
|
848
|
-
t.plan(
|
|
882
|
+
test('not duplicate client close when double close() called', async (t) => {
|
|
883
|
+
t.plan(2)
|
|
849
884
|
|
|
850
|
-
const
|
|
851
|
-
|
|
885
|
+
const s = await createAndConnect(t)
|
|
886
|
+
const checkDoubleDrain = async () => {
|
|
887
|
+
await once(s.client.conn, 'drain')
|
|
888
|
+
t.assert.ok(true, 'client closed ok')
|
|
889
|
+
const result = await withTimeout(once(s.client.conn, 'drain'), 10, ['timeout'])
|
|
890
|
+
t.assert.equal(result, 'timeout', 'no double client close calls')
|
|
891
|
+
}
|
|
892
|
+
const doubleClose = async () => {
|
|
893
|
+
s.client.close()
|
|
894
|
+
await delay(1)
|
|
895
|
+
s.client.close()
|
|
896
|
+
}
|
|
852
897
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
client.close()
|
|
859
|
-
// add back to test if there is duplicated close() call
|
|
860
|
-
client.conn.on('drain', () => {
|
|
861
|
-
t.fail('double execute client close function')
|
|
862
|
-
})
|
|
863
|
-
client.close()
|
|
864
|
-
})
|
|
898
|
+
// run parallel
|
|
899
|
+
await Promise.all([
|
|
900
|
+
checkDoubleDrain(),
|
|
901
|
+
doubleClose()
|
|
902
|
+
])
|
|
865
903
|
})
|