import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock dependencies before imports vi.mock('drizzle-orm/postgres-js', () => ({ drizzle: vi.fn(() => mockDb), })); vi.mock('postgres', () => ({ default: vi.fn(() => ({})), })); vi.mock('../socket', () => ({ getSocketIO: vi.fn(() => mockSocketIO), })); let mockDb: any; let mockSocketIO: any; // Setup mocks beforeEach(() => { mockSocketIO = { to: vi.fn().mockReturnThis(), emit: vi.fn(), }; mockDb = { query: { seasons: { findMany: vi.fn(), findFirst: vi.fn(), }, draftSlots: { findMany: vi.fn(), }, draftTimers: { findFirst: vi.fn(), }, autodraftSettings: { findFirst: vi.fn(), }, draftPicks: { findMany: vi.fn(), findFirst: vi.fn(), }, draftQueue: { findMany: vi.fn(), }, participants: { findMany: vi.fn(), }, seasonTemplateSports: { findMany: vi.fn(), }, }, update: vi.fn().mockReturnThis(), set: vi.fn().mockReturnThis(), where: vi.fn().mockReturnThis(), returning: vi.fn(), insert: vi.fn().mockReturnThis(), values: vi.fn().mockReturnThis(), delete: vi.fn().mockReturnThis(), select: vi.fn().mockReturnThis(), from: vi.fn().mockReturnThis(), innerJoin: vi.fn().mockReturnThis(), orderBy: vi.fn().mockReturnThis(), limit: vi.fn().mockReturnThis(), }; }); describe('Timer Autodraft Integration', () => { describe('Autodraft Settings Check', () => { it('should check autodraft settings when timer expires', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; // Mock active draft mockDb.query.seasons.findMany.mockResolvedValue([ { id: seasonId, status: 'draft', draftPaused: false, currentPickNumber: 1, draftInitialTime: 120, draftIncrementTime: 30, draftRounds: 10, }, ]); // Mock draft slots mockDb.query.draftSlots.findMany.mockResolvedValue([ { id: 'slot-1', teamId, draftOrder: 1 }, { id: 'slot-2', teamId: 'team-2', draftOrder: 2 }, ]); // Mock timer at 0 mockDb.query.draftTimers.findFirst.mockResolvedValue({ id: 'timer-1', seasonId, teamId, timeRemaining: 0, }); // Mock autodraft settings enabled mockDb.query.autodraftSettings.findFirst.mockResolvedValue({ id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', }); // Mock no existing pick mockDb.query.draftPicks.findFirst.mockResolvedValue(null); // Mock queue mockDb.query.draftQueue.findMany.mockResolvedValue([ { id: 'queue-1', participantId: 'participant-1', queuePosition: 1, }, ]); // Mock drafted picks mockDb.query.draftPicks.findMany.mockResolvedValue([]); // Mock insert returning created pick mockDb.returning.mockResolvedValue([ { id: 'pick-1', seasonId, teamId, participantId: 'participant-1', pickNumber: 1, round: 1, pickInRound: 1, pickedByUserId: '', pickedByType: 'auto', timeUsed: 120, createdAt: new Date(), }, ]); // Mock select for complete pick mockDb.limit.mockResolvedValue([ { id: 'pick-1', seasonId, teamId, participantId: 'participant-1', pickNumber: 1, round: 1, pickInRound: 1, pickedByUserId: '', pickedByType: 'auto', timeUsed: 120, createdAt: new Date(), team: { id: teamId, name: 'Test Team' }, participant: { id: 'participant-1', name: 'Test Participant' }, sport: { id: 'sport-1', name: 'Test Sport' }, }, ]); // The actual timer logic would be tested here // This test verifies the mocks are set up correctly expect(mockDb.query.autodraftSettings.findFirst).toBeDefined(); }); it('should use regular auto-pick when autodraft is disabled', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; // Mock autodraft settings disabled mockDb.query.autodraftSettings.findFirst.mockResolvedValue({ id: 'settings-1', seasonId, teamId, isEnabled: false, mode: 'next_pick', }); const settings = await mockDb.query.autodraftSettings.findFirst(); expect(settings.isEnabled).toBe(false); }); it('should handle missing autodraft settings', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; // Mock no autodraft settings mockDb.query.autodraftSettings.findFirst.mockResolvedValue(null); const settings = await mockDb.query.autodraftSettings.findFirst(); expect(settings).toBeNull(); }); }); describe('Autodraft Mode Handling', () => { it('should disable autodraft after pick when mode is next_pick', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', }; // Mock update mockDb.returning.mockResolvedValue([ { ...autodraftSettings, isEnabled: false, updatedAt: new Date(), }, ]); // Simulate disabling after pick if (autodraftSettings.isEnabled && autodraftSettings.mode === 'next_pick') { await mockDb.update(); await mockDb.set({ isEnabled: false, updatedAt: new Date() }); await mockDb.where(); const result = await mockDb.returning(); expect(result[0].isEnabled).toBe(false); expect(mockSocketIO.to).toBeDefined(); } }); it('should keep autodraft enabled when mode is while_on', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; const autodraftSettings = { id: 'settings-1', seasonId: _seasonId, teamId: _teamId, isEnabled: true, mode: 'while_on', }; // Should not disable when mode is while_on if (autodraftSettings.mode === 'while_on') { expect(autodraftSettings.isEnabled).toBe(true); } }); }); describe('Autodraft Pick Logic', () => { it('should pick from queue first when autodraft is enabled', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; // Mock queue with available participant mockDb.query.draftQueue.findMany.mockResolvedValue([ { id: 'queue-1', participantId: 'participant-1', queuePosition: 1, participant: { id: 'participant-1', name: 'Queued Participant', }, }, { id: 'queue-2', participantId: 'participant-2', queuePosition: 2, participant: { id: 'participant-2', name: 'Second Queued', }, }, ]); // Mock no drafted picks mockDb.query.draftPicks.findMany.mockResolvedValue([]); const queueItems = await mockDb.query.draftQueue.findMany(); const draftedPicks = await mockDb.query.draftPicks.findMany(); const draftedParticipantIds = draftedPicks.map((p: any) => p.participantId); // Find first non-drafted participant from queue const availableQueueItem = queueItems.find( (item: any) => !draftedParticipantIds.includes(item.participantId) ); expect(availableQueueItem).toBeDefined(); expect(availableQueueItem.participantId).toBe('participant-1'); }); it('should skip drafted participants in queue', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; // Mock queue mockDb.query.draftQueue.findMany.mockResolvedValue([ { id: 'queue-1', participantId: 'participant-1', queuePosition: 1, }, { id: 'queue-2', participantId: 'participant-2', queuePosition: 2, }, ]); // Mock first participant already drafted mockDb.query.draftPicks.findMany.mockResolvedValue([ { id: 'pick-1', participantId: 'participant-1', }, ]); const queueItems = await mockDb.query.draftQueue.findMany(); const draftedPicks = await mockDb.query.draftPicks.findMany(); const draftedParticipantIds = draftedPicks.map((p: any) => p.participantId); // Should pick second participant const availableQueueItem = queueItems.find( (item: any) => !draftedParticipantIds.includes(item.participantId) ); expect(availableQueueItem.participantId).toBe('participant-2'); }); it('should fall back to highest EV when queue is empty or all drafted', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; // Mock empty queue mockDb.query.draftQueue.findMany.mockResolvedValue([]); // Mock season template sports mockDb.query.seasonTemplateSports.findMany.mockResolvedValue([ { sportsSeasonId: 'sports-season-1' }, ]); // Mock available participants sorted by EV mockDb.query.participants.findMany.mockResolvedValue([ { id: 'participant-high-ev', name: 'High EV Participant', expectedValue: 1000, }, ]); const queueItems = await mockDb.query.draftQueue.findMany(); expect(queueItems.length).toBe(0); const availableParticipants = await mockDb.query.participants.findMany(); expect(availableParticipants[0].expectedValue).toBe(1000); }); }); describe('Socket Event Emission', () => { it('should emit autodraft-updated event when disabling next_pick mode', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; mockSocketIO.to(`draft-${seasonId}`).emit('autodraft-updated', { teamId, isEnabled: false, mode: 'next_pick', }); expect(mockSocketIO.to).toHaveBeenCalledWith(`draft-${seasonId}`); expect(mockSocketIO.emit).toHaveBeenCalledWith('autodraft-updated', { teamId, isEnabled: false, mode: 'next_pick', }); }); it('should emit pick-made event after autodraft pick', async () => { const seasonId = 'season-123'; const _teamId = 'team-456'; const pickData = { pick: { id: 'pick-1', participantId: 'participant-1', pickNumber: 1, }, nextPickNumber: 2, isDraftComplete: false, }; mockSocketIO.to(`draft-${seasonId}`).emit('pick-made', pickData); expect(mockSocketIO.emit).toHaveBeenCalledWith('pick-made', pickData); }); }); describe('Back-to-Back Picks', () => { it('should only autodraft first pick when mode is next_pick and team has consecutive picks', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; // Mock autodraft settings with next_pick mode const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', }; // Simulate first pick (pick #1) const _firstPickNumber = 1; // After first pick, autodraft should be disabled if (autodraftSettings.isEnabled && autodraftSettings.mode === 'next_pick') { // Simulate disabling autodraft after first pick const updatedSettings = { ...autodraftSettings, isEnabled: false, }; expect(updatedSettings.isEnabled).toBe(false); expect(updatedSettings.mode).toBe('next_pick'); } // Simulate second pick (pick #2) - should NOT autodraft const _secondPickNumber = 2; // Autodraft should now be disabled, so second pick requires manual action const settingsForSecondPick = { ...autodraftSettings, isEnabled: false, }; expect(settingsForSecondPick.isEnabled).toBe(false); // Verify that autodraft-updated event would be emitted after first pick mockSocketIO.to(`draft-${seasonId}`).emit('autodraft-updated', { teamId, isEnabled: false, mode: 'next_pick', }); expect(mockSocketIO.emit).toHaveBeenCalledWith('autodraft-updated', { teamId, isEnabled: false, mode: 'next_pick', }); }); it('should continue autodrafting consecutive picks when mode is while_on', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; // Mock autodraft settings with while_on mode const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'while_on', }; // Simulate first pick (pick #1) const _firstPickNumber = 1; // After first pick, autodraft should REMAIN enabled for while_on mode if (autodraftSettings.mode === 'while_on') { expect(autodraftSettings.isEnabled).toBe(true); } // Simulate second pick (pick #2) - SHOULD autodraft const _secondPickNumber = 2; // Autodraft should still be enabled for second pick expect(autodraftSettings.isEnabled).toBe(true); expect(autodraftSettings.mode).toBe('while_on'); }); it('should handle snake draft back-to-back picks correctly', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; const _totalTeams = 10; // In a snake draft with 10 teams: // Pick 10 (end of round 1) and Pick 11 (start of round 2) are back-to-back for same team const _firstPick = 10; // Last pick of round 1 const _secondPick = 11; // First pick of round 2 (same team in snake draft) // Mock autodraft with next_pick mode const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', }; // After pick 10, autodraft should be disabled const settingsAfterFirstPick = { ...autodraftSettings, isEnabled: false, }; expect(settingsAfterFirstPick.isEnabled).toBe(false); // Pick 11 should NOT autodraft because autodraft was disabled after pick 10 // This ensures the team owner has a chance to make their second pick manually expect(settingsAfterFirstPick.mode).toBe('next_pick'); }); }); describe('Time Increment', () => { it('should add increment time after autodraft pick', async () => { const _seasonId = 'season-123'; const _teamId = 'team-456'; const incrementTime = 30; const currentTime = 0; const newTimeRemaining = currentTime + incrementTime; expect(newTimeRemaining).toBe(30); }); it('should emit timer-update after adding increment', async () => { const seasonId = 'season-123'; const teamId = 'team-456'; mockSocketIO.to(`draft-${seasonId}`).emit('timer-update', { seasonId, teamId, timeRemaining: 30, currentPickNumber: 1, }); expect(mockSocketIO.emit).toHaveBeenCalledWith('timer-update', { seasonId, teamId, timeRemaining: 30, currentPickNumber: 1, }); }); }); });