@boringnode/transmit
Version:
A framework agnostic Server-Sent-Event library
325 lines (324 loc) • 12.1 kB
JavaScript
/*
* @boringnode/transmit
*
* @license MIT
* @copyright BoringNode
*/
import { randomUUID } from 'node:crypto';
import { test } from '@japa/runner';
import { Transmit } from '../src/transmit.js';
import { StreamManager } from '../src/stream_manager.js';
import { makeStream } from './fixtures/stream.js';
import { TransportMessageType } from '../src/transport_message_type.js';
import { makeTransmitWithTransport, makeTransport } from './fixtures/transmit.js';
test.group('Transmit', () => {
test('should return the manager instance', async ({ assert }) => {
const transmit = new Transmit({
transport: null,
});
assert.instanceOf(transmit.getManager(), StreamManager);
});
test('should call the authorization callback', async ({ assert }) => {
const transmit = new Transmit({
transport: null,
});
const uid = randomUUID();
let authorized = false;
transmit.authorize('channel1', () => {
authorized = true;
return true;
});
await transmit.subscribe({ channel: 'channel1', uid });
assert.isTrue(authorized);
});
test('should call the authorization callback with the context', async ({ assert }) => {
assert.plan(1);
const transmit = new Transmit({
transport: null,
});
const uid = randomUUID();
transmit.authorize('channel1', (context) => {
assert.equal(context, 'foo');
return true;
});
await transmit.subscribe({ channel: 'channel1', uid, context: 'foo' });
});
test('should authorize the subscription', async ({ assert }) => {
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
transmit.authorize('channel1', () => {
return true;
});
const authorized = await transmit.subscribe({ channel: 'channel1', uid: stream.getUid() });
assert.isTrue(authorized);
});
test('should not authorize the subscription', async ({ assert }) => {
const transmit = new Transmit({
transport: null,
});
const uid = randomUUID();
transmit.authorize('channel1', () => {
return false;
});
const authorized = await transmit.subscribe({ channel: 'channel1', uid });
assert.isFalse(authorized);
});
test('should authorize with channel params', async ({ assert }) => {
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
transmit.authorize('channel/:id', (_context, params) => {
return params.id === '1';
});
const authorized = await transmit.subscribe({ channel: 'channel/1', uid: stream.getUid() });
const refused = await transmit.subscribe({ channel: 'channel/2', uid: stream.getUid() });
assert.isTrue(authorized);
assert.isFalse(refused);
});
test('should emit an connect event', async ({ assert }, done) => {
assert.plan(2);
const transmit = new Transmit({
transport: null,
});
const uid = randomUUID();
let connected = false;
transmit.on('connect', (params) => {
connected = true;
assert.equal(params.uid, uid);
});
makeStream(transmit, uid);
setTimeout(() => {
assert.isTrue(connected);
done();
}, 0);
}).waitForDone();
test('should emit an subscribe event', async ({ assert }) => {
assert.plan(3);
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
let subscribed = false;
transmit.on('subscribe', (params) => {
subscribed = true;
assert.equal(params.uid, stream.getUid());
assert.equal(params.channel, 'users/1');
});
await transmit.subscribe({
uid: stream.getUid(),
channel: 'users/1',
});
assert.isTrue(subscribed);
});
test('should emit an unsubscribe event', async ({ assert }) => {
assert.plan(3);
const transmit = new Transmit({
transport: null,
});
const uid = randomUUID();
let unsubscribed = false;
transmit.on('unsubscribe', (params) => {
unsubscribed = true;
assert.equal(params.uid, uid);
assert.equal(params.channel, 'users/1');
});
await transmit.unsubscribe({
uid,
channel: 'users/1',
});
assert.isTrue(unsubscribed);
});
test('should emit a broadcast event when a message is broadcasted', async ({ assert }, done) => {
assert.plan(3);
const transmit = new Transmit({
transport: null,
});
const payload = { foo: 'bar' };
let broadcasted = false;
transmit.on('broadcast', (params) => {
broadcasted = true;
assert.equal(params.channel, 'users/1');
assert.equal(params.payload, payload);
});
transmit.broadcast('users/1', payload);
setTimeout(() => {
assert.isTrue(broadcasted);
done();
}, 0);
}).waitForDone();
test('should ping all subscribers', async ({ assert, cleanup }, done) => {
assert.plan(1);
const transmit = new Transmit({
transport: null,
pingInterval: 100,
});
cleanup(() => transmit.shutdown());
const stream = makeStream(transmit, randomUUID());
stream.on('data', (message) => {
//? Ignore the first message
if (message === '\n')
return;
assert.include(message, '$$transmit/ping');
done();
});
}).waitForDone();
test('should broadcast a message to all listening clients', async ({ assert }) => {
assert.plan(1);
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
const stream2 = makeStream(transmit);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
let dataReceived = false;
stream.on('data', (message) => {
//? Ignore the first message
if (message === '\n')
return;
dataReceived = true;
});
stream2.on('data', () => {
assert.fail('Should not receive the broadcasted message');
});
transmit.broadcast('channel1', { message: 'hello' });
assert.isTrue(dataReceived);
});
test('should broadcast a message to all listening clients except the sender', async ({ assert, }) => {
assert.plan(1);
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
const stream2 = makeStream(transmit);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
await transmit.subscribe({
uid: stream2.getUid(),
channel: 'channel1',
});
let dataReceived = false;
stream.on('data', (message) => {
//? Ignore the first message
if (message === '\n')
return;
dataReceived = true;
});
stream2.on('data', () => {
assert.fail('Should not receive the broadcasted message');
});
transmit.broadcastExcept('channel1', { message: 'hello' }, stream2.getUid());
assert.isTrue(dataReceived);
});
test('should not broadcast to ourself when sending to the bus', async ({ assert }) => {
const transport = makeTransport();
const transmit = makeTransmitWithTransport(transport);
const stream = makeStream(transmit);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
transmit.broadcast('channel1', { message: 'hello' });
assert.lengthOf(transport.transport.receivedMessages, 0);
});
test('should broadcast to the bus when sending a message', async ({ assert }) => {
const transport = makeTransport();
const transmit = makeTransmitWithTransport(transport);
makeTransmitWithTransport(transport);
const stream = makeStream(transmit);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
transmit.broadcast('channel1', { message: 'hello' });
assert.lengthOf(transport.transport.receivedMessages, 1);
assert.equal(transport.transport.receivedMessages[0].type, TransportMessageType.Broadcast);
});
test('second instance should receive the broadcasted message', async ({ assert }) => {
const transport = makeTransport();
const transmit = makeTransmitWithTransport(transport);
const transmit2 = makeTransmitWithTransport(transport);
const stream = makeStream(transmit);
const stream2 = makeStream(transmit2);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
await transmit2.subscribe({
uid: stream2.getUid(),
channel: 'channel1',
});
let dataReceived = false;
stream.on('data', () => {
dataReceived = true;
});
transmit.broadcast('channel1', { message: 'hello' });
assert.isTrue(dataReceived);
});
test('should broadcast falsy payloads without converting them to empty object', async ({ assert, }) => {
const transmit = new Transmit({
transport: null,
});
const stream = makeStream(transmit);
await transmit.subscribe({
uid: stream.getUid(),
channel: 'channel1',
});
const receivedPayloads = [];
stream.on('data', (message) => {
if (message === '\n')
return;
const parsed = JSON.parse(message.replace('data: ', ''));
receivedPayloads.push(parsed.payload);
});
transmit.broadcast('channel1', 0);
transmit.broadcast('channel1', false);
transmit.broadcast('channel1', '');
assert.deepEqual(receivedPayloads, [0, false, '']);
});
test('should return all subscribers for a channel', async ({ assert }) => {
const transport = makeTransport();
const transmit = makeTransmitWithTransport(transport);
const stream1 = makeStream(transmit);
const stream2 = makeStream(transmit);
const stream3 = makeStream(transmit);
const stream4 = makeStream(transmit);
const stream5 = makeStream(transmit);
await transmit.subscribe({
uid: stream1.getUid(),
channel: 'channel1',
});
await transmit.subscribe({
uid: stream2.getUid(),
channel: 'channel1',
});
await transmit.subscribe({
uid: stream3.getUid(),
channel: 'channel2',
});
await transmit.subscribe({
uid: stream4.getUid(),
channel: 'channel2',
});
await transmit.subscribe({
uid: stream5.getUid(),
channel: 'channel2',
});
const uuidsChannel1 = transmit.getSubscribersFor('channel1');
const uuidsChannel2 = transmit.getSubscribersFor('channel2');
assert.lengthOf(uuidsChannel1, 2);
assert.equal(uuidsChannel1[0], stream1.getUid());
assert.equal(uuidsChannel1[1], stream2.getUid());
assert.lengthOf(uuidsChannel2, 3);
assert.equal(uuidsChannel2[0], stream3.getUid());
assert.equal(uuidsChannel2[1], stream4.getUid());
assert.equal(uuidsChannel2[2], stream5.getUid());
});
});