Files
2026-07-13 12:06:16 +08:00

243 lines
5.9 KiB
JavaScript

'use strict'
const { test } = require('tap')
const mqemitter = require('mqemitter')
const { setup, connect, subscribe } = require('./helper')
const aedes = require('../')
test('publishes an hearbeat', function (t) {
t.plan(2)
const broker = aedes({
heartbeatInterval: 10 // ms
})
t.teardown(broker.close.bind(broker))
broker.subscribe('$SYS/+/heartbeat', function (message, cb) {
const id = message.topic.match(/\$SYS\/([^/]+)\/heartbeat/)[1]
t.equal(id, broker.id, 'broker id matches')
t.same(message.payload.toString(), id, 'message has id as the payload')
cb()
})
})
test('publishes birth', function (t) {
t.plan(4)
const mq = mqemitter()
const brokerId = 'test-broker'
const fakeBroker = 'fake-broker'
const clientId = 'test-client'
mq.on(`$SYS/${brokerId}/birth`, (message, cb) => {
t.pass('broker birth received')
t.same(message.payload.toString(), brokerId, 'message has id as the payload')
cb()
})
const broker = aedes({
id: brokerId,
mq
})
broker.on('client', (client) => {
t.equal(client.id, clientId, 'client connected')
// set a fake counter on a fake broker
process.nextTick(() => {
broker.clients[clientId].duplicates[fakeBroker] = 42
mq.emit({ topic: `$SYS/${fakeBroker}/birth`, payload: Buffer.from(fakeBroker) })
})
})
mq.on(`$SYS/${fakeBroker}/birth`, (message, cb) => {
process.nextTick(() => {
t.equal(!!broker.clients[clientId].duplicates[fakeBroker], false, 'client duplicates has been resetted')
cb()
})
})
const s = connect(setup(broker), { clientId })
t.teardown(s.broker.close.bind(s.broker))
})
;['$mcollina', '$SYS'].forEach(function (topic) {
test('does not forward $ prefixed topics to # subscription - ' + topic, function (t) {
t.plan(4)
const s = connect(setup())
t.teardown(s.broker.close.bind(s.broker))
subscribe(t, s, '#', 0, function () {
s.outStream.once('data', function (packet) {
t.fail('no packet should be received')
})
s.broker.mq.emit({
cmd: 'publish',
topic: topic + '/hello',
payload: 'world'
}, function () {
t.pass('nothing happened')
})
})
})
test('does not forward $ prefixed topics to +/# subscription - ' + topic, function (t) {
t.plan(4)
const s = connect(setup())
t.teardown(s.broker.close.bind(s.broker))
subscribe(t, s, '+/#', 0, function () {
s.outStream.once('data', function (packet) {
t.fail('no packet should be received')
})
s.broker.mq.emit({
cmd: 'publish',
topic: topic + '/hello',
payload: 'world'
}, function () {
t.pass('nothing happened')
})
})
})
})
test('does not store $SYS topics to QoS 1 # subscription', function (t) {
t.plan(3)
const broker = aedes()
t.teardown(broker.close.bind(broker))
const opts = { clean: false, clientId: 'abcde' }
let s = connect(setup(broker), opts)
subscribe(t, s, '#', 1, function () {
s.inStream.end()
s.broker.publish({
cmd: 'publish',
topic: '$SYS/hello',
payload: 'world',
qos: 1
}, function () {
s = connect(setup(broker), { clean: false, clientId: 'abcde' })
s.outStream.once('data', function (packet) {
t.fail('no packet should be received')
})
})
})
})
test('Emit event when receives a ping', { timeout: 2000 }, function (t) {
t.plan(5)
const broker = aedes()
t.teardown(broker.close.bind(broker))
broker.on('ping', function (packet, client) {
if (client && client) {
t.equal(client.id, 'abcde')
t.equal(packet.cmd, 'pingreq')
t.equal(packet.payload, null)
t.equal(packet.topic, null)
t.equal(packet.length, 0)
}
})
const s = connect(setup(broker), { clientId: 'abcde' })
s.inStream.write({
cmd: 'pingreq'
})
})
test('Emit event when broker closed', function (t) {
t.plan(1)
const broker = aedes()
broker.once('closed', function () {
t.ok(true)
})
broker.close()
})
test('Emit closed event one only when double broker.close()', function (t) {
t.plan(4)
const broker = aedes()
broker.on('closed', function () {
t.pass('closed')
})
t.notOk(broker.closed)
broker.close()
t.ok(broker.closed)
broker.close()
t.ok(broker.closed)
})
test('Test backpressure aedes published function', function (t) {
t.plan(2)
let publishCount = 10
let count = 0
const broker = aedes({
published: function (packet, client, done) {
if (client) {
count++
setTimeout(() => {
publisher.end()
done()
})
} else { done() }
}
})
const mqtt = require('mqtt')
const server = require('net').createServer(broker.handle)
let publisher
server.listen(0, function () {
const port = server.address().port
publisher = mqtt.connect({ port, host: 'localhost', clean: true, keepalive: 30 })
function next () {
if (--publishCount > 0) { process.nextTick(publish) }
}
function publish () {
publisher.publish('test', 'payload', next)
}
publisher.on('connect', publish)
publisher.on('end', function () {
t.ok(count > publishCount)
t.equal(publishCount, 0)
broker.close()
server.close()
})
})
})
test('clear closed clients when the same clientId is managed by another broker', function (t) {
t.plan(2)
const clientId = 'closed-client'
const aedesBroker = aedes()
// simulate a closed client on the broker
aedesBroker.clients[clientId] = { closed: true, broker: aedesBroker }
aedesBroker.connectedClients = 1
// simulate the creation of the same client on another broker of the cluster
aedesBroker.publish({ topic: '$SYS/anotherbroker/new/clients', payload: clientId }, () => {
t.equal(aedesBroker.clients[clientId], undefined) // check that the closed client was removed
t.equal(aedesBroker.connectedClients, 0)
})
t.teardown(aedesBroker.close.bind(aedesBroker))
})