Files
gridpilot.gg/core/racing/domain/entities/RaceEvent.test.ts
2025-12-17 00:33:13 +01:00

578 lines
20 KiB
TypeScript

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