import { RaceEvent } from './RaceEvent'; import { Session } from './Session'; import { SessionType } from '../value-objects/SessionType'; import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError'; describe('RaceEvent', () => { const createMockSession = (overrides: Partial<{ id: string; raceEventId: string; sessionType: SessionType; status: 'scheduled' | 'running' | 'completed' | 'cancelled'; scheduledAt: Date; }> = {}): Session => { return Session.create({ id: overrides.id ?? 'session-1', raceEventId: overrides.raceEventId ?? 'race-event-1', scheduledAt: overrides.scheduledAt ?? new Date('2023-01-01T10:00:00Z'), track: 'Monza', car: 'Ferrari SF21', sessionType: overrides.sessionType ?? SessionType.main(), status: overrides.status ?? 'scheduled', }); }; describe('create', () => { it('should create a race event with required fields', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.id).toBe('race-event-1'); expect(raceEvent.seasonId).toBe('season-1'); expect(raceEvent.leagueId).toBe('league-1'); expect(raceEvent.name).toBe('Monza Grand Prix'); expect(raceEvent.sessions).toHaveLength(1); expect(raceEvent.status).toBe('scheduled'); expect(raceEvent.stewardingClosesAt).toBeUndefined(); }); it('should create a race event with all fields', () => { const sessions = [ createMockSession({ id: 'session-1', sessionType: SessionType.practice() }), createMockSession({ id: 'session-2', sessionType: SessionType.qualifying() }), createMockSession({ id: 'session-3', sessionType: SessionType.main() }), ]; const stewardingClosesAt = new Date('2023-01-02T10:00:00Z'); const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', stewardingClosesAt, }); expect(raceEvent.id).toBe('race-event-1'); expect(raceEvent.seasonId).toBe('season-1'); expect(raceEvent.leagueId).toBe('league-1'); expect(raceEvent.name).toBe('Monza Grand Prix'); expect(raceEvent.sessions).toHaveLength(3); expect(raceEvent.status).toBe('in_progress'); expect(raceEvent.stewardingClosesAt).toEqual(stewardingClosesAt); }); it('should throw error for invalid id', () => { const sessions = [createMockSession()]; expect(() => RaceEvent.create({ id: '', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for invalid seasonId', () => { const sessions = [createMockSession()]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: '', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for invalid leagueId', () => { const sessions = [createMockSession()]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: '', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for invalid name', () => { const sessions = [createMockSession()]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: '', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for no sessions', () => { expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions: [], })).toThrow(RacingDomainValidationError); }); it('should throw error for sessions not belonging to race event', () => { const sessions = [createMockSession({ raceEventId: 'other-race-event' })]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for duplicate session types', () => { const sessions = [ createMockSession({ id: 'session-1', sessionType: SessionType.main() }), createMockSession({ id: 'session-2', sessionType: SessionType.main() }), ]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); it('should throw error for no main race session', () => { const sessions = [createMockSession({ sessionType: SessionType.practice() })]; expect(() => RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, })).toThrow(RacingDomainValidationError); }); }); describe('start', () => { it('should start a scheduled race event', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); const started = raceEvent.start(); expect(started.status).toBe('in_progress'); }); it('should throw error if not scheduled', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', }); expect(() => raceEvent.start()).toThrow(RacingDomainInvariantError); }); }); describe('completeMainRace', () => { it('should complete main race and move to awaiting_stewarding', () => { const mainSession = createMockSession({ sessionType: SessionType.main(), status: 'completed' }); const sessions = [mainSession]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', }); const completed = raceEvent.completeMainRace(); expect(completed.status).toBe('awaiting_stewarding'); }); it('should throw error if not in progress', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); expect(() => raceEvent.completeMainRace()).toThrow(RacingDomainInvariantError); }); it('should throw error if main race not completed', () => { const sessions = [createMockSession({ sessionType: SessionType.main(), status: 'running' })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', }); expect(() => raceEvent.completeMainRace()).toThrow(RacingDomainInvariantError); }); }); describe('closeStewarding', () => { it('should close stewarding and finalize race event', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'awaiting_stewarding', }); const closed = raceEvent.closeStewarding(); expect(closed.status).toBe('closed'); }); it('should throw error if not awaiting stewarding', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', }); expect(() => raceEvent.closeStewarding()).toThrow(RacingDomainInvariantError); }); }); describe('cancel', () => { it('should cancel a scheduled race event', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); const cancelled = raceEvent.cancel(); expect(cancelled.status).toBe('cancelled'); }); it('should return same instance if already cancelled', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'cancelled', }); const cancelled = raceEvent.cancel(); expect(cancelled).toBe(raceEvent); }); it('should throw error if closed', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'closed', }); expect(() => raceEvent.cancel()).toThrow(RacingDomainInvariantError); }); }); describe('getMainRaceSession', () => { it('should return the main race session', () => { const mainSession = createMockSession({ sessionType: SessionType.main() }); const practiceSession = createMockSession({ id: 'session-2', sessionType: SessionType.practice() }); const sessions = [practiceSession, mainSession]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.getMainRaceSession()).toBe(mainSession); }); }); describe('getSessionsByType', () => { it('should return sessions of specific type', () => { const practice = createMockSession({ id: 'p1', sessionType: SessionType.practice() }); const qualifying = createMockSession({ id: 'q1', sessionType: SessionType.qualifying() }); const main = createMockSession({ id: 'm1', sessionType: SessionType.main() }); const sessions = [practice, qualifying, main]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); const practices = raceEvent.getSessionsByType(SessionType.practice()); expect(practices).toHaveLength(1); expect(practices).toContain(practice); }); }); describe('getCompletedSessions', () => { it('should return only completed sessions', () => { const completed1 = createMockSession({ id: 'c1', sessionType: SessionType.practice(), status: 'completed' }); const completed2 = createMockSession({ id: 'c2', sessionType: SessionType.qualifying(), status: 'completed' }); const running = createMockSession({ id: 'r1', sessionType: SessionType.main(), status: 'running' }); const sessions = [completed1, running, completed2]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); const completed = raceEvent.getCompletedSessions(); expect(completed).toHaveLength(2); expect(completed).toContain(completed1); expect(completed).toContain(completed2); }); }); describe('areAllSessionsCompleted', () => { it('should return true if all sessions completed', () => { const sessions = [ createMockSession({ id: 's1', sessionType: SessionType.practice(), status: 'completed' }), createMockSession({ id: 's2', sessionType: SessionType.main(), status: 'completed' }), ]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.areAllSessionsCompleted()).toBe(true); }); it('should return false if not all sessions completed', () => { const sessions = [ createMockSession({ id: 's1', sessionType: SessionType.practice(), status: 'completed' }), createMockSession({ id: 's2', sessionType: SessionType.main(), status: 'running' }), ]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.areAllSessionsCompleted()).toBe(false); }); }); describe('isMainRaceCompleted', () => { it('should return true if main race is completed', () => { const mainSession = createMockSession({ sessionType: SessionType.main(), status: 'completed' }); const sessions = [mainSession]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.isMainRaceCompleted()).toBe(true); }); it('should return false if main race not completed', () => { const mainSession = createMockSession({ sessionType: SessionType.main(), status: 'running' }); const sessions = [mainSession]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.isMainRaceCompleted()).toBe(false); }); }); describe('hasStewardingExpired', () => { it('should return true if stewarding has expired', () => { const pastDate = new Date(Date.now() - 3600000); const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, stewardingClosesAt: pastDate, }); expect(raceEvent.hasStewardingExpired()).toBe(true); }); it('should return false if stewarding not set', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.hasStewardingExpired()).toBe(false); }); it('should return false if stewarding not expired', () => { const futureDate = new Date(Date.now() + 3600000); const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, stewardingClosesAt: futureDate, }); expect(raceEvent.hasStewardingExpired()).toBe(false); }); }); describe('isPast', () => { it('should return true if latest session is in past', () => { const pastDate = new Date(Date.now() - 3600000); const sessions = [createMockSession({ scheduledAt: pastDate, sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.isPast()).toBe(true); }); it('should return false if latest session is future', () => { const futureDate = new Date(Date.now() + 3600000); const sessions = [createMockSession({ scheduledAt: futureDate, sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, }); expect(raceEvent.isPast()).toBe(false); }); }); describe('isUpcoming', () => { it('should return true for scheduled future event', () => { const futureDate = new Date(Date.now() + 3600000); const sessions = [createMockSession({ scheduledAt: futureDate, sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); expect(raceEvent.isUpcoming()).toBe(true); }); it('should return false for past scheduled event', () => { const pastDate = new Date(Date.now() - 3600000); const sessions = [createMockSession({ scheduledAt: pastDate, sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); expect(raceEvent.isUpcoming()).toBe(false); }); }); describe('isLive', () => { it('should return true for in_progress status', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'in_progress', }); expect(raceEvent.isLive()).toBe(true); }); it('should return false for other statuses', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'scheduled', }); expect(raceEvent.isLive()).toBe(false); }); }); describe('isAwaitingStewarding', () => { it('should return true for awaiting_stewarding status', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'awaiting_stewarding', }); expect(raceEvent.isAwaitingStewarding()).toBe(true); }); }); describe('isClosed', () => { it('should return true for closed status', () => { const sessions = [createMockSession({ sessionType: SessionType.main() })]; const raceEvent = RaceEvent.create({ id: 'race-event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Monza Grand Prix', sessions, status: 'closed', }); expect(raceEvent.isClosed()).toBe(true); }); }); });