You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2556 lines
63 KiB

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,
retain: true
};
var opts = buildOpts();
opts.clean = true;
var totalMessages = 3;
var publishCount = 0;
client.connect(opts);
client.on('puback', function(packet){
publishCount++;
if(publishCount == totalMessages) {
client.stream.end();
}
});
client.on('connack', function(packet) {
for(var c = 1; c <= totalMessages; c++) {
defaultMessage.payload = (c == totalMessages) ? new Buffer("Final Message") : new Buffer("Message " + c);
defaultMessage.messageId = 40 + c;
client.publish(defaultMessage);
}
});
});
},
function(cb) {
setTimeout(cb, 100);
},
function(cb) {
buildClient(cb, function(client) {
var retainedReceivedCount = 0;
var opts = buildOpts();
opts.clean = true;
client.connect(opts);
client.on("connack", function(packet) {
var subscriptions = [{
topic: "hello",
qos: 0
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 20
});
});
var handleTimeout = function() {
expect(retainedReceivedCount).to.be.equal(1);
client.stream.end();
};
var timeout;
client.on("publish", function(packet) {
clearTimeout(timeout);
timeout = setTimeout(handleTimeout, 100);
retainedReceivedCount++;
});
});
}
], done);
});
it("should restore subscriptions for uncleaned clients", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-clients-test";
opts.clean = false;
steed.series([
function(cb) {
buildAndConnect(cb, opts, function(client, connack) {
// sessionPresent must be false
expect(connack.sessionPresent).to.be.eql(false);
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
client.stream.end();
});
});
},
function(cb) {
buildAndConnect(cb, opts, function(client, connack) {
// reconnection sessionPresent must be true
expect(connack.sessionPresent).to.be.eql(true);
client.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 42
});
client.on("publish", function(packet) {
expect(packet.topic).to.be.eql("hello");
expect(packet.payload.toString()).to.be.eql("world");
expect(packet.qos).to.be.eql(1);
client.disconnect();
});
});
}
], done);
});
it("should restore subscriptions for uncleaned clients (bis)", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-client-test";
opts.clean = false;
steed.series([
function(cb) {
buildAndConnect(cb, opts, function(client, connack) {
// sessionPresent must be false
expect(connack.sessionPresent).to.be.eql(false);
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
client.stream.end();
});
});
},
function(cb) {
buildAndConnect(cb, buildOpts(), function(client, connack) {
// buildOpts create new id, so is new session, sessionPresent must be false
expect(connack.sessionPresent).to.be.eql(false);
client.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 24
});
client.on("puback", function() {
client.disconnect();
});
});
},
function(cb) {
buildAndConnect(cb, opts, function(client, connack) {
// reconnection sessionPresent must be true
expect(connack.sessionPresent).to.be.eql(true);
client.on("publish", function(packet) {
expect(packet.topic).to.be.eql("hello");
expect(packet.payload.toString()).to.be.eql("world");
client.disconnect();
});
});
}
], done);
});
it("cleanSession = false, on reconnect cleanSession = true", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-client-test";
steed.series([
function(cb) {
opts.clean = false;
buildAndConnect(cb, opts, function(client, connack) {
// sessionPresent must be false
expect(connack.sessionPresent).to.be.eql(false);
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
client.stream.end();
});
});
},
function(cb) {
buildAndConnect(cb, buildOpts(), function(client, connack) {
// buildOpts create new id, so is new session, sessionPresent must be false
expect(connack.sessionPresent).to.be.eql(false);
client.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 24
});
client.on("puback", function() {
client.disconnect();
});
});
},
function(cb) {
opts.clean = true;
buildAndConnect(cb, opts, function(client, connack) {
// reconnection sessionPresent must be false, it is clean
expect(connack.sessionPresent).to.be.eql(false);
client.on("publish", function(packet) {
cb(new Error("unexpected publish"));
});
setTimeout(function(){
client.disconnect();
}, 1000);
});
}
], done);
});
it("should remove already pubacked messages from the offline store", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-clients-test";
opts.clean = false;
opts.keepalive = 0;
function step1(cb) {
buildAndConnect(function() {}, opts, function(client) {
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
cb(null, client);
});
});
}
function step2(subscriber, cb) {
buildAndConnect(function() {}, buildOpts(), function(client) {
cb(null, subscriber, client);
});
}
function step3(subscriber, publisher, cb) {
publisher.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 42
});
publisher.on("puback", function(packet) {
publisher.disconnect();
});
subscriber.on("publish", function(packet) {
subscriber.puback({ messageId: packet.messageId });
subscriber.disconnect();
cb();
});
}
steed.waterfall([
step1, step2, step3,
// two times!
step1, step2, step3
], function(err) {
expect(err).to.be.falsy;
buildClient(done, function(client) {
client.connect(opts);
client.on("publish", function(packet) {
done(new Error("not expected"));
});
setTimeout(function() {
client.disconnect();
}, 100);
});
});
});
it("should support offline messaging", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-clients-test2";
opts.clean = false;
opts.keepalive = 0;
steed.series([
function(cb) {
buildAndConnect(cb, opts, function(client) {
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
client.disconnect();
});
});
},
function(cb) {
buildClient(cb, function(client) {
client.connect(buildOpts());
client.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 42
});
client.on("puback", function(packet) {
client.disconnect();
});
});
},
function(cb) {
buildAndConnect(cb, opts, function(client) {
client.on("publish", function(packet) {
client.puback({ messageId: packet.messageId });
client.disconnect();
expect(packet.topic).to.eql("hello");
expect(packet.payload.toString()).to.eql("world");
expect(packet.qos).to.eql(1);
});
});
}
], done);
});
it("should not deliver all offline messages more than once", function(done) {
var opts = buildOpts();
opts.clientId = "mosca-unclean-clients-test3";
opts.clean = false;
opts.keepalive = 0;
steed.series([
function(cb) {
buildAndConnect(cb, opts, function(client) {
var subscriptions = [{
topic: "hello",
qos: 1
}];
client.subscribe({
subscriptions: subscriptions,
messageId: 42
});
client.on("suback", function() {
client.disconnect();
});
});
},
function(cb) {
buildClient(cb, function(client) {
client.connect(buildOpts());
client.publish({
topic: "hello",
qos: 1,
payload: "world",
messageId: 42
});
client.on("puback", function(packet) {
client.disconnect();
});
});
},
function(cb) {
buildAndConnect(cb, opts, function(client) {
client.on("publish", function(packet) {
client.puback({ messageId: packet.messageId });
client.disconnect();
expect(packet.topic).to.eql("hello");
expect(packet.payload.toString()).to.eql("world");
expect(packet.qos).to.eql(1);
});
});
},
function(cb) {
setTimeout(cb, 100);
},
function(cb) {
buildAndConnect(cb, opts, function(client) {
client.on("publish", function(packet) {
cb(new Error("unexpected publish"));
});
setTimeout(function() {
client.disconnect();
}, 50);
});
}
], done);
});
describe("pattern matching", function() {
var buildTest = function(subscribed, published, expected) {
var not = "";
if (expected === undefined) {
expected = true;
}
if (!expected) {
not = "not ";
}
if (!(subscribed instanceof Array)) {
subscribed = [subscribed];
}
it("should " + not + "support forwarding to " + subscribed + " when publishing " + published, function(done) {
var d = donner(2, done);
buildAndConnect(d, function(client1) {
var messageId = Math.floor(65535 * Math.random());
var subscriptions = subscribed.map(function(topic) {
return {
topic: topic,
qos: 0
};
});
client1.on("publish", function(packet) {
client1.disconnect();
if (!expected) {
throw new Error("the message was not expected");
}
});
client1.on("suback", function() {
buildAndConnect(d, function(client2) {
client2.publish({
topic: published,
payload: "some data",
messageId: messageId
});
client2.disconnect();
});
});
client1.subscribe({
subscriptions: subscriptions,
messageId: messageId
});
if (!expected) {
setTimeout(function() {
client1.disconnect();
}, 50);
}
});
});
};
buildTest("#", "test/topic");
buildTest("#", "/test/topic");
buildTest("foo/#", "foo/bar/baz");
buildTest("foo/+/baz", "foo/bar/baz");
buildTest("foo/#", "foo");
buildTest("/#", "/foo");
buildTest("test/topic/", "test/topic", false);
buildTest("+/+/+/+/+/+/+/+/+/+/test", "one/two/three/four/five/six/seven/eight/nine/ten/test");
buildTest("/test/topic", "test/topic", false);
buildTest("/test//topic", "/test/topic", false);
buildTest("/test//topic", "/test//topic");
buildTest("/test/+/topic", "/test//topic", false);
buildTest("/test/#/topic", "/test//topic");
buildTest("#", "$SYS/hello", false);
buildTest("/#", "$SYS/hello", false);
buildTest("/+/hello", "$SYS/hello", false);
buildTest("$SYS/hello", "$SYS/hello");
buildTest("$SYS/hello", "$SYS/hello");
buildTest(["#", "$SYS/#"], "$SYS/hello");
});
it("should allow plugin authors to publish", function(done) {
buildAndConnect(done, function(client) {
var messageId = Math.floor(65535 * Math.random());
var subscriptions = [{
topic: "hello",
qos: 1
}
];
client.on("suback", function(packet) {
instance.publish({ topic: "hello", payload: "world", qos: 1 });
});
client.on("publish", function(packet) {
expect(packet).to.have.property("topic", "hello");
expect(packet.payload.toString()).to.equal("world");
expect(packet).to.have.property("qos", 1);
client.disconnect();
});
client.subscribe({
subscriptions: subscriptions,
messageId: messageId
});
});
});
it("should have an id", function() {
expect(instance.id).to.be.truthy;
});
it("should have a configurable id", function(done) {
var newSettings = moscaSettings();
newSettings.id = "4242";
secondInstance = new mosca.Server(newSettings, done);
expect(secondInstance.id).to.eql("4242");
});
};