293 lines
8.9 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|