import { describe, it, expect } from 'vitest'; import { RaceResultsViewDataBuilder } from './RaceResultsViewDataBuilder'; describe('RaceResultsViewDataBuilder', () => { describe('happy paths', () => { it('should transform API DTO to RaceResultsViewData correctly', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [ { driverId: 'driver-2', driverName: 'Driver 2', type: 'time_penalty', value: 5, reason: 'Track limits', notes: 'Warning issued', }, ], pointsSystem: { 1: 25, 2: 18, 3: 15, }, fastestLapTime: 120000, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result).toEqual({ raceTrack: 'Test Track', raceScheduledAt: '2024-01-01T10:00:00Z', totalDrivers: 20, leagueName: 'Test League', raceSOF: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', driverAvatar: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [ { driverId: 'driver-2', driverName: 'Driver 2', type: 'time_penalty', value: 5, reason: 'Track limits', notes: 'Warning issued', }, ], pointsSystem: { 1: 25, 2: 18, 3: 15, }, fastestLapTime: 120000, }); }); it('should handle empty results and penalties', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 0, }, league: { name: 'Test League', }, strengthOfField: null, results: [], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results).toHaveLength(0); expect(result.penalties).toHaveLength(0); expect(result.raceSOF).toBeNull(); }); it('should handle multiple results and penalties', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, { position: 2, driverId: 'driver-2', driverName: 'Driver 2', avatarUrl: 'avatar-url', country: 'UK', car: 'Test Car', laps: 30, time: '1:24.000', fastestLap: '1:21.000', points: 18, incidents: 1, isCurrentUser: true, }, ], penalties: [ { driverId: 'driver-3', driverName: 'Driver 3', type: 'time_penalty', value: 5, reason: 'Track limits', notes: 'Warning issued', }, { driverId: 'driver-4', driverName: 'Driver 4', type: 'grid_penalty', value: 3, reason: 'Qualifying infringement', notes: null, }, ], pointsSystem: { 1: 25, 2: 18, 3: 15, }, fastestLapTime: 120000, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results).toHaveLength(2); expect(result.penalties).toHaveLength(2); }); }); describe('data transformation', () => { it('should preserve all DTO fields in the output', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: { 1: 25, 2: 18, 3: 15, }, fastestLapTime: 120000, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.raceTrack).toBe(apiDto.race.track); expect(result.raceScheduledAt).toBe(apiDto.race.scheduledAt); expect(result.totalDrivers).toBe(apiDto.stats.totalDrivers); expect(result.leagueName).toBe(apiDto.league.name); expect(result.raceSOF).toBe(apiDto.strengthOfField); expect(result.pointsSystem).toEqual(apiDto.pointsSystem); expect(result.fastestLapTime).toBe(apiDto.fastestLapTime); }); it('should not modify the input DTO', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const originalDto = { ...apiDto }; RaceResultsViewDataBuilder.build(apiDto); expect(apiDto).toEqual(originalDto); }); }); describe('edge cases', () => { it('should handle null API DTO', () => { const result = RaceResultsViewDataBuilder.build(null); expect(result.raceSOF).toBeNull(); expect(result.results).toHaveLength(0); expect(result.penalties).toHaveLength(0); expect(result.pointsSystem).toEqual({}); expect(result.fastestLapTime).toBe(0); }); it('should handle undefined API DTO', () => { const result = RaceResultsViewDataBuilder.build(undefined); expect(result.raceSOF).toBeNull(); expect(result.results).toHaveLength(0); expect(result.penalties).toHaveLength(0); expect(result.pointsSystem).toEqual({}); expect(result.fastestLapTime).toBe(0); }); it('should handle results without country', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: null, car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].country).toBe('US'); }); it('should handle results without car', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: null, laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].car).toBe('Unknown'); }); it('should handle results without laps', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: null, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].laps).toBe(0); }); it('should handle results without time', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: null, fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].time).toBe('0:00.00'); }); it('should handle results without fastest lap', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: null, points: 25, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].fastestLap).toBe('0.00'); }); it('should handle results without points', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: null, incidents: 0, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].points).toBe(0); }); it('should handle results without incidents', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: null, isCurrentUser: false, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].incidents).toBe(0); }); it('should handle results without isCurrentUser', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [ { position: 1, driverId: 'driver-1', driverName: 'Driver 1', avatarUrl: 'avatar-url', country: 'US', car: 'Test Car', laps: 30, time: '1:23.456', fastestLap: '1:20.000', points: 25, incidents: 0, isCurrentUser: null, }, ], penalties: [], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.results[0].isCurrentUser).toBe(false); }); it('should handle penalties without driver name', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [], penalties: [ { driverId: 'driver-1', driverName: null, type: 'time_penalty', value: 5, reason: 'Track limits', notes: 'Warning issued', }, ], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.penalties[0].driverName).toBe('Unknown'); }); it('should handle penalties without value', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [], penalties: [ { driverId: 'driver-1', driverName: 'Driver 1', type: 'time_penalty', value: null, reason: 'Track limits', notes: 'Warning issued', }, ], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.penalties[0].value).toBe(0); }); it('should handle penalties without reason', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [], penalties: [ { driverId: 'driver-1', driverName: 'Driver 1', type: 'time_penalty', value: 5, reason: null, notes: 'Warning issued', }, ], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.penalties[0].reason).toBe('Penalty applied'); }); it('should handle different penalty types', () => { const apiDto = { race: { track: 'Test Track', scheduledAt: '2024-01-01T10:00:00Z', }, stats: { totalDrivers: 20, }, league: { name: 'Test League', }, strengthOfField: 1500, results: [], penalties: [ { driverId: 'driver-1', driverName: 'Driver 1', type: 'grid_penalty', value: 3, reason: 'Qualifying infringement', notes: null, }, { driverId: 'driver-2', driverName: 'Driver 2', type: 'points_deduction', value: 10, reason: 'Dangerous driving', notes: null, }, { driverId: 'driver-3', driverName: 'Driver 3', type: 'disqualification', value: 0, reason: 'Technical infringement', notes: null, }, { driverId: 'driver-4', driverName: 'Driver 4', type: 'warning', value: 0, reason: 'Minor infraction', notes: null, }, { driverId: 'driver-5', driverName: 'Driver 5', type: 'license_points', value: 2, reason: 'Multiple incidents', notes: null, }, ], pointsSystem: {}, fastestLapTime: 0, }; const result = RaceResultsViewDataBuilder.build(apiDto); expect(result.penalties[0].type).toBe('grid_penalty'); expect(result.penalties[1].type).toBe('points_deduction'); expect(result.penalties[2].type).toBe('disqualification'); expect(result.penalties[3].type).toBe('warning'); expect(result.penalties[4].type).toBe('license_points'); }); }); });