website service tests
This commit is contained in:
205
apps/website/lib/services/protests/ProtestService.test.ts
Normal file
205
apps/website/lib/services/protests/ProtestService.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { ProtestService } from './ProtestService';
|
||||
import { ProtestsApiClient } from '../../api/protests/ProtestsApiClient';
|
||||
import { ProtestViewModel } from '../../view-models/ProtestViewModel';
|
||||
import { RaceViewModel } from '../../view-models/RaceViewModel';
|
||||
import { ProtestDriverViewModel } from '../../view-models/ProtestDriverViewModel';
|
||||
|
||||
describe('ProtestService', () => {
|
||||
let mockApiClient: Mocked<ProtestsApiClient>;
|
||||
let service: ProtestService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getLeagueProtests: vi.fn(),
|
||||
getLeagueProtest: vi.fn(),
|
||||
applyPenalty: vi.fn(),
|
||||
requestDefense: vi.fn(),
|
||||
reviewProtest: vi.fn(),
|
||||
getRaceProtests: vi.fn(),
|
||||
} as Mocked<ProtestsApiClient>;
|
||||
|
||||
service = new ProtestService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getLeagueProtests', () => {
|
||||
it('should call apiClient.getLeagueProtests and return transformed data', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const mockDto = {
|
||||
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',
|
||||
},
|
||||
],
|
||||
racesById: { 'race-1': { id: 'race-1', track: 'Monza' } },
|
||||
driversById: {
|
||||
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtests.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getLeagueProtests(leagueId);
|
||||
|
||||
expect(mockApiClient.getLeagueProtests).toHaveBeenCalledWith(leagueId);
|
||||
expect(result.protests).toHaveLength(1);
|
||||
expect(result.protests[0]).toBeInstanceOf(ProtestViewModel);
|
||||
expect(result.racesById).toEqual(mockDto.racesById);
|
||||
expect(result.driversById).toEqual(mockDto.driversById);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getLeagueProtests fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getLeagueProtests.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getLeagueProtests(leagueId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProtestById', () => {
|
||||
it('should call apiClient.getLeagueProtest and return transformed data', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-1';
|
||||
const mockDto = {
|
||||
protests: [
|
||||
{
|
||||
id: protestId,
|
||||
protestingDriverId: 'driver-1',
|
||||
accusedDriverId: 'driver-2',
|
||||
incident: { lap: 5, description: 'Contact at turn 1' },
|
||||
filedAt: '2023-10-01T10:00:00Z',
|
||||
status: 'pending',
|
||||
},
|
||||
],
|
||||
racesById: { 'race-1': { id: 'race-1', track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z' } },
|
||||
driversById: {
|
||||
'driver-1': { id: 'driver-1', name: 'Driver 1', avatarUrl: 'https://example.com/avatar1.jpg' },
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2', avatarUrl: 'https://example.com/avatar2.jpg' },
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtest.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getProtestById(leagueId, protestId);
|
||||
|
||||
expect(mockApiClient.getLeagueProtest).toHaveBeenCalledWith(leagueId, protestId);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.protest).toBeInstanceOf(ProtestViewModel);
|
||||
expect(result?.race).toBeInstanceOf(RaceViewModel);
|
||||
expect(result?.protestingDriver).toBeInstanceOf(ProtestDriverViewModel);
|
||||
expect(result?.accusedDriver).toBeInstanceOf(ProtestDriverViewModel);
|
||||
});
|
||||
|
||||
it('should return null when protest not found', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-999';
|
||||
const mockDto = {
|
||||
protests: [],
|
||||
racesById: {},
|
||||
driversById: {},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtest.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getProtestById(leagueId, protestId);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getLeagueProtest fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-1';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getLeagueProtest.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getProtestById(leagueId, protestId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyPenalty', () => {
|
||||
it('should call apiClient.applyPenalty', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
driverId: 'driver-456',
|
||||
type: 'time',
|
||||
value: 10,
|
||||
reason: 'Test reason',
|
||||
};
|
||||
|
||||
mockApiClient.applyPenalty.mockResolvedValue(undefined);
|
||||
|
||||
await service.applyPenalty(input);
|
||||
|
||||
expect(mockApiClient.applyPenalty).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestDefense', () => {
|
||||
it('should call apiClient.requestDefense', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
driverId: 'driver-456',
|
||||
message: 'Please provide defense',
|
||||
};
|
||||
|
||||
mockApiClient.requestDefense.mockResolvedValue(undefined);
|
||||
|
||||
await service.requestDefense(input);
|
||||
|
||||
expect(mockApiClient.requestDefense).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reviewProtest', () => {
|
||||
it('should call apiClient.reviewProtest', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
stewardId: 'steward-456',
|
||||
decision: 'upheld',
|
||||
decisionNotes: 'Test notes',
|
||||
};
|
||||
|
||||
mockApiClient.reviewProtest.mockResolvedValue(undefined);
|
||||
|
||||
await service.reviewProtest(input);
|
||||
|
||||
expect(mockApiClient.reviewProtest).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findByRaceId', () => {
|
||||
it('should call apiClient.getRaceProtests and return protests array', async () => {
|
||||
const raceId = 'race-123';
|
||||
const mockDto = {
|
||||
protests: [
|
||||
{ id: 'protest-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', status: 'pending' },
|
||||
{ id: 'protest-2', protestingDriverId: 'driver-3', accusedDriverId: 'driver-4', status: 'resolved' },
|
||||
],
|
||||
driverMap: {},
|
||||
};
|
||||
|
||||
mockApiClient.getRaceProtests.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.findByRaceId(raceId);
|
||||
|
||||
expect(mockApiClient.getRaceProtests).toHaveBeenCalledWith(raceId);
|
||||
expect(result).toEqual(mockDto.protests);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getRaceProtests fails', async () => {
|
||||
const raceId = 'race-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getRaceProtests.mockRejectedValue(error);
|
||||
|
||||
await expect(service.findByRaceId(raceId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user