180 lines
6.4 KiB
TypeScript
180 lines
6.4 KiB
TypeScript
import { describe, it, expect, vi, Mocked } from 'vitest';
|
|
import { LeagueStewardingService } from './LeagueStewardingService';
|
|
import { RaceService } from '../races/RaceService';
|
|
import { ProtestService } from '../protests/ProtestService';
|
|
import { PenaltyService } from '../penalties/PenaltyService';
|
|
import { DriverService } from '../drivers/DriverService';
|
|
import { LeagueMembershipService } from './LeagueMembershipService';
|
|
import { LeagueStewardingViewModel } from '@/lib/view-models/LeagueStewardingViewModel';
|
|
|
|
describe('LeagueStewardingService', () => {
|
|
let mockRaceService: Mocked<RaceService>;
|
|
let mockProtestService: Mocked<ProtestService>;
|
|
let mockPenaltyService: Mocked<PenaltyService>;
|
|
let mockDriverService: Mocked<DriverService>;
|
|
let mockLeagueMembershipService: Mocked<LeagueMembershipService>;
|
|
let service: LeagueStewardingService;
|
|
|
|
beforeEach(() => {
|
|
mockRaceService = {
|
|
findByLeagueId: vi.fn(),
|
|
} as Mocked<RaceService>;
|
|
|
|
mockProtestService = {
|
|
findByRaceId: vi.fn(),
|
|
getProtestById: vi.fn(),
|
|
} as Mocked<ProtestService>;
|
|
|
|
mockPenaltyService = {
|
|
findByRaceId: vi.fn(),
|
|
getPenaltyTypesReference: vi.fn(),
|
|
} as Mocked<PenaltyService>;
|
|
|
|
mockDriverService = {
|
|
findByIds: vi.fn(),
|
|
} as Mocked<DriverService>;
|
|
|
|
mockLeagueMembershipService = {} as Mocked<LeagueMembershipService>;
|
|
|
|
service = new LeagueStewardingService(
|
|
mockRaceService,
|
|
mockProtestService,
|
|
mockPenaltyService,
|
|
mockDriverService,
|
|
mockLeagueMembershipService
|
|
);
|
|
});
|
|
|
|
describe('getLeagueStewardingData', () => {
|
|
it('should return stewarding data with all races and their protests/penalties', async () => {
|
|
const leagueId = 'league-123';
|
|
|
|
// Mock races
|
|
mockRaceService.findByLeagueId.mockResolvedValue([
|
|
{ id: 'race-1', track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z' },
|
|
{ id: 'race-2', track: 'Silverstone', scheduledAt: '2023-09-15T10:00:00Z' },
|
|
]);
|
|
|
|
// Mock protests for race-1
|
|
mockProtestService.findByRaceId.mockImplementation(async (raceId) => {
|
|
if (raceId === 'race-1') {
|
|
return [
|
|
{ id: 'protest-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', status: 'pending' },
|
|
{ id: 'protest-2', protestingDriverId: 'driver-3', accusedDriverId: 'driver-4', status: 'upheld' },
|
|
];
|
|
}
|
|
return [];
|
|
});
|
|
|
|
// Mock penalties for race-1
|
|
mockPenaltyService.findByRaceId.mockImplementation(async (raceId) => {
|
|
if (raceId === 'race-1') {
|
|
return [
|
|
{ id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
|
|
];
|
|
}
|
|
return [];
|
|
});
|
|
|
|
// Mock driver service
|
|
mockDriverService.findByIds.mockResolvedValue([
|
|
{ id: 'driver-1', name: 'Driver 1' },
|
|
{ id: 'driver-2', name: 'Driver 2' },
|
|
{ id: 'driver-3', name: 'Driver 3' },
|
|
{ id: 'driver-4', name: 'Driver 4' },
|
|
]);
|
|
|
|
const result = await service.getLeagueStewardingData(leagueId);
|
|
|
|
expect(result).toBeInstanceOf(LeagueStewardingViewModel);
|
|
expect(result.racesWithData).toHaveLength(2);
|
|
expect(result.totalPending).toBe(1);
|
|
expect(result.totalResolved).toBe(1);
|
|
expect(result.totalPenalties).toBe(1);
|
|
expect(result.pendingRaces).toHaveLength(1);
|
|
expect(result.historyRaces).toHaveLength(1);
|
|
});
|
|
|
|
it('should handle empty races array', async () => {
|
|
const leagueId = 'league-123';
|
|
|
|
mockRaceService.findByLeagueId.mockResolvedValue([]);
|
|
mockProtestService.findByRaceId.mockResolvedValue([]);
|
|
mockPenaltyService.findByRaceId.mockResolvedValue([]);
|
|
mockDriverService.findByIds.mockResolvedValue([]);
|
|
|
|
const result = await service.getLeagueStewardingData(leagueId);
|
|
|
|
expect(result.racesWithData).toHaveLength(0);
|
|
expect(result.totalPending).toBe(0);
|
|
expect(result.totalResolved).toBe(0);
|
|
expect(result.totalPenalties).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('reviewProtest', () => {
|
|
it('should call protestService.reviewProtest', async () => {
|
|
const input = {
|
|
protestId: 'protest-123',
|
|
stewardId: 'steward-456',
|
|
decision: 'upheld',
|
|
decisionNotes: 'Test notes',
|
|
};
|
|
|
|
mockProtestService.reviewProtest = vi.fn().mockResolvedValue(undefined);
|
|
|
|
await service.reviewProtest(input);
|
|
|
|
expect(mockProtestService.reviewProtest).toHaveBeenCalledWith(input);
|
|
});
|
|
});
|
|
|
|
describe('applyPenalty', () => {
|
|
it('should call penaltyService.applyPenalty', async () => {
|
|
const input = {
|
|
driverId: 'driver-123',
|
|
raceId: 'race-456',
|
|
type: 'time',
|
|
value: 10,
|
|
reason: 'Test reason',
|
|
};
|
|
|
|
mockPenaltyService.applyPenalty = vi.fn().mockResolvedValue(undefined);
|
|
|
|
await service.applyPenalty(input);
|
|
|
|
expect(mockPenaltyService.applyPenalty).toHaveBeenCalledWith(input);
|
|
});
|
|
});
|
|
|
|
describe('getProtestDetailViewModel', () => {
|
|
it('should combine protest details + penalty types into a page-ready view model', async () => {
|
|
const leagueId = 'league-123';
|
|
const protestId = 'protest-1';
|
|
|
|
mockProtestService.getProtestById.mockResolvedValue({
|
|
protest: { id: protestId, raceId: 'race-1', protestingDriverId: 'd1', accusedDriverId: 'd2', status: 'pending', submittedAt: '2023-10-01T10:00:00Z', description: 'desc' } as any,
|
|
race: { id: 'race-1' } as any,
|
|
protestingDriver: { id: 'd1', name: 'Driver 1' } as any,
|
|
accusedDriver: { id: 'd2', name: 'Driver 2' } as any,
|
|
});
|
|
|
|
mockPenaltyService.getPenaltyTypesReference.mockResolvedValue({
|
|
penaltyTypes: [
|
|
{ type: 'time_penalty', requiresValue: true, valueKind: 'seconds' },
|
|
{ type: 'warning', requiresValue: false, valueKind: 'none' },
|
|
],
|
|
defaultReasons: { upheld: 'Upheld reason', dismissed: 'Dismissed reason' },
|
|
} as any);
|
|
|
|
const result = await service.getProtestDetailViewModel(leagueId, protestId);
|
|
|
|
expect(mockProtestService.getProtestById).toHaveBeenCalledWith(leagueId, protestId);
|
|
expect(mockPenaltyService.getPenaltyTypesReference).toHaveBeenCalled();
|
|
expect(result.protest.id).toBe(protestId);
|
|
expect(result.penaltyTypes.length).toBe(2);
|
|
expect(result.defaultReasons.upheld).toBe('Upheld reason');
|
|
expect(result.initialPenaltyType).toBe('time_penalty');
|
|
});
|
|
});
|
|
}); |