__tests__/lib/pubsub.test.js
// Dependencies
const assert = require('assert');
const { Hub, HubClient } = require('../../index');
const WebSocket = require('ws');
const { v4: uuidv4 } = require('uuid');
const { delay, delayUntil } = require('../../helpers/delay');
const MemoryDataStore = require('../../lib/dataStores/memory');
const httpShutdown = require('http-shutdown');
describe('pubsub', () => {
let hub;
let server;
before(async () => {
hub = new Hub({ port: 5050 });
server = httpShutdown(hub.server);
server.listen(5050);
});
after((done) => {
server.shutdown(done);
});
describe('#subscribe', () => {
describe('when passed a clientId and a channel', () => {
it('should add a client to a channel', async () => {
const data = { channel: 'sport' };
const socket = {
clientId: 'xxxx',
};
const secondWs = {
clientId: 'wwww',
};
const secondData = { channel: 'business' };
const response = await hub.pubsub.subscribe({ data, socket });
assert(response.success);
assert.strictEqual(
response.message,
'Client "xxxx" subscribed to channel "sport"'
);
assert.deepStrictEqual(hub.pubsub.dataStore.channels.sport, [
'xxxx',
]);
assert.deepStrictEqual(hub.pubsub.dataStore.clients.xxxx, [
'sport',
]);
await hub.pubsub.subscribe({ data, socket: secondWs });
await hub.pubsub.subscribe({ data: secondData, socket });
assert.deepStrictEqual(hub.pubsub.dataStore.channels.sport, [
'xxxx',
'wwww',
]);
assert.deepStrictEqual(hub.pubsub.dataStore.clients.wwww, [
'sport',
]);
assert.deepStrictEqual(hub.pubsub.dataStore.channels.business, [
'xxxx',
]);
assert.deepStrictEqual(hub.pubsub.dataStore.clients.xxxx, [
'sport',
'business',
]);
});
});
describe('when the websocket does not have a client id', () => {
it('should throw an error indicating that the websocket does not have an id', async () => {
const data = { channel: 'weather' };
const socket = {};
assert.rejects(
async () => {
await hub.pubsub.subscribe({ data, socket });
},
{ message: 'No client id was found on the WebSocket' }
);
});
});
describe('when the channel is not passed', () => {
it('should throw an error indicating that the channel was not passed', async () => {
const data = {};
const socket = {
clientId: 'yyyy',
};
assert.rejects(
async () => {
await hub.pubsub.subscribe({ data, socket });
},
{ message: 'No channel was passed in the data' }
);
});
});
describe('when the channel has been added by the server with options', () => {
it('should run a check against the authenticate function set for that channel', async () => {
const channel = 'fish';
let called = false;
const authenticate = ({ data, socket }) => {
assert.strictEqual(data.password, 'food');
assert.strictEqual(socket.clientId, 'ooo');
called = true;
return called;
};
hub.pubsub.addChannelConfiguration({ channel, authenticate });
const data = {
channel,
password: 'food',
};
const socket = {
clientId: 'ooo',
};
await hub.pubsub.subscribe({ data, socket });
assert(called);
const channels = await hub.pubsub.dataStore.getChannelsForClientId(
socket.clientId
);
assert(channels.indexOf('fish') !== -1);
});
});
describe('when a client makes multiple attempts to subscribe to the same channel', () => {
it('should only record a single entry of the client id in the channel subscriptions, and vice versa', async () => {
const data = { channel: 'entertainment' };
const socket = {
clientId: 'zzzz',
};
const firstResponse = await hub.pubsub.subscribe({
data,
socket,
});
const secondResponse = await hub.pubsub.subscribe({
data,
socket,
});
assert(firstResponse.success);
assert(secondResponse.success);
const message =
'Client "zzzz" subscribed to channel "entertainment"';
assert.strictEqual(firstResponse.message, message);
assert.strictEqual(secondResponse.message, message);
assert.deepStrictEqual(
hub.pubsub.dataStore.channels.entertainment,
['zzzz']
);
assert.deepStrictEqual(hub.pubsub.dataStore.clients.zzzz, [
'entertainment',
]);
});
});
});
describe('#publish', () => {
it('should allow the client to publish a message to all of the channel subscribers, including themselves', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('politics');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "politics"`
);
// Get the client to publish a message to the channel
await hubClient.publish('politics', 'Elections held');
// Check that the client receives the messages
const thePreviousLatestMessage = messages[messages.length - 2];
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(thePreviousLatestMessage.action, 'message');
assert.strictEqual(
thePreviousLatestMessage.data.channel,
'politics'
);
assert.strictEqual(
thePreviousLatestMessage.data.message,
'Elections held'
);
assert.strictEqual(theNextLatestMessage.action, 'publish');
assert.strictEqual(theNextLatestMessage.data.success, true);
hubClient.sarus.disconnect();
});
it('should allow the client to publish a message to all of the channel subscribers, excluding themselves', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('showbiz');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "showbiz"`
);
// Get the client to publish a message to the channel
await hubClient.publish(
'showbiz',
'Oscars ceremony to be virtual',
true
);
// Check that the client does not receive the message
const thePreviousLatestMessage = messages[messages.length - 2];
const theNextLatestMessage = messages[messages.length - 1];
assert.notStrictEqual(thePreviousLatestMessage.action, 'message');
assert.strictEqual(thePreviousLatestMessage.action, 'subscribe');
assert.strictEqual(theNextLatestMessage.action, 'publish');
assert.strictEqual(theNextLatestMessage.data.success, true);
assert.strictEqual(
theNextLatestMessage.data.message,
'Published message'
);
hubClient.sarus.disconnect();
});
it('should allow the server to publish a message to all of the channel subscribers', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('markets');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "markets"`
);
// Get the server to publish a message to the channel
await hub.pubsub.publish({
data: { channel: 'markets', message: 'FTSE: 5845 (-5)' },
});
await delay(25);
// Check that the client receives the message
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.action, 'message');
assert.strictEqual(theNextLatestMessage.data.channel, 'markets');
assert.strictEqual(
theNextLatestMessage.data.message,
'FTSE: 5845 (-5)'
);
hubClient.sarus.disconnect();
});
describe('when publishing from a client', () => {
it('should return an error response if the websocket client id is not present', async () => {
const messages = [];
const client = new WebSocket('ws://localhost:5050');
client.on('message', (event) => {
const message = JSON.parse(event);
messages.push(message);
});
const publishRequest = {
id: uuidv4(),
action: 'publish',
type: 'request',
data: {
channel: 'sport',
message: 'Something is happening',
},
};
await delayUntil(() => client.readyState === 1);
client.send(JSON.stringify(publishRequest));
await delay(50);
const latestMessage = messages[messages.length - 1];
assert.strictEqual(latestMessage.type, 'error');
assert.strictEqual(
latestMessage.error,
'No client id was found on the WebSocket'
);
client.close();
});
it('should return an error response if the channel is missing', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('showbiz');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "showbiz"`
);
// Get the client to publish a message to the channel
await hubClient.publish(null, 'Oscars ceremony to be virtual');
// Check that the client receives the message
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.type, 'error');
assert.strictEqual(
theNextLatestMessage.error,
'No channel was passed in the data'
);
hubClient.sarus.disconnect();
});
it('should return an error response if the message is missing', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('showbiz');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "showbiz"`
);
// Get the client to publish a message to the channel
await hubClient.publish('showbiz', null);
// Check that the client receives the message
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.type, 'error');
assert.strictEqual(
theNextLatestMessage.error,
'No message was passed in the data'
);
hubClient.sarus.disconnect();
});
it('should not allow a client to publish to a channel that they are not subscribed to', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
// Get the client to publish a message to the channel
await hubClient.publish('dashboard_y', 'Some data');
// Check that the client receives the message
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.type, 'error');
assert.strictEqual(
theNextLatestMessage.error,
'You must subscribe to the channel to publish messages to it'
);
hubClient.sarus.disconnect();
});
});
describe('when publishing from a server', () => {
it('should return an error response if the channel is missing', async () => {
assert.rejects(
async () => {
await hub.pubsub.publish({
data: {
message: 'FTSE: 5845 (-5)',
},
});
},
{ message: 'No channel was passed in the data' }
);
});
it('should return an error response if the message is missing', async () => {
assert.rejects(
async () => {
await hub.pubsub.publish({
data: {
channel: 'markets',
},
});
},
{ message: 'No message was passed in the data' }
);
});
describe('publishing to a channel that has no subscribers', () => {
it('should publish the message, even if there are no subscribers for that channel', async () => {
await hub.pubsub.publish({
data: {
channel: 'dashboard_x',
message: 'FTSE: 5845 (-5)',
},
});
});
});
});
describe('when using the redis dataStore', () => {
let firstHub;
let secondHub;
let firstHubClient;
let secondHubClient;
before(async () => {
firstHub = new Hub({ port: 4006, dataStoreType: 'redis' });
secondHub = new Hub({ port: 4007, dataStoreType: 'redis' });
firstHub.listen();
secondHub.listen();
firstHubClient = new HubClient({ url: 'ws://localhost:4006' });
secondHubClient = new HubClient({ url: 'ws://localhost:4007' });
await delayUntil(
() =>
firstHubClient.getClientId() !== null &&
secondHubClient.getClientId() !== null
);
});
after(async () => {
firstHubClient.sarus.disconnect();
secondHubClient.sarus.disconnect();
firstHub.server.close();
secondHub.server.close();
await delay(100);
await firstHub.pubsub.dataStore.redis.quit();
await secondHub.pubsub.dataStore.redis.quit();
await firstHub.pubsub.dataStore.internalRedis.quit();
await secondHub.pubsub.dataStore.internalRedis.quit();
});
it('should relay the published message to all Hub server instances via Redis', async () => {
let firstClientMessage;
let firstClientReceivesMessage = false;
let secondClientMessage;
let secondClientReceivesMessage = false;
const firstClientHandlerFunction = (message) => {
firstClientMessage = message;
firstClientReceivesMessage = true;
};
firstHubClient.addChannelMessageHandler(
'news',
firstClientHandlerFunction
);
const secondClientHandlerFunction = (message) => {
secondClientMessage = message;
secondClientReceivesMessage = true;
};
secondHubClient.addChannelMessageHandler(
'news',
secondClientHandlerFunction
);
await firstHubClient.subscribe('news');
await secondHubClient.subscribe('news');
const message = 'Sunny weather on the way';
await firstHub.pubsub.publish({
data: {
channel: 'news',
message,
},
});
await delayUntil(
() =>
firstClientReceivesMessage &&
secondClientReceivesMessage
);
assert.strictEqual(firstClientMessage, message);
assert.strictEqual(secondClientMessage, message);
});
});
});
describe('#unsubscribe', () => {
it('should remove a client from a channel, and ensure that the client no longer receives messages for that channel', async () => {
const messages = [];
const config = { url: 'ws://localhost:5050' };
const hubClient = new HubClient(config);
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('markets');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "markets"`
);
// Subscribe the client to the channel
await hubClient.unsubscribe('markets');
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.data.success, true);
assert.strictEqual(
theNextLatestMessage.data.message,
`Client "${clientId}" unsubscribed from channel "markets"`
);
// Get the server to publish a message to the channel
// a second client needs to be subscribed, and localstorage scrubbed to prevent duplicate client id assignment;
global.localStorage.removeItem('sarus-client-id');
const otherHubClient = new HubClient(config);
await otherHubClient.isReady();
await otherHubClient.subscribe('markets');
await hub.pubsub.publish({
data: {
channel: 'markets',
message: 'FTSE: 5845 (-5)',
},
});
await delay(25);
// Check that the client does not receive the message
const theFinalLatestMessage = messages[messages.length - 1];
assert.notStrictEqual(theFinalLatestMessage.action, 'message');
hubClient.sarus.disconnect();
otherHubClient.sarus.disconnect();
});
it('should return an error response if the websocket client id is not present', async () => {
const messages = [];
const client = new WebSocket('ws://localhost:5050');
client.on('message', (event) => {
const message = JSON.parse(event);
messages.push(message);
});
const unsubscribeRequest = {
id: uuidv4(),
action: 'unsubscribe',
type: 'request',
data: {
channel: 'sport',
},
};
await delayUntil(() => client.readyState === 1);
client.send(JSON.stringify(unsubscribeRequest));
await delay(50);
const latestMessage = messages[messages.length - 1];
assert.strictEqual(latestMessage.type, 'error');
assert.strictEqual(
latestMessage.error,
'No client id was found on the WebSocket'
);
client.close();
});
it('should return an error response if the channel is missing', async () => {
const messages = [];
const hubClient = new HubClient({ url: 'ws://localhost:5050' });
hubClient.sarus.on('message', (event) => {
const message = JSON.parse(event.data);
messages.push(message);
});
await hubClient.isReady();
// Subscribe the client to the channel
await hubClient.subscribe('markets');
// Acknowledge the channel subscription
const clientId = global.localStorage.getItem('sarus-client-id');
const latestMessage = messages[messages.length - 1];
if (!latestMessage) throw new Error('No messages intercepted');
assert.strictEqual(latestMessage.data.success, true);
assert.strictEqual(
latestMessage.data.message,
`Client "${clientId}" subscribed to channel "markets"`
);
// Unsubscribe the client from the channel
await hubClient.unsubscribe(null);
const theNextLatestMessage = messages[messages.length - 1];
assert.strictEqual(theNextLatestMessage.type, 'error');
assert.strictEqual(
theNextLatestMessage.error,
'No channel was passed in the data'
);
hubClient.sarus.disconnect();
});
});
describe('dataStore types', () => {
it('should use the memory data store by default', () => {
assert(hub.pubsub.dataStore instanceof MemoryDataStore);
});
describe('when passed a dataStoreType parameter', () => {
describe('when the dataStore type exists', () => {
it('should create an instance of that dataStore type and bind it to the class', async () => {
const redisHub = new Hub({
port: 6000,
dataStoreType: 'redis',
});
assert(redisHub.pubsub.dataStore.redis);
assert.strictEqual(
redisHub.pubsub.dataStore.redis._eventsCount,
0
);
});
});
describe('when the dataStore type does not exist', () => {
it('should throw an error', async () => {
assert.throws(
() => {
new Hub({ port: 6000, dataStoreType: 'postgres' });
},
{
message:
'dataStoreType "postgres" is not a valid option',
}
);
});
});
});
});
describe('#unsubscribeClientFromAllChannels', () => {
it('should unsubscribe the client from all of its channels', async () => {
const newHub = await new Hub({ port: 5004 });
newHub.listen();
const hubClient = new HubClient({ url: 'ws://localhost:5004' });
await hubClient.isReady();
await hubClient.subscribe('shares');
await newHub.pubsub.unsubscribeClientFromAllChannels({
ws: { clientId: hubClient.getClientId() },
});
const channels = await newHub.pubsub.dataStore.getChannelsForClientId(
hubClient.getClientId()
);
const clientIds = await newHub.pubsub.dataStore.getClientIdsForChannel(
'shares'
);
assert.deepStrictEqual(channels, []);
assert.deepStrictEqual(clientIds, []);
hubClient.sarus.disconnect();
newHub.server.close();
});
});
describe('#addChannelConfiguration', () => {
describe('when passed an authenticate option', () => {
it('should add a channel with an authenticate function to call during subscription requests', async () => {
const channel = 'dogs';
let called = false;
const authenticate = ({ data, socket }) => {
assert.strictEqual(data.password, 'food');
assert.strictEqual(socket.clientId, 'woof');
called = true;
return called;
};
hub.pubsub.addChannelConfiguration({ channel, authenticate });
assert.deepStrictEqual(
hub.pubsub.channelConfigurations.dogs.authenticate,
authenticate
);
const data = {
channel,
password: 'food',
};
const socket = {
clientId: 'woof',
};
await hub.pubsub.subscribe({ data, socket });
assert(called);
const channels = await hub.pubsub.dataStore.getChannelsForClientId(
socket.clientId
);
assert(channels.indexOf('dogs') !== -1);
});
});
describe('when passed a wildcard channel name', () => {
it('should be able to run the channel configuration for channels that match the wildcard channel name', async () => {
// create a channel configuration for multiple dashboards
const wildcardChannel = 'dashboard_*';
let callCount = 0;
const authenticate = ({ data, socket }) => {
assert.strictEqual(data.password, 'opensesame');
assert.strictEqual(socket.clientId, 'viewer');
callCount++;
return true;
};
hub.pubsub.addChannelConfiguration({
channel: wildcardChannel,
authenticate,
});
const otherWildcardChannel = 'dash_*';
let otherCallCount = 0;
const otherAuthenticate = () => {
otherCallCount++;
return true;
};
hub.pubsub.addChannelConfiguration({
channel: otherWildcardChannel,
authenticate: otherAuthenticate,
});
assert.deepStrictEqual(
hub.pubsub.channelConfigurations[wildcardChannel]
.authenticate,
authenticate
);
const data = {
channel: 'dashboard_e220j92',
password: 'opensesame',
};
const socket = {
clientId: 'viewer',
};
await hub.pubsub.subscribe({ data, socket });
assert(callCount === 1);
const channels = await hub.pubsub.dataStore.getChannelsForClientId(
socket.clientId
);
assert(channels.indexOf('dashboard_e220j92') !== -1);
const dataTwo = {
channel: 'dashboard_29jd92j',
password: 'opensesame',
};
await hub.pubsub.subscribe({ data: dataTwo, socket });
assert(callCount === 2);
const channelsTwo = await hub.pubsub.dataStore.getChannelsForClientId(
socket.clientId
);
assert(channelsTwo.indexOf('dashboard_29jd92j') !== -1);
assert.strictEqual(otherCallCount, 0);
});
it('should prevent the developer from adding wildcard channels that might overlap in channel matches', async () => {
// First case - long then short wildcard overlap
const wildcardChannelOne = 'magazine_*';
const wildcardChannelTwo = 'mag*';
// Second case - short then long wildcard overlap
const wildcardChannelThree = 'des*';
const wildcardChannelFour = 'design*';
const normalChannelFive = 'de';
// Just an example
const authenticate = ({ data }) => {
return data.password === 'xyz';
};
hub.pubsub.addChannelConfiguration({
channel: wildcardChannelOne,
authenticate,
});
assert.deepStrictEqual(
hub.pubsub.channelConfigurations[wildcardChannelOne]
.authenticate,
authenticate
);
assert.throws(
() => {
hub.pubsub.addChannelConfiguration({
channel: wildcardChannelTwo,
authenticate,
});
},
{
message: `Wildcard channel name too ambiguous - will collide with "${wildcardChannelOne}"`,
}
);
hub.pubsub.addChannelConfiguration({
channel: wildcardChannelThree,
authenticate,
});
assert.deepStrictEqual(
hub.pubsub.channelConfigurations[wildcardChannelThree]
.authenticate,
authenticate
);
assert.throws(
() => {
hub.pubsub.addChannelConfiguration({
channel: wildcardChannelFour,
authenticate,
});
},
{
message: `Wildcard channel name too ambiguous - will collide with "${wildcardChannelThree}"`,
}
);
assert.throws(
() => {
hub.pubsub.addChannelConfiguration({
channel: normalChannelFive,
authenticate,
});
},
{
message: `Wildcard channel name too ambiguous - will collide with "${wildcardChannelThree}"`,
}
);
});
});
describe('when passed a clientCanPublish value', () => {
describe('and the value is true', () => {
it('should allow the client to publish to the channel', async () => {
const channelAllowed = 'birds';
let messageReceived;
hub.pubsub.addChannelConfiguration({
channel: channelAllowed,
clientCanPublish: true,
});
const hubClient = new HubClient({
url: 'ws://localhost:5050',
});
await hubClient.isReady();
await hubClient.subscribe(channelAllowed);
hubClient.addChannelMessageHandler(
channelAllowed,
(message) => {
messageReceived = message;
}
);
await hubClient.publish(channelAllowed, 'hello everyone');
assert.strictEqual(messageReceived, 'hello everyone');
});
});
describe('and the value is false', () => {
it('should not allow the client to publish to the channel', async () => {
let messageReceived;
const channelNotAllowed = 'crocadiles';
hub.pubsub.addChannelConfiguration({
channel: channelNotAllowed,
clientCanPublish: false,
});
const hubClient = new HubClient({
url: 'ws://localhost:5050',
});
await hubClient.isReady();
await hubClient.subscribe(channelNotAllowed);
hubClient.addChannelMessageHandler(
channelNotAllowed,
(message) => {
messageReceived = message;
}
);
const response = await hubClient.publish(
channelNotAllowed,
'hello everyone'
);
assert.strictEqual(
response,
'Clients cannot publish to the channel'
);
assert.notStrictEqual(messageReceived, 'hello everyone');
});
});
describe('and the value is a function', () => {
it('should be passed the data and the socket', async () => {
const channelAllowed = 'lizards';
let messageReceived;
let dataPassed;
let socketPassed;
const clientCanPublish = ({ data, socket }) => {
dataPassed = data;
socketPassed = socket;
return true;
};
hub.pubsub.addChannelConfiguration({
channel: channelAllowed,
clientCanPublish,
});
const hubClient = new HubClient({
url: 'ws://localhost:5050',
});
await hubClient.isReady();
await hubClient.subscribe(channelAllowed);
hubClient.addChannelMessageHandler(
channelAllowed,
(message) => {
messageReceived = message;
}
);
await hubClient.publish(channelAllowed, 'hello everyone');
assert.strictEqual(messageReceived, 'hello everyone');
assert.strictEqual(dataPassed.channel, channelAllowed);
assert.strictEqual(dataPassed.message, 'hello everyone');
assert.strictEqual(
socketPassed.clientId,
hubClient.getClientId()
);
});
});
});
});
describe('#removeChannelConfiguration', () => {
it('should remove the channel configuration', () => {
const channel = 'dogs';
assert(hub.pubsub.channelConfigurations[channel]);
hub.pubsub.removeChannelConfiguration(channel);
assert(!hub.pubsub.channelConfigurations[channel]);
});
});
});