brackt/server/__tests__/socket-autodraft.test.ts

293 lines
8.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('Socket Autodraft Events', () => {
let mockSocket: any;
let mockIO: any;
beforeEach(() => {
vi.clearAllMocks();
mockSocket = {
id: 'socket-123',
join: vi.fn(),
leave: vi.fn(),
emit: vi.fn(),
on: vi.fn(),
};
mockIO = {
to: vi.fn().mockReturnThis(),
emit: vi.fn(),
on: vi.fn(),
};
});
describe('Team Connection Tracking', () => {
it('should emit team-connected event when team joins draft', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Simulate joining draft with teamId
mockSocket.join(`draft-${seasonId}`);
mockSocket.join(`team-${teamId}`);
mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockSocket.join).toHaveBeenCalledWith(`team-${teamId}`);
expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockIO.emit).toHaveBeenCalledWith('team-connected', { teamId });
});
it('should emit team-disconnected event when team leaves draft', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Simulate leaving draft
mockSocket.leave(`draft-${seasonId}`);
mockSocket.leave(`team-${teamId}`);
mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });
expect(mockSocket.leave).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockSocket.leave).toHaveBeenCalledWith(`team-${teamId}`);
expect(mockIO.emit).toHaveBeenCalledWith('team-disconnected', { teamId });
});
it('should emit team-disconnected on socket disconnect', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Simulate disconnect
mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });
expect(mockIO.emit).toHaveBeenCalledWith('team-disconnected', { teamId });
});
it('should not emit connection events when joining without teamId', () => {
const seasonId = 'season-123';
// Simulate joining draft without teamId (commissioner/spectator)
mockSocket.join(`draft-${seasonId}`);
expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockIO.emit).not.toHaveBeenCalledWith('team-connected', expect.anything());
});
});
describe('Autodraft Status Updates', () => {
it('should broadcast autodraft-updated event to draft room', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
});
it('should broadcast when autodraft is disabled', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: false,
mode: 'next_pick',
});
expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
teamId,
isEnabled: false,
mode: 'next_pick',
});
});
it('should broadcast mode changes', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Change from next_pick to while_on
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'while_on',
});
expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'while_on',
});
});
it('should include all required fields in autodraft-updated event', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
const emitCall = mockIO.emit.mock.calls[0];
expect(emitCall[0]).toBe('autodraft-updated');
expect(emitCall[1]).toHaveProperty('teamId');
expect(emitCall[1]).toHaveProperty('isEnabled');
expect(emitCall[1]).toHaveProperty('mode');
});
});
describe('Event Ordering', () => {
it('should emit team-connected before autodraft-updated on join', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
const emitSpy = vi.fn();
mockIO.emit = emitSpy;
// Join and set autodraft
mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
expect(emitSpy).toHaveBeenNthCalledWith(1, 'team-connected', { teamId });
expect(emitSpy).toHaveBeenNthCalledWith(2, 'autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
});
it('should emit autodraft-updated before team-disconnected on leave', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
const emitSpy = vi.fn();
mockIO.emit = emitSpy;
// Disable autodraft then leave
mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
teamId,
isEnabled: false,
mode: 'next_pick',
});
mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });
expect(emitSpy).toHaveBeenNthCalledWith(1, 'autodraft-updated', {
teamId,
isEnabled: false,
mode: 'next_pick',
});
expect(emitSpy).toHaveBeenNthCalledWith(2, 'team-disconnected', { teamId });
});
});
describe('Room Isolation', () => {
it('should only emit to specific draft room', () => {
const seasonId1 = 'season-123';
const seasonId2 = 'season-456';
const teamId = 'team-789';
const toSpy = vi.fn().mockReturnThis();
mockIO.to = toSpy;
mockIO.to(`draft-${seasonId1}`).emit('autodraft-updated', {
teamId,
isEnabled: true,
mode: 'next_pick',
});
expect(toSpy).toHaveBeenCalledWith(`draft-${seasonId1}`);
expect(toSpy).not.toHaveBeenCalledWith(`draft-${seasonId2}`);
});
it('should broadcast to all clients in the room', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Multiple clients in same room should all receive the event
mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
expect(mockIO.emit).toHaveBeenCalledWith('team-connected', { teamId });
});
});
describe('Error Handling', () => {
it('should handle missing seasonId gracefully', () => {
const seasonId = '';
if (!seasonId) {
// Should not attempt to join or emit
expect(mockSocket.join).not.toHaveBeenCalled();
expect(mockIO.emit).not.toHaveBeenCalled();
}
});
it('should handle missing teamId gracefully', () => {
const seasonId = 'season-123';
const teamId = undefined;
mockSocket.join(`draft-${seasonId}`);
// Should join draft room but not emit team-connected
expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
if (!teamId) {
expect(mockIO.emit).not.toHaveBeenCalledWith('team-connected', expect.anything());
}
});
});
describe('Multiple Connections', () => {
it('should handle same team connecting from multiple devices', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// First connection
mockSocket.join(`draft-${seasonId}`);
mockSocket.join(`team-${teamId}`);
mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
// Second connection (same team)
const mockSocket2 = { ...mockSocket, id: 'socket-456' };
mockSocket2.join(`draft-${seasonId}`);
mockSocket2.join(`team-${teamId}`);
mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
// Both should emit connection events
expect(mockIO.emit).toHaveBeenCalledTimes(2);
});
it('should only disconnect when all connections are closed', () => {
const seasonId = 'season-123';
const teamId = 'team-456';
// Two connections
const socket1 = { ...mockSocket, id: 'socket-1' };
const socket2 = { ...mockSocket, id: 'socket-2' };
// First disconnects
socket1.leave(`draft-${seasonId}`);
// Should not emit team-disconnected yet
// Second disconnects
socket2.leave(`draft-${seasonId}`);
mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });
// In practice, this would be handled by tracking connection count
expect(mockSocket.leave).toHaveBeenCalled();
});
});
});