578 lines
20 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
}); |