import { describe, it, expect } from 'vitest'; import { LeagueDetailViewDataBuilder } from './LeagueDetailViewDataBuilder'; import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO'; import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO'; import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO'; import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO'; import type { RaceDTO } from '@/lib/types/generated/RaceDTO'; describe('LeagueDetailViewDataBuilder', () => { describe('happy paths', () => { it('should transform league DTOs to LeagueDetailViewData correctly', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Pro League', description: 'A competitive league for experienced drivers', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, qualifyingFormat: 'Solo • 32 max', }, usedSlots: 25, category: 'competitive', scoring: { gameId: 'game-1', gameName: 'iRacing', primaryChampionshipType: 'Single Championship', scoringPresetId: 'preset-1', scoringPresetName: 'Standard', dropPolicySummary: 'Drop 2 worst races', scoringPatternSummary: 'Points based on finish position', }, timingSummary: 'Weekly races on Sundays', logoUrl: 'https://example.com/logo.png', pendingJoinRequestsCount: 3, pendingProtestsCount: 1, walletBalance: 1000, }; const owner: GetDriverOutputDTO = { id: 'owner-1', name: 'John Doe', iracingId: '12345', country: 'USA', bio: 'Experienced driver', joinedAt: '2023-01-01T00:00:00.000Z', avatarUrl: 'https://example.com/avatar.jpg', }; const scoringConfig: LeagueScoringConfigDTO = { id: 'config-1', leagueId: 'league-1', gameId: 'game-1', gameName: 'iRacing', primaryChampionshipType: 'Single Championship', scoringPresetId: 'preset-1', scoringPresetName: 'Standard', dropPolicySummary: 'Drop 2 worst races', scoringPatternSummary: 'Points based on finish position', dropRaces: 2, pointsPerRace: 100, pointsForWin: 25, pointsForPodium: [20, 15, 10], }; const memberships: LeagueMembershipsDTO = { members: [ { driverId: 'driver-1', driver: { id: 'driver-1', name: 'Alice', iracingId: '11111', country: 'UK', joinedAt: '2023-06-01T00:00:00.000Z', }, role: 'admin', joinedAt: '2023-06-01T00:00:00.000Z', }, { driverId: 'driver-2', driver: { id: 'driver-2', name: 'Bob', iracingId: '22222', country: 'Germany', joinedAt: '2023-07-01T00:00:00.000Z', }, role: 'steward', joinedAt: '2023-07-01T00:00:00.000Z', }, { driverId: 'driver-3', driver: { id: 'driver-3', name: 'Charlie', iracingId: '33333', country: 'France', joinedAt: '2023-08-01T00:00:00.000Z', }, role: 'member', joinedAt: '2023-08-01T00:00:00.000Z', }, ], }; const races: RaceDTO[] = [ { id: 'race-1', name: 'Race 1', date: '2024-01-15T14:00:00.000Z', track: 'Spa', car: 'Porsche 911 GT3', sessionType: 'race', strengthOfField: 1500, }, { id: 'race-2', name: 'Race 2', date: '2024-01-22T14:00:00.000Z', track: 'Monza', car: 'Ferrari 488 GT3', sessionType: 'race', strengthOfField: 1600, }, ]; const sponsors: any[] = [ { id: 'sponsor-1', name: 'Sponsor A', tier: 'main', logoUrl: 'https://example.com/sponsor-a.png', websiteUrl: 'https://sponsor-a.com', tagline: 'Premium racing gear', }, ]; const result = LeagueDetailViewDataBuilder.build({ league, owner, scoringConfig, memberships, races, sponsors, }); expect(result.leagueId).toBe('league-1'); expect(result.name).toBe('Pro League'); expect(result.description).toBe('A competitive league for experienced drivers'); expect(result.logoUrl).toBe('https://example.com/logo.png'); expect(result.info.name).toBe('Pro League'); expect(result.info.description).toBe('A competitive league for experienced drivers'); expect(result.info.membersCount).toBe(3); expect(result.info.racesCount).toBe(2); expect(result.info.avgSOF).toBe(1550); expect(result.info.structure).toBe('Solo • 32 max'); expect(result.info.scoring).toBe('preset-1'); expect(result.info.createdAt).toBe('2024-01-01T00:00:00.000Z'); expect(result.info.discordUrl).toBeUndefined(); expect(result.info.youtubeUrl).toBeUndefined(); expect(result.info.websiteUrl).toBeUndefined(); expect(result.ownerSummary).not.toBeNull(); expect(result.ownerSummary?.driverId).toBe('owner-1'); expect(result.ownerSummary?.driverName).toBe('John Doe'); expect(result.ownerSummary?.avatarUrl).toBe('https://example.com/avatar.jpg'); expect(result.ownerSummary?.roleBadgeText).toBe('Owner'); expect(result.adminSummaries).toHaveLength(1); expect(result.adminSummaries[0].driverId).toBe('driver-1'); expect(result.adminSummaries[0].driverName).toBe('Alice'); expect(result.adminSummaries[0].roleBadgeText).toBe('Admin'); expect(result.stewardSummaries).toHaveLength(1); expect(result.stewardSummaries[0].driverId).toBe('driver-2'); expect(result.stewardSummaries[0].driverName).toBe('Bob'); expect(result.stewardSummaries[0].roleBadgeText).toBe('Steward'); expect(result.memberSummaries).toHaveLength(1); expect(result.memberSummaries[0].driverId).toBe('driver-3'); expect(result.memberSummaries[0].driverName).toBe('Charlie'); expect(result.memberSummaries[0].roleBadgeText).toBe('Member'); expect(result.sponsors).toHaveLength(1); expect(result.sponsors[0].id).toBe('sponsor-1'); expect(result.sponsors[0].name).toBe('Sponsor A'); expect(result.sponsors[0].tier).toBe('main'); expect(result.walletBalance).toBe(1000); expect(result.pendingProtestsCount).toBe(1); expect(result.pendingJoinRequestsCount).toBe(3); }); it('should handle league with no owner', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(result.ownerSummary).toBeNull(); }); it('should handle league with no scoring config', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(result.info.scoring).toBe('Standard'); }); it('should handle league with no races', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(result.info.racesCount).toBe(0); expect(result.info.avgSOF).toBeNull(); expect(result.runningRaces).toEqual([]); expect(result.nextRace).toBeUndefined(); expect(result.seasonProgress).toEqual({ completedRaces: 0, totalRaces: 0, percentage: 0, }); expect(result.recentResults).toEqual([]); }); }); describe('data transformation', () => { it('should preserve all DTO fields in the output', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, qualifyingFormat: 'Solo • 32 max', }, usedSlots: 20, category: 'test', scoring: { gameId: 'game-1', gameName: 'Test Game', primaryChampionshipType: 'Test Type', scoringPresetId: 'preset-1', scoringPresetName: 'Test Preset', dropPolicySummary: 'Test drop policy', scoringPatternSummary: 'Test pattern', }, timingSummary: 'Test timing', logoUrl: 'https://example.com/test.png', pendingJoinRequestsCount: 5, pendingProtestsCount: 2, walletBalance: 500, }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(result.leagueId).toBe(league.id); expect(result.name).toBe(league.name); expect(result.description).toBe(league.description); expect(result.logoUrl).toBe(league.logoUrl); expect(result.walletBalance).toBe(league.walletBalance); expect(result.pendingProtestsCount).toBe(league.pendingProtestsCount); expect(result.pendingJoinRequestsCount).toBe(league.pendingJoinRequestsCount); }); it('should not modify the input DTOs', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 20, }; const originalLeague = JSON.parse(JSON.stringify(league)); LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(league).toEqual(originalLeague); }); }); describe('edge cases', () => { it('should handle league with missing optional fields', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Minimal League', description: '', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races: [], sponsors: [], }); expect(result.description).toBe(''); expect(result.logoUrl).toBeUndefined(); expect(result.info.description).toBe(''); expect(result.info.discordUrl).toBeUndefined(); expect(result.info.youtubeUrl).toBeUndefined(); expect(result.info.websiteUrl).toBeUndefined(); }); it('should handle races with missing strengthOfField', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const races: RaceDTO[] = [ { id: 'race-1', name: 'Race 1', date: '2024-01-15T14:00:00.000Z', track: 'Spa', car: 'Porsche 911 GT3', sessionType: 'race', }, ]; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races, sponsors: [], }); expect(result.info.avgSOF).toBeNull(); }); it('should handle races with zero strengthOfField', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const races: RaceDTO[] = [ { id: 'race-1', name: 'Race 1', date: '2024-01-15T14:00:00.000Z', track: 'Spa', car: 'Porsche 911 GT3', sessionType: 'race', strengthOfField: 0, }, ]; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races, sponsors: [], }); expect(result.info.avgSOF).toBeNull(); }); it('should handle races with different dates for next race calculation', () => { const now = new Date(); const pastDate = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 1 day ago const futureDate = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 1 day from now const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const races: RaceDTO[] = [ { id: 'race-1', name: 'Past Race', date: pastDate.toISOString(), track: 'Spa', car: 'Porsche 911 GT3', sessionType: 'race', }, { id: 'race-2', name: 'Future Race', date: futureDate.toISOString(), track: 'Monza', car: 'Ferrari 488 GT3', sessionType: 'race', }, ]; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships: { members: [] }, races, sponsors: [], }); expect(result.nextRace).toBeDefined(); expect(result.nextRace?.id).toBe('race-2'); expect(result.nextRace?.name).toBe('Future Race'); expect(result.seasonProgress.completedRaces).toBe(1); expect(result.seasonProgress.totalRaces).toBe(2); expect(result.seasonProgress.percentage).toBe(50); expect(result.recentResults).toHaveLength(1); expect(result.recentResults[0].raceId).toBe('race-1'); }); it('should handle members with different roles', () => { const league: LeagueWithCapacityAndScoringDTO = { id: 'league-1', name: 'Test League', description: 'Test description', ownerId: 'owner-1', createdAt: '2024-01-01T00:00:00.000Z', settings: { maxDrivers: 32, }, usedSlots: 10, }; const memberships: LeagueMembershipsDTO = { members: [ { driverId: 'driver-1', driver: { id: 'driver-1', name: 'Admin', iracingId: '11111', country: 'UK', joinedAt: '2023-06-01T00:00:00.000Z', }, role: 'admin', joinedAt: '2023-06-01T00:00:00.000Z', }, { driverId: 'driver-2', driver: { id: 'driver-2', name: 'Steward', iracingId: '22222', country: 'Germany', joinedAt: '2023-07-01T00:00:00.000Z', }, role: 'steward', joinedAt: '2023-07-01T00:00:00.000Z', }, { driverId: 'driver-3', driver: { id: 'driver-3', name: 'Member', iracingId: '33333', country: 'France', joinedAt: '2023-08-01T00:00:00.000Z', }, role: 'member', joinedAt: '2023-08-01T00:00:00.000Z', }, ], }; const result = LeagueDetailViewDataBuilder.build({ league, owner: null, scoringConfig: null, memberships, races: [], sponsors: [], }); expect(result.adminSummaries).toHaveLength(1); expect(result.stewardSummaries).toHaveLength(1); expect(result.memberSummaries).toHaveLength(1); expect(result.info.membersCount).toBe(3); }); }); });