UNPKG

mosca

Version:
1,972 lines (1,607 loc) 64.7 kB
var steed = require("steed"); var ascoltatori = require("ascoltatori"); module.exports = function(moscaSettings, createConnection) { var instance; var secondInstance; var settings; beforeEach(function(done) { settings = moscaSettings(); settings.publishNewClient = false; settings.publishClientDisconnect = false; instance = new mosca.Server(settings, done); this.instance = instance; this.settings = settings; secondInstance = null; }); afterEach(function(done) { var instances = [this.instance]; if (secondInstance) { instances.push(secondInstance); } steed.each(instances, function(instance, cb) { instance.close(cb); }, function() { setImmediate(done); }); }); function buildClient(done, callback) { var client = createConnection(settings.port, settings.host); client.once('error', finish); client.stream.once('close', finish); client.on("connected", function() { callback(client); }); function finish () { client.removeListener('error', finish); client.stream.removeListener('close', finish); done(); } } function buildAndConnect(done, opts, callback) { if (typeof opts === "function") { callback = opts; opts = buildOpts(); } buildClient(done, function(client) { client.opts = opts; client.connect(opts); client.on('connack', function(packet) { callback(client, packet); }); }); } it("should publish connected client to '$SYS/{broker-id}/new/clients'", function(done) { var connectedClient = null, publishedClientId = null; settings = moscaSettings(); settings.publishNewClient = true; settings.publishClientDisconnect = false; function verify() { if (connectedClient && publishedClientId) { expect(publishedClientId).to.be.equal(connectedClient.opts.clientId); connectedClient.disconnect(); } } secondInstance = new mosca.Server(settings, function(err, server) { server.on("published", function(packet, clientId) { expect(packet.topic).to.be.equal("$SYS/" + secondInstance.id + "/new/clients"); publishedClientId = packet.payload.toString(); verify(); }); buildAndConnect(done, function(client) { connectedClient = client; verify(); }); }); }); it("should publish disconnected client to '$SYS/{broker-id}/disconnect/clients'", function(done) { var connectedClient = null, publishedClientId = null; settings = moscaSettings(); settings.publishNewClient = false; settings.publishClientDisconnect = true; function verify() { if (connectedClient && publishedClientId) { expect(publishedClientId).to.be.equal(connectedClient.opts.clientId); done(); } } secondInstance = new mosca.Server(settings, function(err, server) { server.on("published", function(packet, clientId) { expect(packet.topic).to.be.equal("$SYS/" + secondInstance.id + "/disconnect/clients"); publishedClientId = packet.payload.toString(); verify(); }); buildAndConnect(function(){}, function(client) { connectedClient = client; connectedClient.disconnect(); }); }); }); it("should publish each subscribe to '$SYS/{broker-id}/new/subscribes'", function(done) { var d = donner(2, done); var connectedClient = null; var publishedClientId = null; function verify() { if (connectedClient && publishedClientId) { expect(publishedClientId).to.be.equal(connectedClient.opts.clientId); d(); } } buildAndConnect(d, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; connectedClient = client; instance.once("published", function(packet) { expect(packet.topic).to.be.equal("$SYS/" + instance.id + "/new/subscribes"); var payload = JSON.parse( packet.payload.toString() ); publishedClientId = payload.clientId; expect(payload.topic).to.be.equal('hello'); verify(); client.disconnect(); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should publish each unsubscribe to '$SYS/{broker-id}/new/unsubscribes'", function(done) { var d = donner(2, done), connectedClient = null, publishedClientId = null; function verify() { if (connectedClient && publishedClientId) { expect(publishedClientId).to.be.equal(connectedClient.opts.clientId); d(); } } buildAndConnect(d, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 }]; connectedClient = client; instance.once("published", function(packet) { expect(packet.topic).to.be.equal("$SYS/" + instance.id + "/new/subscribes"); client.unsubscribe({ unsubscriptions: ["hello"], messageId: messageId }); instance.once("published", function(packet) { expect(packet.topic).to.be.equal("$SYS/" + instance.id + "/new/unsubscribes"); var payload = JSON.parse( packet.payload.toString() ); expect(payload.topic).to.be.equal('hello'); publishedClientId = payload.clientId; verify(); client.disconnect(); }); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); describe("multi mosca servers", function() { var serverOne = null, serverTwo = null, clientOpts = buildOpts(); afterEach(function(done) { var instances = []; instances.push(serverOne); instances.push(serverTwo); steed.each(instances, function(instance, cb) { if (instance) { instance.close(cb); } else { cb(); } }, function() { setImmediate(done); }); }); it("should disconnect client connected to another broker", function(done) { var settingsOne = moscaSettings(), settingsTwo = moscaSettings(); if (!settings.backend || !settings.backend.type) { // only need to validate cases with backend return done(); } clientOpts.clientId = '123456'; clientOpts.keepalive = 0; settingsOne.publishNewClient = settingsTwo.publishNewClient = true; settingsOne.backend = settingsTwo.backend = settings.backend; steed.series([ function(cb) { serverOne = new mosca.Server(settingsOne, function(err, server) { serverOne.on('clientDisconnected', function(serverClient, reason) { expect(reason).to.be.equal('new connection request'); expect(serverClient).not.to.be.equal(undefined); done(); }); cb(); }); }, function(cb) { serverTwo = new mosca.Server(settingsTwo, function(err, server) { cb(); }); }, function(cb) { var clientOne = createConnection(settingsOne.port, settingsOne.host); clientOne.connect(clientOpts); clientOne.on("connected", function() { cb(); }); }, function(cb) { var clientTwo = createConnection(settingsTwo.port, settingsTwo.host); clientTwo.connect(clientOpts); clientTwo.on("connected", function() { cb(); }); } ]); }); }); it("should pass itself in the callback", function(done) { secondInstance = new mosca.Server(moscaSettings(), function(err, server) { expect(server === secondInstance).to.be.true; done(); }); }); it("should allow to be called like a function", function(done) { var func = mosca.Server; secondInstance = func(moscaSettings(), function(err, server) { expect(server === secondInstance).to.be.true; done(); }); }); it("should support connecting and disconnecting", function(done) { buildClient(done, function(client) { client.connect(buildOpts()); client.on('connack', function(packet) { client.disconnect(); }); }); }); it("should support connecting and disconnecting with a zero keepalive", function(done) { var client = createConnection(settings.port, settings.host); var disconnect = false; client.once('error', done); client.stream.once('close', function() { expect(disconnect).to.be.true; done(); }); client.on("connected", function() { var opts = buildOpts(); opts.keepalive = 0; client.connect(opts); }); client.on("connack", function() { setTimeout(function() { disconnect = true; client.disconnect(); }, 5); }); }); it("should send a connack packet with returnCode 0", function(done) { buildClient(done, function(client) { client.connect(buildOpts()); client.on('connack', function(packet) { client.disconnect(); expect(packet.returnCode).to.eql(0); }); }); }); it("should send a connack packet with returnCode 0 if the clientId is 65535 chars", function(done) { buildClient(done, function(client) { var opts = buildOpts(), clientId = []; for(var i=0; i < 65535; i++) { clientId.push("i"); } opts.clientId = clientId.join(""); client.connect(opts); client.on('connack', function(packet) { client.disconnect(); expect(packet.returnCode).to.eql(0); }); }); }); it("should send a connack packet with returnCode 0 if the clientId is 1 char", function(done) { buildClient(done, function(client) { var opts = buildOpts(); opts.clientId = "i"; client.connect(opts); client.on('connack', function(packet) { client.disconnect(); expect(packet.returnCode).to.eql(0); }); }); }); it("should close the first client if a second client with the same clientId connects", function(done) { var d = donner(2, done); var opts = buildOpts(), clientId = "123456789"; opts.clientId = clientId; steed.waterfall([ function(cb) { buildAndConnect(d, opts, function(client1) { cb(null, client1); }); }, function(client1, cb) { buildAndConnect(d, opts, function(client2) { // no need to check if client1 is destroyed // if not, this test will timeout client2.disconnect(); }); } ]); }); it("should generate a random clientId if none is supplied by the client and protocol is 3.1.1", function(done) { var connectedClient = null, publishedClientId = null, opts = { keepalive: 1000, clientId: '', protocolId: 'MQTT', protocolVersion: 4 }; settings = moscaSettings(); settings.publishNewClient = true; settings.publishClientDisconnect = false; function verify() { if (connectedClient && publishedClientId) { expect(publishedClientId).to.be.ok; connectedClient.disconnect(); } } secondInstance = new mosca.Server(settings, function(err, server) { server.on("published", function(packet, clientId) { expect(packet.topic).to.be.equal("$SYS/" + secondInstance.id + "/new/clients"); publishedClientId = packet.payload.toString(); verify(); }); buildAndConnect(done, opts, function(client) { connectedClient = client; verify(); }); }); }); it("should send a pingresp when it receives a pingreq", function(done) { buildAndConnect(done, function(client) { client.on("pingresp", function() { client.disconnect(); }); client.pingreq(); }); }); it("should support subscribing", function(done) { buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 0 } ]; client.on("suback", function(packet) { expect(packet).to.have.property("messageId", messageId); client.disconnect(); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should emit an event for each subscribe", function(done) { var d = donner(2, done); buildAndConnect(d, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.on("suback", function(packet) { client.disconnect(); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); instance.on("subscribed", function(topic, client) { expect(topic).to.eql("hello"); expect(client).to.exist; d(); }); }); it("should support subscribing to multiple topics", function(done) { buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 }, { topic: "hello2", qos: 0 } ]; client.on("suback", function(packet) { client.disconnect(); expect(packet.granted).to.be.deep.equal([1, 0]); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should support subscribing and publishing", function(done) { var d = donner(2, done); buildAndConnect(d, function(client1) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 0 } ]; client1.on("publish", function(packet) { expect(packet.topic).to.be.equal("hello"); expect(packet.payload.toString()).to.be.equal("some data"); client1.disconnect(); }); client1.on("suback", function() { buildAndConnect(d, function(client2) { client2.publish({ topic: "hello", payload: "some data", messageId: messageId }); client2.disconnect(); }); }); client1.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should support publishing big messages", function(done) { var d = donner(2, done); var bigPayload = new Buffer(5 * 1024); bigPayload.fill("42"); buildAndConnect(d, function(client1) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 0 } ]; client1.on("publish", function(packet) { expect(packet.topic).to.be.equal("hello"); expect(packet.payload.toString().length).to.be.equal(bigPayload.length); client1.disconnect(); }); client1.on("suback", function() { buildAndConnect(d, function(client2) { client2.publish({ topic: "hello", payload: bigPayload, messageId: messageId }); client2.disconnect(); }); }); client1.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should support unsubscribing", function(done) { buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.on("unsuback", function(packet) { expect(packet).to.have.property("messageId", messageId); client.disconnect(); }); client.on("suback", function(packet) { client.unsubscribe({ unsubscriptions: ["hello"], messageId: messageId }); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should unsubscribe for real", function(done) { buildAndConnect(done, function(client) { client.on("publish", function(packet) { client.disconnect(); throw new Error("a message could not have been published"); }); client.on("unsuback", function(packet) { client.publish({ topic: "hello", payload: "data"  }); client.disconnect(); }); client.on("suback", function(packet) { client.unsubscribe({ unsubscriptions: ["hello"], messageId: messageId }); }); var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should unsubscribe from topics with multiple wildcards", function(done) { buildAndConnect(done, function(client) { client.on("publish", function(packet) { client.disconnect(); throw new Error("a message could not have been published"); }); client.on("unsuback", function(packet) { client.publish({ topic: "hello/foo/there/bar", payload: "data"  }); client.disconnect(); }); client.on("suback", function(packet) { client.unsubscribe({ unsubscriptions: ["hello/#/there/#"], messageId: messageId }); }); var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello/#/there/#", qos: 1 } ]; client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should emit an event for each unsubscribe", function(done) { var d = donner(2, done); buildAndConnect(d, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.on("unsuback", function(packet) { client.disconnect(); }); client.on("suback", function(packet) { client.unsubscribe({ unsubscriptions: ["hello"], messageId: messageId }); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); instance.on("unsubscribed", function(topic, client) { expect(topic).to.eql("hello"); expect(client).to.exist; d(); }); }); it("should emit an event for unsubscribe without subscribe", function(done) { var d = donner(2, done); buildAndConnect(d, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.on("unsuback", function(packet) { client.disconnect(); }); client.unsubscribe({ unsubscriptions: ["hello"], messageId: messageId }); }); instance.on("unsubscribed", function(topic, client) { expect(topic).to.eql("hello"); expect(client).to.exist; d(); }); }); it("should emit an event on every newly published packet", function(done) { buildAndConnect(done, function(client) { var clientId = client.opts.clientId; instance.on("published", function(packet, serverClient) { expect(packet.topic).to.be.equal("hello"); expect(packet.payload.toString().toString()).to.be.equal("some data"); expect(serverClient.id).to.be.equal(clientId); client.disconnect(); }); client.publish({ topic: "hello", payload: "some data" }); }); }); it("should emit an event for puback of each published packet", function(done) { buildAndConnect(done, function(client) { var clientId = client.opts.clientId; var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "delivery", qos: 1 }]; instance.on("delivered", function(packet, serverClient) { expect(packet.topic).to.be.equal("delivery"); expect(packet.payload.toString().toString()).to.be.equal("some data"); expect(serverClient.id).to.be.equal(clientId); client.disconnect(); }); instance.on("subscribed", function(topic, serverClient) { instance.publish({ topic: "delivery", payload: "some data", qos: 1 }); }); client.on("publish", function(packet){ client.puback({ messageId: packet.messageId }); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should call onPublished on every newly published packet", function(done) { var onPublishedCalled = false; var clientId; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("hello"); expect(packet.payload.toString().toString()).to.be.equal("some data"); expect(serverClient.id).to.be.equal(clientId); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "hello", payload: "some data", qos: 1 }); client.on("puback", function() { expect(onPublishedCalled).to.eql(true); client.disconnect(); }); }); }); // tests for local authorizePublish, with 'ignore' return var authorizePublishIgnore = function(client, topic, payload, callback) { var auth = true; if (topic === 'authignore'){ auth = 'ignore'; } if (topic === 'authfalse'){ auth = false; } callback(null, auth); }; it("should not call onPublished on publish to topic where auth='ignore'", function(done) { var onPublishedCalled = false; var clientId; var count = 0; instance.authorizePublish = authorizePublishIgnore; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("hello"); expect(packet.payload.toString().toString()).to.be.equal("some data"); expect(serverClient.id).to.be.equal(clientId); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "authignore", payload: "some data to ignore", qos: 1 }); client.publish({ messageId: 43, topic: "hello", payload: "some data", qos: 1 }); // auth='ignore' should puback, but not publish client.on("puback", function() { count++; // on second call, onPublished should be true if(count === 2){ expect(onPublishedCalled).to.eql(true); client.disconnect(); } }); }); }); it("should disconnect client on publish to topic where auth=false", function(done) { var onPublishedCalled = false; var clientId; var count = 0; var timer; instance.authorizePublish = authorizePublishIgnore; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("should not have published"); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "authfalse", payload: "some data to cause close", qos: 1 }); // if after 2 seconds, we've not closed timer = setTimeout(function(){ var test = false; expect(count).to.eql(0); expect(test).to.eql(true); client.disconnect(); }, 2000); // auth=false should NOT puback client.on("puback", function() { expect(onPublishedCalled).to.eql(false); count++; expect(count).to.eql(0); client.disconnect(); }); client.on("close", function() { expect(onPublishedCalled).to.eql(false); expect(count).to.eql(0); client.disconnect(); clearTimeout(timer); }); }); }); it("should by default not puback client publish to QOS 2", function(done) { var onPublishedCalled = false; var clientId; var count = 0; var timer; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("testQOS2"); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "testQOS2", payload: "publish expected", qos: 2 }); // allow 1 second to hear puback timer = setTimeout(function(){ client.disconnect(); }, 1000); // default QOS 2 should NOT puback client.on("puback", function() { count++; //expect(count).to.eql(1); client.disconnect(); }); client.on("close", function() { expect(count).to.eql(0); client.disconnect(); clearTimeout(timer); }); }); }); it("should optionally (onQoS2publish='dropToQoS1') puback client publish to QOS 2", function(done) { var onPublishedCalled = false; var clientId; var count = 0; var timer; instance.onQoS2publish = 'dropToQoS1'; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("testQOS2"); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "testQOS2", payload: "publish expected", qos: 2 }); // allow 1 second to hear puback timer = setTimeout(function(){ client.disconnect(); }, 1000); // with maxqos=1, QOS 2 should puback client.on("puback", function() { count++; expect(count).to.eql(1); client.disconnect(); }); client.on("close", function() { expect(count).to.eql(1); client.disconnect(); clearTimeout(timer); }); }); }); it("should optionally (onQoS2publish='disconnect') disconnect client on publish of QOS2 message", function(done) { var onPublishedCalled = false; var clientId; var count = 0; var timer; instance.onQoS2publish = 'disconnect'; instance.published = function(packet, serverClient, callback) { onPublishedCalled = true; expect(packet.topic).to.be.equal("should not have published"); callback(); }; buildAndConnect(done, function(client) { clientId = client.opts.clientId; client.publish({ messageId: 42, topic: "QOS2Test", payload: "some data to cause close", qos: 2 }); // if after 2 seconds, we've not closed timer = setTimeout(function(){ var test = false; expect(count).to.eql(0); expect(test).to.eql(true); client.disconnect(); }, 2000); // onQoS2publish = 'disconnect' should NOT puback client.on("puback", function() { expect(onPublishedCalled).to.eql(false); count++; expect(count).to.eql(0); client.disconnect(); }); client.on("close", function() { expect(onPublishedCalled).to.eql(false); expect(count).to.eql(0); client.disconnect(); clearTimeout(timer); }); }); }); it("should emit an event when a new client is connected", function(done) { buildClient(done, function(client) { instance.on("clientConnected", function(serverClient) { expect(serverClient).not.to.be.equal(undefined); client.stream.end(); }); client.connect(buildOpts()); }); }); it("should emit an event when a client is disconnected", function(done) { var client = createConnection(settings.port, settings.host); instance.on('clientDisconnected', function(serverClient, reason) { expect(reason).to.be.equal('disconnect request'); expect(serverClient).not.to.be.equal(undefined); done(); }); client.on('error', done); client.on('connack', function() { client.disconnect(); }); client.connect(buildOpts()); }); it("should emit only once clientDisconnected event per client", function(done) { var client = createConnection(settings.port, settings.host); instance.on('clientDisconnected', function(serverClient) { done(); }); client.on('error', done); client.on('connack', function() { client.disconnect(); client.disconnect(); setImmediate(function() { client.stream.end(); }); }); client.connect(buildOpts()); }); it("should emit an event when a client is disconnected without a disconnect", function(done) { var client = createConnection(settings.port, settings.host); instance.on('clientDisconnected', function(serverClient, reason) { expect(reason).to.be.equal('close'); expect(serverClient).not.to.be.equal(undefined); done(); }); client.on('error', done); client.on('connack', function() { client.stream.end(); }); client.connect(buildOpts()); }); it("should emit a ready and closed events", function(done) { var server = new mosca.Server(moscaSettings()); steed.series([ function(cb) { server.on("ready", cb); }, function(cb) { server.on("closed", cb); server.close(); } ], done); }); it("should support subscribing to # wildcard", function(done) { var d = donner(2, done); buildAndConnect(d, function(client1) { client1.on("publish", function(packet) { expect(packet.topic).to.be.equal("hello/world"); expect(packet.payload.toString()).to.be.equal("some data"); client1.disconnect(); }); client1.on("suback", function() { buildAndConnect(d, function(client2) { client2.publish({ topic: "hello/world", payload: "some data" }); client2.disconnect(); }); }); var subscriptions = [{ topic: "hello/#", qos: 0 } ]; client1.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should support subscribing to + wildcard", function(done) { var d = donner(2, done); buildAndConnect(d, function(client1) { client1.on("publish", function(packet) { expect(packet.topic).to.be.equal("hello/world"); expect(packet.payload.toString()).to.be.equal("some data"); client1.disconnect(); }); client1.on("suback", function() { buildAndConnect(d, function(client2) { client2.publish({ topic: "hello/world", payload: "some data" }); client2.disconnect(); }); }); var subscriptions = [{ topic: "hello/+", qos: 0 } ]; client1.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should support subscribing to topics with multiple wildcards", function(done) { var d = donner(2, done); buildAndConnect(d, function(client1) { client1.on("publish", function(packet) { expect(packet.topic).to.be.equal("hello/foo/world/bar"); expect(packet.payload.toString()).to.be.equal("some data"); client1.disconnect(); }); client1.on("suback", function() { buildAndConnect(d, function(client2) { client2.publish({ topic: "hello/foo/world/bar", payload: "some data" }); client2.disconnect(); }); }); var subscriptions = [{ topic: "hello/#/world/#", qos: 0 } ]; client1.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should support unsubscribing a single client", function(done) { var d = donner(3, done); steed.waterfall([ function(cb) { buildAndConnect(d, function(client1) { cb(null, client1); }); }, function(client1, cb) { var called = false; client1.on("publish", function(packet) { // we are expecting this client1.disconnect(); }); var subscriptions = [{ topic: "hello/#", qos: 0 } ]; client1.subscribe({ subscriptions: subscriptions, messageId: 42 }); client1.on("suback", function() { cb(null, client1); }); }, function(client1, cb) { buildAndConnect(d, function(client3) { cb(null, client1, client3); }); }, function(client1, client3, cb) { var subscriptions = [{ topic: "hello/#", qos: 0 } ]; client3.subscribe({ subscriptions: subscriptions, messageId: 42 }); client3.on("suback", function() { client3.disconnect(); cb(null); }); }, function(cb) { buildAndConnect(d, function(client2) { cb(null, client2); }); }, function(client2, cb) { client2.publish({ topic: "hello/world", payload: "some data" }); client2.disconnect(); } ]); }); it("should support send a puback when publishing QoS 1 messages", function(done) { buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); client.on("puback", function(packet) { expect(packet).to.have.property("messageId", messageId); client.disconnect(); }); client.publish({ topic: "hello", qos: 1, messageId: messageId }); }); }); it("should support subscribing to QoS 1", function(done) { buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [{ topic: "hello", qos: 1 } ]; client.on("suback", function(packet) { expect(packet.granted).to.be.deep.equal([1]); client.disconnect(); }); client.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should receive all messages at QoS 0 if a subscription is done with QoS 0", function(done) { buildAndConnect(done, function(client) { client.once("publish", function(packet) { expect(packet.qos).to.be.equal(0); client.disconnect(); }); client.on("suback", function(packet) { client.publish({ topic: "hello", qos: 1, messageId: 24 }); }); var subscriptions = [{ topic: "hello", qos: 0 } ]; client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); function maxInflightMessageTest(max, done) { buildAndConnect(done, function (client) { var counter = max + 1; function doPublish() { if (counter-- === 0) { return; } client.publish({ topic: "hello/foo", qos: 1, messageId: counter }); setImmediate(doPublish); } // we are not replaying with any pubacks client.on("suback", function(packet) { doPublish(); }); var subscriptions = [{ topic: "hello/#", qos: 1 }]; client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); } it("should disconnect a client if it has more thant 1024 inflight messages", function (done) { maxInflightMessageTest(1024, done); }); it("should have the max inflight message limit configurable", function (done) { var that = this; instance.close(function() { settings.maxInflightMessages = 512; that.instance = new mosca.Server(settings, function() { maxInflightMessageTest(512, done); }); }); }); it("QoS 1 wildcard subscriptions should receive QoS 1 messages at QoS 1", function (done) { buildAndConnect(done, function (client) { client.on("publish", function(packet) { expect(packet.qos).to.be.equal(1); client.disconnect(); }); client.on("suback", function(packet) { client.publish({ topic: "hello/foo", qos: 1, messageId: 24 }); }); var subscriptions = [{ topic: "hello/#", qos: 1 }]; client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should support will message", function(done) { steed.waterfall([ function(cb) { var client = createConnection(settings.port, settings.host); client.on("connected", function() { var opts = buildOpts(); opts.clientId = 'client1'; opts.will = { topic: "hello/died", payload: "client1 died", qos: 1 }; client.connect(opts); client.on('connack', function(packet) { cb(null, client); }); }); }, function(client1, cb) { var subscriptions = [{ topic: "hello/died", qos: 0 } ]; client1.subscribe({ subscriptions: subscriptions, messageId: 42 }); client1.on("suback", function() { cb(null, client1); }); }, function(client1, cb) { buildAndConnect(done, function(client3) { cb(null, client1, client3); }); }, function(client1, client3, cb) { var subscriptions = [{ topic: "hello/died", qos: 0 } ]; client3.subscribe({ subscriptions: subscriptions, messageId: 42 }); client3.on("suback", function() { client1.stream.end(); cb(null); }); client3.on("publish", function(packet) { expect(packet.topic).to.be.eql("hello/died"); expect(packet.payload.toString()).to.be.eql("client1 died"); client3.disconnect(); }); } ]); }); it("should support authentication (success)", function(done) { instance.authenticate = function(client, username, password, callback) { expect(username.toString()).to.be.eql("matteo"); expect(password.toString()).to.be.eql("collina"); callback(null, true); }; buildClient(done, function(client) { var options = buildOpts(); options.username = "matteo"; options.password = "collina"; client.connect(options); client.on('connack', function(packet) { expect(packet.returnCode).to.eql(0); client.disconnect(); }); }); }); it("should support authentication (failure)", function(done) { instance.authenticate = function(client, username, password, callback) { expect(username.toString()).to.be.eql("matteo"); expect(password.toString()).to.be.eql("collina"); callback(null, false); }; buildClient(done, function(client) { var options = buildOpts(); options.username = "matteo"; options.password = "collina"; client.connect(options); client.on('connack', function(packet) { expect(packet.returnCode).to.eql(5); }); }); }); it("should support authentication (error)", function(done) { instance.authenticate = function(client, username, password, callback) { callback(new Error("auth error")); }; buildClient(done, function(client) { var options = buildOpts(); options.username = "matteo"; options.password = "collina"; client.connect(options); client.on('connack', function(packet) { expect(packet.returnCode).to.eql(4); }); }); }); it("should support publish authorization (success)", function(done) { instance.authorizePublish = function(client, topic, payload, callback) { expect(topic).to.be.eql("hello"); expect(payload.toString()).to.be.eql("world"); callback(null, true); }; buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); client.on("puback", function(packet) { expect(packet).to.have.property("messageId", messageId); client.disconnect(); }); client.publish({ topic: "hello", qos: 1, payload: "world", messageId: messageId }); }); }); it("should support publish authorization (failure)", function(done) { instance.authorizePublish = function(client, topic, payload, callback) { expect(topic).to.be.eql("hello"); expect(payload.toString()).to.be.eql("world"); callback(null, false); }; buildAndConnect(done, function(client) { // it exists no negation of auth, it just disconnect the client client.publish({ topic: "hello", payload: "world", qos: 1, messageId: 42 }); }); }); it("should support overriding the payload during authorization", function(done) { instance.authorizePublish = function(client, topic, payload, callback) { callback(null, new Buffer("rewritten")); }; instance.on("published", function(packet) { expect(packet.payload.toString().toString()).to.be.equal("rewritten"); }); buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); client.on("puback", function(packet) { expect(packet).to.have.property("messageId", messageId); client.disconnect(); }); client.publish({ topic: "hello", qos: 1, payload: "world", messageId: messageId }); }); }); it("should share the authenticated client during the publish authorization", function(done) { instance.authenticate = function(client, username, password, callback) { client.shared = 'message'; callback(null, true); }; instance.authorizePublish = function(client, topic, payload, callback) { expect(client).to.have.property("shared", "message"); callback(null, true); }; buildAndConnect(done, function(client) { var messageId = Math.floor(65535 * Math.random()); client.on("puback", function(packet) { client.disconnect(); }); client.publish({ topic: "hello", qos: 1, payload: "world", messageId: messageId }); }); }); it("should support will authorization (success)", function(done) { instance.authorizePublish = function(client, topic, payload, callback) { expect(topic).to.be.eql("hello"); expect(payload.toString()).to.be.eql("world"); callback(null, true); }; var opts = buildOpts(); opts.will = { topic: "hello", payload: "world" }; buildAndConnect(function() {}, opts, function(client) { client.stream.end(); }); instance.on('published', function(packet) { expect(packet.topic).to.be.eql("hello"); expect(packet.payload.toString().toString()).to.be.eql("world"); done(); }); }); it("should support will authorization (failure)", function(done) { instance.authorizePublish = function(client, topic, payload, callback) { expect(topic).to.be.eql("hello"); expect(payload.toString()).to.be.eql("world"); callback(null, false); done(); }; var opts = buildOpts(); opts.will = { topic: "hello", payload: "world" }; buildAndConnect(function() {}, opts, function(client) { client.stream.end(); }); }); it("should support subscribe authorization (success)", function(done) { instance.authorizeSubscribe = function(client, topic, callback) { expect(topic).to.be.eql("hello"); callback(null, true); }; buildAndConnect(done, function(client) { client.on("suback", function(packet) { client.disconnect(); }); var subscriptions = [{ topic: "hello", qos: 0 } ]; client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should support subscribe authorization (failure)", function(done) { var d = donner(2, done); instance.authorizeSubscribe = function(client, topic, callback) { expect(topic).to.be.eql("hello"); callback(null, false); }; buildAndConnect(d, function(client) { var subscriptions = [{ topic: "hello", qos: 0 } ]; client.on("suback", function(packet) { expect(packet.granted).to.be.eql([0x80]); client.disconnect(); d(); }); client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should share the authenticated client during the subscribe authorization", function(done) { instance.authenticate = function(client, username, password, callback) { client.shared = "message"; callback(null, true); }; instance.authorizeSubscribe = function(client, topic, callback) { expect(client).to.have.property("shared", "message"); callback(null, true); }; buildAndConnect(done, function(client) { client.on("suback", function(packet) { client.disconnect(); }); var subscriptions = [{ topic: "hello", qos: 0 } ]; client.subscribe({ subscriptions: subscriptions, messageId: 42 }); }); }); it("should not forward packet if authorizeForward do not call the callback", function(done) { var d = donner(2, done); var that = this; this.instance.authorizeForward = function(client, packet, callback) { callback(null, packet.topic !== 'stop_forward'); }; buildAndConnect(d, buildOpts(), function(client1) { var messageId = Math.floor(65535 * Math.random()); var subscriptions = [ { topic : "stop_forward", qos : 1 }, { topic : "go_forward", qos : 1 } ]; client1.on("publish", function(packet) { expect(packet.topic).to.equal("go_forward"); }); client1.on("suback", function() { buildAndConnect(d, buildOpts(), function(client2) { client2.on("puback", function(packet) { client1.disconnect(); client2.disconnect(); }); client2.publish({ topic: "stop_forward", messageId: messageId, qos: 1 }); client2.publish({ topic: "go_forward", messageId: messageId, qos: 1 }); }); }); client1.subscribe({ subscriptions: subscriptions, messageId: messageId }); }); }); it("should support retained messages", function(done) { steed.waterfall([ function(cb) { var client = createConnection(settings.port, settings.host); client.on("connected", function() { var opts = buildOpts(); client.connect(opts); client.on('connack', function(packet) { client.publish({ topic: "hello", qos: 1, payload: new Buffer("world world"), messageId: 42, retain: true }); }); client.on('puback', function() { client.stream.end(); cb(); }); }); }, function(cb) { var client = createConnection(settings.port, settings.host); client.on("connected", function() { var opts = buildOpts(); client.connect(opts); client.on('connack', function(packet) { var subscriptions = [{ topic: "hello", qos: 0 }]; client.subscribe({ subscriptions: subscriptions, messageId: 29 }); }); client.on("publish", function(packet) { expect(packet.topic).to.be.eql("hello"); expect(packet.payload.toString().toString()).to.be.eql("world world"); client.stream.end(); }); client.stream.on('end', cb); }); } ], function() { setImmediate(done); }); }); it("should return only a single retained message", function(done) { steed.waterfall([ function(cb) { buildClient(cb, function(client) { client.name = "Phase 1"; var defaultMessage = { topic: "hello", qos: 1, payload: null, messageId: null,