Files
gridpilot.gg/core/racing/application/use-cases/GetRaceDetailUseCase.test.ts
2025-12-16 21:05:01 +01:00

162 lines
6.3 KiB
TypeScript

import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { GetRaceDetailUseCase } from './GetRaceDetailUseCase';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import type { IImageServicePort } from '../ports/IImageServicePort';
describe('GetRaceDetailUseCase', () => {
let useCase: GetRaceDetailUseCase;
let raceRepository: { findById: Mock };
let leagueRepository: { findById: Mock };
let driverRepository: { findById: Mock };
let raceRegistrationRepository: { getRegisteredDrivers: Mock };
let resultRepository: { findByRaceId: Mock };
let leagueMembershipRepository: { getMembership: Mock };
let driverRatingProvider: { getRating: Mock; getRatings: Mock };
let imageService: { getDriverAvatar: Mock; getTeamLogo: Mock; getLeagueCover: Mock; getLeagueLogo: Mock };
beforeEach(() => {
raceRepository = { findById: vi.fn() };
leagueRepository = { findById: vi.fn() };
driverRepository = { findById: vi.fn() };
raceRegistrationRepository = { getRegisteredDrivers: vi.fn() };
resultRepository = { findByRaceId: vi.fn() };
leagueMembershipRepository = { getMembership: vi.fn() };
driverRatingProvider = { getRating: vi.fn(), getRatings: vi.fn() };
imageService = { getDriverAvatar: vi.fn(), getTeamLogo: vi.fn(), getLeagueCover: vi.fn(), getLeagueLogo: vi.fn() };
useCase = new GetRaceDetailUseCase(
raceRepository as unknown as IRaceRepository,
leagueRepository as unknown as ILeagueRepository,
driverRepository as unknown as IDriverRepository,
raceRegistrationRepository as unknown as IRaceRegistrationRepository,
resultRepository as unknown as IResultRepository,
leagueMembershipRepository as unknown as ILeagueMembershipRepository,
driverRatingProvider as DriverRatingProvider,
imageService as IImageServicePort,
);
});
it('should return race detail when race exists', async () => {
const raceId = 'race-1';
const driverId = 'driver-1';
const race = {
id: raceId,
leagueId: 'league-1',
track: 'Track 1',
car: 'Car 1',
scheduledAt: new Date('2023-01-01T10:00:00Z'),
sessionType: 'race' as const,
status: 'scheduled' as const,
strengthOfField: 1500,
registeredCount: 10,
maxParticipants: 20,
};
const league = {
id: 'league-1',
name: 'League 1',
description: 'Description',
settings: { maxDrivers: 20, qualifyingFormat: 'ladder' },
};
const registeredDriverIds = ['driver-1', 'driver-2'];
const membership = { status: 'active' as const };
const ratings = new Map([['driver-1', 1600], ['driver-2', 1400]]);
const drivers = [
{ id: 'driver-1', name: 'Driver 1', country: 'US' },
{ id: 'driver-2', name: 'Driver 2', country: 'UK' },
];
raceRepository.findById.mockResolvedValue(race);
leagueRepository.findById.mockResolvedValue(league);
raceRegistrationRepository.getRegisteredDrivers.mockResolvedValue(registeredDriverIds);
leagueMembershipRepository.getMembership.mockResolvedValue(membership);
driverRatingProvider.getRatings.mockReturnValue(ratings);
driverRepository.findById.mockImplementation((id) => Promise.resolve(drivers.find(d => d.id === id) || null));
imageService.getDriverAvatar.mockImplementation((id) => `avatar-${id}`);
const result = await useCase.execute({ raceId, driverId });
expect(result.isOk()).toBe(true);
const viewModel = result.unwrap();
expect(viewModel.race).toEqual({
id: raceId,
leagueId: 'league-1',
track: 'Track 1',
car: 'Car 1',
scheduledAt: '2023-01-01T10:00:00.000Z',
sessionType: 'race',
status: 'scheduled',
strengthOfField: 1500,
registeredCount: 10,
maxParticipants: 20,
});
expect(viewModel.league).toEqual({
id: 'league-1',
name: 'League 1',
description: 'Description',
settings: { maxDrivers: 20, qualifyingFormat: 'ladder' },
});
expect(viewModel.entryList).toHaveLength(2);
expect(viewModel.registration).toEqual({ isUserRegistered: true, canRegister: false });
expect(viewModel.userResult).toBeNull();
});
it('should return error when race not found', async () => {
raceRepository.findById.mockResolvedValue(null);
const result = await useCase.execute({ raceId: 'race-1', driverId: 'driver-1' });
expect(result.isErr()).toBe(true);
expect(result.error).toEqual({ code: 'RACE_NOT_FOUND' });
});
it('should include user result when race is completed', async () => {
const raceId = 'race-1';
const driverId = 'driver-1';
const race = {
id: raceId,
leagueId: 'league-1',
track: 'Track 1',
car: 'Car 1',
scheduledAt: new Date('2023-01-01T10:00:00Z'),
sessionType: 'race' as const,
status: 'completed' as const,
};
const results = [{
driverId: 'driver-1',
position: 2,
startPosition: 1,
incidents: 0,
fastestLap: 120,
getPositionChange: () => -1,
isPodium: () => true,
isClean: () => true,
}];
raceRepository.findById.mockResolvedValue(race);
leagueRepository.findById.mockResolvedValue(null);
raceRegistrationRepository.getRegisteredDrivers.mockResolvedValue([]);
leagueMembershipRepository.getMembership.mockResolvedValue(null);
driverRatingProvider.getRatings.mockReturnValue(new Map());
resultRepository.findByRaceId.mockResolvedValue(results);
const result = await useCase.execute({ raceId, driverId });
expect(result.isOk()).toBe(true);
const viewModel = result.unwrap();
expect(viewModel.userResult).toEqual({
position: 2,
startPosition: 1,
incidents: 0,
fastestLap: 120,
positionChange: -1,
isPodium: true,
isClean: true,
ratingChange: 61, // based on calculateRatingChange
});
});
});