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; let mockProtestsApiClient: Mocked; let mockPenaltiesApiClient: Mocked; let service: RaceStewardingService; beforeEach(() => { mockRacesApiClient = { getDetail: vi.fn(), } as Mocked; mockProtestsApiClient = { getRaceProtests: vi.fn(), } as Mocked; mockPenaltiesApiClient = { getRacePenalties: vi.fn(), } as Mocked; 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'); }); }); });