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(); }); }); });