842 lines
24 KiB
TypeScript
842 lines
24 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { RaceStewardingViewDataBuilder } from './RaceStewardingViewDataBuilder';
|
|
|
|
describe('RaceStewardingViewDataBuilder', () => {
|
|
describe('happy paths', () => {
|
|
it('should transform API DTO to RaceStewardingViewData correctly', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-123',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-456',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-2',
|
|
protestingDriverId: 'driver-3',
|
|
accusedDriverId: 'driver-4',
|
|
incident: {
|
|
lap: 10,
|
|
description: 'Contact at turn 5',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Penalty applied',
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-5',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
},
|
|
pendingCount: 1,
|
|
resolvedCount: 1,
|
|
penaltiesCount: 1,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result).toEqual({
|
|
race: {
|
|
id: 'race-123',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-456',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-2',
|
|
protestingDriverId: 'driver-3',
|
|
accusedDriverId: 'driver-4',
|
|
incident: {
|
|
lap: 10,
|
|
description: 'Contact at turn 5',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Penalty applied',
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-5',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
},
|
|
pendingCount: 1,
|
|
resolvedCount: 1,
|
|
penaltiesCount: 1,
|
|
});
|
|
});
|
|
|
|
it('should handle empty protests and penalties', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-456',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-789',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingProtests).toHaveLength(0);
|
|
expect(result.resolvedProtests).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.pendingCount).toBe(0);
|
|
expect(result.resolvedCount).toBe(0);
|
|
expect(result.penaltiesCount).toBe(0);
|
|
});
|
|
|
|
it('should handle multiple protests and penalties', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-789',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-101',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
{
|
|
id: 'protest-2',
|
|
protestingDriverId: 'driver-3',
|
|
accusedDriverId: 'driver-4',
|
|
incident: {
|
|
lap: 10,
|
|
description: 'Contact at turn 5',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-3',
|
|
protestingDriverId: 'driver-5',
|
|
accusedDriverId: 'driver-6',
|
|
incident: {
|
|
lap: 15,
|
|
description: 'Contact at turn 7',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Penalty applied',
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-7',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
{
|
|
id: 'penalty-2',
|
|
driverId: 'driver-8',
|
|
type: 'grid_penalty',
|
|
value: 3,
|
|
reason: 'Qualifying infringement',
|
|
notes: null,
|
|
},
|
|
],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
'driver-6': { id: 'driver-6', name: 'Driver 6' },
|
|
'driver-7': { id: 'driver-7', name: 'Driver 7' },
|
|
'driver-8': { id: 'driver-8', name: 'Driver 8' },
|
|
},
|
|
pendingCount: 2,
|
|
resolvedCount: 1,
|
|
penaltiesCount: 2,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingProtests).toHaveLength(2);
|
|
expect(result.resolvedProtests).toHaveLength(1);
|
|
expect(result.penalties).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe('data transformation', () => {
|
|
it('should preserve all DTO fields in the output', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-102',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-103',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
|
},
|
|
pendingCount: 1,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.race?.id).toBe(apiDto.race.id);
|
|
expect(result.race?.track).toBe(apiDto.race.track);
|
|
expect(result.race?.scheduledAt).toBe(apiDto.race.scheduledAt);
|
|
expect(result.league?.id).toBe(apiDto.league.id);
|
|
expect(result.pendingCount).toBe(apiDto.pendingCount);
|
|
expect(result.resolvedCount).toBe(apiDto.resolvedCount);
|
|
expect(result.penaltiesCount).toBe(apiDto.penaltiesCount);
|
|
});
|
|
|
|
it('should not modify the input DTO', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-104',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-105',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const originalDto = { ...apiDto };
|
|
RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(apiDto).toEqual(originalDto);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle null API DTO', () => {
|
|
const result = RaceStewardingViewDataBuilder.build(null);
|
|
|
|
expect(result.race).toBeNull();
|
|
expect(result.league).toBeNull();
|
|
expect(result.pendingProtests).toHaveLength(0);
|
|
expect(result.resolvedProtests).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.driverMap).toEqual({});
|
|
expect(result.pendingCount).toBe(0);
|
|
expect(result.resolvedCount).toBe(0);
|
|
expect(result.penaltiesCount).toBe(0);
|
|
});
|
|
|
|
it('should handle undefined API DTO', () => {
|
|
const result = RaceStewardingViewDataBuilder.build(undefined);
|
|
|
|
expect(result.race).toBeNull();
|
|
expect(result.league).toBeNull();
|
|
expect(result.pendingProtests).toHaveLength(0);
|
|
expect(result.resolvedProtests).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.driverMap).toEqual({});
|
|
expect(result.pendingCount).toBe(0);
|
|
expect(result.resolvedCount).toBe(0);
|
|
expect(result.penaltiesCount).toBe(0);
|
|
});
|
|
|
|
it('should handle race without league', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-106',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.league).toBeNull();
|
|
});
|
|
|
|
it('should handle protests without proof video', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-107',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-108',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: null,
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
|
},
|
|
pendingCount: 1,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingProtests[0].proofVideoUrl).toBeNull();
|
|
});
|
|
|
|
it('should handle protests without decision notes', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-109',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-110',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
penalties: [],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
|
},
|
|
pendingCount: 0,
|
|
resolvedCount: 1,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.resolvedProtests[0].decisionNotes).toBeNull();
|
|
});
|
|
|
|
it('should handle penalties without notes', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-111',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-112',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-1',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: null,
|
|
},
|
|
],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 1,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].notes).toBeNull();
|
|
});
|
|
|
|
it('should handle penalties without value', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-113',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-114',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-1',
|
|
type: 'disqualification',
|
|
value: null,
|
|
reason: 'Technical infringement',
|
|
notes: null,
|
|
},
|
|
],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 1,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].value).toBe(0);
|
|
});
|
|
|
|
it('should handle penalties without reason', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-115',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-116',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-1',
|
|
type: 'warning',
|
|
value: 0,
|
|
reason: null,
|
|
notes: null,
|
|
},
|
|
],
|
|
driverMap: {
|
|
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
|
},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 1,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].reason).toBe('');
|
|
});
|
|
|
|
it('should handle different protest statuses', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-117',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-118',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-2',
|
|
protestingDriverId: 'driver-3',
|
|
accusedDriverId: 'driver-4',
|
|
incident: {
|
|
lap: 10,
|
|
description: 'Contact at turn 5',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Penalty applied',
|
|
},
|
|
{
|
|
id: 'protest-3',
|
|
protestingDriverId: 'driver-5',
|
|
accusedDriverId: 'driver-6',
|
|
incident: {
|
|
lap: 15,
|
|
description: 'Contact at turn 7',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'rejected',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Insufficient evidence',
|
|
},
|
|
],
|
|
penalties: [],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
'driver-6': { id: 'driver-6', name: 'Driver 6' },
|
|
},
|
|
pendingCount: 1,
|
|
resolvedCount: 2,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingProtests[0].status).toBe('pending');
|
|
expect(result.resolvedProtests[0].status).toBe('resolved');
|
|
expect(result.resolvedProtests[1].status).toBe('rejected');
|
|
});
|
|
|
|
it('should handle different penalty types', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-119',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-120',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-1',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
{
|
|
id: 'penalty-2',
|
|
driverId: 'driver-2',
|
|
type: 'grid_penalty',
|
|
value: 3,
|
|
reason: 'Qualifying infringement',
|
|
notes: null,
|
|
},
|
|
{
|
|
id: 'penalty-3',
|
|
driverId: 'driver-3',
|
|
type: 'points_deduction',
|
|
value: 10,
|
|
reason: 'Dangerous driving',
|
|
notes: null,
|
|
},
|
|
{
|
|
id: 'penalty-4',
|
|
driverId: 'driver-4',
|
|
type: 'disqualification',
|
|
value: 0,
|
|
reason: 'Technical infringement',
|
|
notes: null,
|
|
},
|
|
{
|
|
id: 'penalty-5',
|
|
driverId: 'driver-5',
|
|
type: 'warning',
|
|
value: 0,
|
|
reason: 'Minor infraction',
|
|
notes: null,
|
|
},
|
|
{
|
|
id: 'penalty-6',
|
|
driverId: 'driver-6',
|
|
type: 'license_points',
|
|
value: 2,
|
|
reason: 'Multiple incidents',
|
|
notes: null,
|
|
},
|
|
],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
'driver-6': { id: 'driver-6', name: 'Driver 6' },
|
|
},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 6,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].type).toBe('time_penalty');
|
|
expect(result.penalties[1].type).toBe('grid_penalty');
|
|
expect(result.penalties[2].type).toBe('points_deduction');
|
|
expect(result.penalties[3].type).toBe('disqualification');
|
|
expect(result.penalties[4].type).toBe('warning');
|
|
expect(result.penalties[5].type).toBe('license_points');
|
|
});
|
|
|
|
it('should handle empty driver map', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-121',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-122',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {},
|
|
pendingCount: 0,
|
|
resolvedCount: 0,
|
|
penaltiesCount: 0,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.driverMap).toEqual({});
|
|
});
|
|
|
|
it('should handle count values from DTO', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-123',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-124',
|
|
},
|
|
pendingProtests: [],
|
|
resolvedProtests: [],
|
|
penalties: [],
|
|
driverMap: {},
|
|
pendingCount: 5,
|
|
resolvedCount: 10,
|
|
penaltiesCount: 3,
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingCount).toBe(5);
|
|
expect(result.resolvedCount).toBe(10);
|
|
expect(result.penaltiesCount).toBe(3);
|
|
});
|
|
|
|
it('should calculate counts from arrays when not provided', () => {
|
|
const apiDto = {
|
|
race: {
|
|
id: 'race-125',
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
league: {
|
|
id: 'league-126',
|
|
},
|
|
pendingProtests: [
|
|
{
|
|
id: 'protest-1',
|
|
protestingDriverId: 'driver-1',
|
|
accusedDriverId: 'driver-2',
|
|
incident: {
|
|
lap: 5,
|
|
description: 'Contact at turn 3',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'pending',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: null,
|
|
},
|
|
],
|
|
resolvedProtests: [
|
|
{
|
|
id: 'protest-2',
|
|
protestingDriverId: 'driver-3',
|
|
accusedDriverId: 'driver-4',
|
|
incident: {
|
|
lap: 10,
|
|
description: 'Contact at turn 5',
|
|
},
|
|
filedAt: '2024-01-01T10:00:00Z',
|
|
status: 'resolved',
|
|
proofVideoUrl: 'video-url',
|
|
decisionNotes: 'Penalty applied',
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
id: 'penalty-1',
|
|
driverId: 'driver-5',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
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' },
|
|
'driver-5': { id: 'driver-5', name: 'Driver 5' },
|
|
},
|
|
};
|
|
|
|
const result = RaceStewardingViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.pendingCount).toBe(1);
|
|
expect(result.resolvedCount).toBe(1);
|
|
expect(result.penaltiesCount).toBe(1);
|
|
});
|
|
});
|
|
});
|