Files
gridpilot.gg/apps/website/lib/services/races/RaceStewardingService.test.ts
2026-01-12 01:01:49 +01:00

140 lines
5.2 KiB
TypeScript

import { describe, it, expect, vi, Mocked } from 'vitest';
import { RaceStewardingService } from './RaceStewardingService';
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { ProtestsApiClient } from '@/lib/api/protests/ProtestsApiClient';
import { PenaltiesApiClient } from '@/lib/api/penalties/PenaltiesApiClient';
import { RaceStewardingViewModel } from '@/lib/view-models/RaceStewardingViewModel';
describe('RaceStewardingService', () => {
let mockRacesApiClient: Mocked<RacesApiClient>;
let mockProtestsApiClient: Mocked<ProtestsApiClient>;
let mockPenaltiesApiClient: Mocked<PenaltiesApiClient>;
let service: RaceStewardingService;
beforeEach(() => {
mockRacesApiClient = {
getDetail: vi.fn(),
} as Mocked<RacesApiClient>;
mockProtestsApiClient = {
getRaceProtests: vi.fn(),
} as Mocked<ProtestsApiClient>;
mockPenaltiesApiClient = {
getRacePenalties: vi.fn(),
} as Mocked<PenaltiesApiClient>;
service = new RaceStewardingService(mockRacesApiClient, mockProtestsApiClient, mockPenaltiesApiClient);
});
describe('getRaceStewardingData', () => {
it('should return race stewarding data with all protests and penalties', async () => {
const raceId = 'race-123';
const driverId = 'driver-456';
const raceDetailDto = {
race: {
id: raceId,
track: 'Monza',
scheduledAt: '2023-10-01T10:00:00Z',
status: 'completed',
},
league: {
id: 'league-1',
name: 'Test League',
},
};
const protestsDto = {
protests: [
{
id: 'protest-1',
protestingDriverId: 'driver-1',
accusedDriverId: 'driver-2',
incident: { lap: 5, description: 'Contact at turn 1' },
filedAt: '2023-10-01T10:00:00Z',
status: 'pending',
},
{
id: 'protest-2',
protestingDriverId: 'driver-3',
accusedDriverId: 'driver-4',
incident: { lap: 10, description: 'Off-track' },
filedAt: '2023-10-01T10:05:00Z',
status: 'upheld',
},
],
driverMap: {
'driver-1': { id: 'driver-1', name: 'Driver 1' },
'driver-2': { id: 'driver-2', name: 'Driver 2' },
'driver-3': { id: 'driver-3', name: 'Driver 3' },
'driver-4': { id: 'driver-4', name: 'Driver 4' },
},
};
const penaltiesDto = {
penalties: [
{ id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
{ id: 'penalty-2', driverId: 'driver-4', type: 'grid', value: 3, reason: 'Qualifying incident' },
],
driverMap: {
'driver-2': { id: 'driver-2', name: 'Driver 2' },
'driver-4': { id: 'driver-4', name: 'Driver 4' },
},
};
mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
mockProtestsApiClient.getRaceProtests.mockResolvedValue(protestsDto as any);
mockPenaltiesApiClient.getRacePenalties.mockResolvedValue(penaltiesDto as any);
const result = await service.getRaceStewardingData(raceId, driverId);
expect(mockRacesApiClient.getDetail).toHaveBeenCalledWith(raceId, driverId);
expect(mockProtestsApiClient.getRaceProtests).toHaveBeenCalledWith(raceId);
expect(mockPenaltiesApiClient.getRacePenalties).toHaveBeenCalledWith(raceId);
expect(result).toBeInstanceOf(RaceStewardingViewModel);
expect(result.race).toEqual(raceDetailDto.race);
expect(result.league).toEqual(raceDetailDto.league);
expect(result.protests).toHaveLength(2);
expect(result.penalties).toHaveLength(2);
expect(result.pendingProtests).toHaveLength(1);
expect(result.resolvedProtests).toHaveLength(1);
expect(result.pendingCount).toBe(1);
expect(result.resolvedCount).toBe(1);
expect(result.penaltiesCount).toBe(2);
});
it('should handle empty protests and penalties', async () => {
const raceId = 'race-123';
const driverId = 'driver-456';
const raceDetailDto = {
race: { id: raceId, track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z', status: 'completed' },
league: { id: 'league-1', name: 'Test League' },
};
mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
mockProtestsApiClient.getRaceProtests.mockResolvedValue({ protests: [], driverMap: {} } as any);
mockPenaltiesApiClient.getRacePenalties.mockResolvedValue({ penalties: [], driverMap: {} } as any);
const result = await service.getRaceStewardingData(raceId, driverId);
expect(result.protests).toHaveLength(0);
expect(result.penalties).toHaveLength(0);
expect(result.pendingCount).toBe(0);
expect(result.resolvedCount).toBe(0);
expect(result.penaltiesCount).toBe(0);
});
it('should throw error when any API call fails', async () => {
const raceId = 'race-123';
const driverId = 'driver-456';
const error = new Error('API call failed');
mockRacesApiClient.getDetail.mockRejectedValue(error);
await expect(service.getRaceStewardingData(raceId, driverId)).rejects.toThrow('API call failed');
});
});
});