103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
import {
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
it
|
|
} from 'vitest';
|
|
import { WebSocket } from 'ws';
|
|
import { connectedUsers } from './state';
|
|
import { ConnectedUser } from './types';
|
|
import { broadcastToServer, findUserByOderId, findVoiceActiveConnection } from './broadcast';
|
|
|
|
function createMockWs(): WebSocket & { sentMessages: string[] } {
|
|
const sent: string[] = [];
|
|
const ws = {
|
|
readyState: WebSocket.OPEN,
|
|
send: (data: string) => { sent.push(data); },
|
|
close: () => {},
|
|
sentMessages: sent
|
|
} as unknown as WebSocket & { sentMessages: string[] };
|
|
|
|
return ws;
|
|
}
|
|
|
|
function createConnectedUser(
|
|
connectionId: string,
|
|
oderId: string,
|
|
overrides: Partial<ConnectedUser> = {}
|
|
): ConnectedUser {
|
|
const user: ConnectedUser = {
|
|
oderId,
|
|
ws: createMockWs(),
|
|
authenticated: true,
|
|
serverIds: new Set(['server-1']),
|
|
displayName: 'Test User',
|
|
lastPong: Date.now(),
|
|
...overrides
|
|
};
|
|
|
|
connectedUsers.set(connectionId, user);
|
|
|
|
return user;
|
|
}
|
|
|
|
describe('broadcastToServer', () => {
|
|
beforeEach(() => {
|
|
connectedUsers.clear();
|
|
});
|
|
|
|
it('delivers chat_message to every connection in the server except the sender connection', () => {
|
|
createConnectedUser('conn-a1', 'user-1');
|
|
const connA2 = createConnectedUser('conn-a2', 'user-1');
|
|
const connB = createConnectedUser('conn-b', 'user-2');
|
|
|
|
broadcastToServer('server-1', { type: 'chat_message', text: 'hello' }, {
|
|
excludeConnectionId: 'conn-a1'
|
|
});
|
|
|
|
expect((connA2.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(1);
|
|
expect((connB.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(1);
|
|
expect(connectedUsers.get('conn-a1')?.ws).toBeDefined();
|
|
expect((connectedUsers.get('conn-a1')!.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(0);
|
|
});
|
|
|
|
it('excludes every connection for an identity when excludeIdentityOderId is set', () => {
|
|
const connA1 = createConnectedUser('conn-a1', 'user-1');
|
|
const connA2 = createConnectedUser('conn-a2', 'user-1');
|
|
const connB = createConnectedUser('conn-b', 'user-2');
|
|
|
|
broadcastToServer('server-1', { type: 'user_left', oderId: 'user-1' }, {
|
|
excludeIdentityOderId: 'user-1'
|
|
});
|
|
|
|
expect((connA1.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(0);
|
|
expect((connA2.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(0);
|
|
expect((connB.ws as WebSocket & { sentMessages: string[] }).sentMessages).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe('findVoiceActiveConnection', () => {
|
|
beforeEach(() => {
|
|
connectedUsers.clear();
|
|
});
|
|
|
|
it('returns the connection marked voiceActive for the user', () => {
|
|
createConnectedUser('conn-passive', 'user-1', { voiceActive: false });
|
|
const active = createConnectedUser('conn-active', 'user-1', { voiceActive: true });
|
|
|
|
expect(findVoiceActiveConnection('user-1')).toBe(active);
|
|
});
|
|
|
|
it('returns undefined when no voiceActive connection exists', () => {
|
|
createConnectedUser('conn-1', 'user-1');
|
|
|
|
expect(findVoiceActiveConnection('user-1')).toBeUndefined();
|
|
});
|
|
|
|
it('findUserByOderId falls back to any open connection when no voiceActive connection exists', () => {
|
|
const fallback = createConnectedUser('conn-1', 'user-1');
|
|
|
|
expect(findUserByOderId('user-1')).toBe(fallback);
|
|
});
|
|
});
|