179 lines
5.8 KiB
TypeScript
179 lines
5.8 KiB
TypeScript
import { beforeEach, describe, expect, it } from 'vitest';
|
|
import { LeaguesTestContext } from '../LeaguesTestContext';
|
|
import { League as RacingLeague } from '../../../../core/racing/domain/entities/League';
|
|
import { Season } from '../../../../core/racing/domain/entities/season/Season';
|
|
import { LeagueMembership } from '../../../../core/racing/domain/entities/LeagueMembership';
|
|
|
|
// Note: the current racing module does not expose explicit "open/close registration" use-cases.
|
|
// Registration is modeled via membership + registrations repository interactions.
|
|
|
|
describe('League Schedule - Race Registration', () => {
|
|
let context: LeaguesTestContext;
|
|
|
|
beforeEach(() => {
|
|
context = new LeaguesTestContext();
|
|
context.clear();
|
|
});
|
|
|
|
const seedRacingLeague = async (params: { leagueId: string }) => {
|
|
const league = RacingLeague.create({
|
|
id: params.leagueId,
|
|
name: 'Racing League',
|
|
description: 'League used for registration integration tests',
|
|
ownerId: 'driver-123',
|
|
});
|
|
|
|
await context.racingLeagueRepository.create(league);
|
|
return league;
|
|
};
|
|
|
|
const seedSeason = async (params: {
|
|
seasonId: string;
|
|
leagueId: string;
|
|
startDate?: Date;
|
|
endDate?: Date;
|
|
}) => {
|
|
const season = Season.create({
|
|
id: params.seasonId,
|
|
leagueId: params.leagueId,
|
|
gameId: 'iracing',
|
|
name: 'Season 1',
|
|
status: 'active',
|
|
startDate: params.startDate ?? new Date('2025-01-01T00:00:00Z'),
|
|
endDate: params.endDate ?? new Date('2025-01-31T23:59:59Z'),
|
|
});
|
|
|
|
await context.seasonRepository.add(season);
|
|
return season;
|
|
};
|
|
|
|
const seedActiveMembership = async (params: { leagueId: string; driverId: string }) => {
|
|
const membership = LeagueMembership.create({
|
|
leagueId: params.leagueId,
|
|
driverId: params.driverId,
|
|
role: 'member',
|
|
status: 'active',
|
|
});
|
|
|
|
await context.leagueMembershipRepository.saveMembership(membership);
|
|
return membership;
|
|
};
|
|
|
|
it('registers an active league member for a race', async () => {
|
|
const leagueId = 'league-1';
|
|
const seasonId = 'season-1';
|
|
const driverId = 'driver-1';
|
|
|
|
await seedRacingLeague({ leagueId });
|
|
await seedSeason({ leagueId, seasonId });
|
|
await seedActiveMembership({ leagueId, driverId });
|
|
|
|
const createRace = await context.createLeagueSeasonScheduleRaceUseCase.execute({
|
|
leagueId,
|
|
seasonId,
|
|
track: 'Monza',
|
|
car: 'GT3',
|
|
scheduledAt: new Date('2025-01-10T20:00:00Z'),
|
|
});
|
|
expect(createRace.isOk()).toBe(true);
|
|
const { raceId } = createRace.unwrap();
|
|
|
|
const register = await context.registerForRaceUseCase.execute({
|
|
leagueId,
|
|
raceId,
|
|
driverId,
|
|
});
|
|
|
|
expect(register.isOk()).toBe(true);
|
|
expect(register.unwrap()).toEqual({ raceId, driverId, status: 'registered' });
|
|
|
|
const isRegistered = await context.raceRegistrationRepository.isRegistered(raceId, driverId);
|
|
expect(isRegistered).toBe(true);
|
|
});
|
|
|
|
it('rejects registration when driver is not an active member', async () => {
|
|
const leagueId = 'league-1';
|
|
const seasonId = 'season-1';
|
|
|
|
await seedRacingLeague({ leagueId });
|
|
await seedSeason({ leagueId, seasonId });
|
|
|
|
const createRace = await context.createLeagueSeasonScheduleRaceUseCase.execute({
|
|
leagueId,
|
|
seasonId,
|
|
track: 'Monza',
|
|
car: 'GT3',
|
|
scheduledAt: new Date('2025-01-10T20:00:00Z'),
|
|
});
|
|
expect(createRace.isOk()).toBe(true);
|
|
|
|
const { raceId } = createRace.unwrap();
|
|
const result = await context.registerForRaceUseCase.execute({ leagueId, raceId, driverId: 'driver-missing' });
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.unwrapErr().code).toBe('NOT_ACTIVE_MEMBER');
|
|
});
|
|
|
|
it('rejects duplicate registration', async () => {
|
|
const leagueId = 'league-1';
|
|
const seasonId = 'season-1';
|
|
const driverId = 'driver-1';
|
|
|
|
await seedRacingLeague({ leagueId });
|
|
await seedSeason({ leagueId, seasonId });
|
|
await seedActiveMembership({ leagueId, driverId });
|
|
|
|
const createRace = await context.createLeagueSeasonScheduleRaceUseCase.execute({
|
|
leagueId,
|
|
seasonId,
|
|
track: 'Monza',
|
|
car: 'GT3',
|
|
scheduledAt: new Date('2025-01-10T20:00:00Z'),
|
|
});
|
|
expect(createRace.isOk()).toBe(true);
|
|
const { raceId } = createRace.unwrap();
|
|
|
|
const first = await context.registerForRaceUseCase.execute({ leagueId, raceId, driverId });
|
|
expect(first.isOk()).toBe(true);
|
|
|
|
const second = await context.registerForRaceUseCase.execute({ leagueId, raceId, driverId });
|
|
expect(second.isErr()).toBe(true);
|
|
expect(second.unwrapErr().code).toBe('ALREADY_REGISTERED');
|
|
});
|
|
|
|
it('withdraws an existing registration for an upcoming race', async () => {
|
|
const leagueId = 'league-1';
|
|
const seasonId = 'season-1';
|
|
const driverId = 'driver-1';
|
|
|
|
await seedRacingLeague({ leagueId });
|
|
await seedSeason({
|
|
leagueId,
|
|
seasonId,
|
|
startDate: new Date('2000-01-01T00:00:00Z'),
|
|
endDate: new Date('2100-12-31T23:59:59Z'),
|
|
});
|
|
await seedActiveMembership({ leagueId, driverId });
|
|
|
|
const createRace = await context.createLeagueSeasonScheduleRaceUseCase.execute({
|
|
leagueId,
|
|
seasonId,
|
|
track: 'Monza',
|
|
car: 'GT3',
|
|
scheduledAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
});
|
|
expect(createRace.isOk()).toBe(true);
|
|
const { raceId } = createRace.unwrap();
|
|
|
|
const register = await context.registerForRaceUseCase.execute({ leagueId, raceId, driverId });
|
|
expect(register.isOk()).toBe(true);
|
|
|
|
const withdraw = await context.withdrawFromRaceUseCase.execute({ raceId, driverId });
|
|
expect(withdraw.isOk()).toBe(true);
|
|
expect(withdraw.unwrap()).toEqual({ raceId, driverId, status: 'withdrawn' });
|
|
|
|
const isRegistered = await context.raceRegistrationRepository.isRegistered(raceId, driverId);
|
|
expect(isRegistered).toBe(false);
|
|
});
|
|
});
|