import { Team } from '@core/racing/domain/entities/Team'; import type { TeamRatingRepository } from '@core/racing/domain/repositories/TeamRatingRepository'; import type { TeamRepository } from '@core/racing/domain/repositories/TeamRepository'; import { TeamRatingSnapshot } from '@core/racing/domain/services/TeamRatingSnapshotCalculator'; import { TeamRatingValue } from '@core/racing/domain/value-objects/TeamRatingValue'; import type { Logger } from '@core/shared/domain/Logger'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { TeamRankingUseCase } from './TeamRankingUseCase'; // Mock repositories class MockTeamRatingRepository implements TeamRatingRepository { private snapshots: Map = new Map(); async findByTeamId(teamId: string): Promise { return this.snapshots.get(teamId) || null; } async save(snapshot: TeamRatingSnapshot): Promise { this.snapshots.set(snapshot.teamId, snapshot); return snapshot; } clear() { this.snapshots.clear(); } setSnapshot(teamId: string, snapshot: TeamRatingSnapshot) { this.snapshots.set(teamId, snapshot); } } class MockTeamRepository implements TeamRepository { private teams: Map = new Map(); async findById(id: string): Promise { return this.teams.get(id) || null; } async findAll(): Promise { return Array.from(this.teams.values()); } async findByLeagueId(leagueId: string): Promise { return Array.from(this.teams.values()).filter(t => t.leagues.some(l => l.toString() === leagueId) ); } async create(team: Team): Promise { this.teams.set(team.id, team); return team; } async update(team: Team): Promise { this.teams.set(team.id, team); return team; } async delete(id: string): Promise { this.teams.delete(id); } async exists(id: string): Promise { return this.teams.has(id); } clear() { this.teams.clear(); } setTeam(team: Team) { this.teams.set(team.id, team); } } // Mock logger const mockLogger: Logger = { info: () => {}, error: () => {}, warn: () => {}, debug: () => {}, }; describe('TeamRankingUseCase', () => { let useCase: TeamRankingUseCase; let mockRatingRepo: MockTeamRatingRepository; let mockTeamRepo: MockTeamRepository; beforeEach(() => { mockRatingRepo = new MockTeamRatingRepository(); mockTeamRepo = new MockTeamRepository(); useCase = new TeamRankingUseCase(mockRatingRepo, mockTeamRepo, mockLogger); }); afterEach(() => { mockRatingRepo.clear(); mockTeamRepo.clear(); }); describe('getAllTeamRankings', () => { it('should return empty array when no teams exist', async () => { const result = await useCase.getAllTeamRankings(); expect(result).toEqual([]); }); it('should return empty array when no rating snapshots exist', async () => { const team = Team.create({ id: 'team-123', name: 'Test Team', tag: 'TT', description: 'A test team', ownerId: 'driver-123', leagues: [], }); mockTeamRepo.setTeam(team); const result = await useCase.getAllTeamRankings(); expect(result).toEqual([]); }); it('should return single team ranking', async () => { const teamId = 'team-123'; const team = Team.create({ id: teamId, name: 'Test Team', tag: 'TT', description: 'A test team', ownerId: 'driver-123', leagues: [], }); mockTeamRepo.setTeam(team); const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(75), adminTrust: TeamRatingValue.create(80), overall: 76.5, lastUpdated: new Date('2024-01-01'), eventCount: 5, }; mockRatingRepo.setSnapshot(teamId, snapshot); const result = await useCase.getAllTeamRankings(); expect(result).toHaveLength(1); expect(result[0]).toEqual({ teamId, teamName: 'Test Team', drivingRating: 75, adminTrustRating: 80, overallRating: 76.5, eventCount: 5, lastUpdated: new Date('2024-01-01'), overallRank: 1, }); }); it('should return multiple teams sorted by overall rating', async () => { // Team 1 const team1 = Team.create({ id: 'team-1', name: 'Team Alpha', tag: 'TA', description: 'Alpha team', ownerId: 'driver-1', leagues: [], }); mockTeamRepo.setTeam(team1); mockRatingRepo.setSnapshot('team-1', { teamId: 'team-1', driving: TeamRatingValue.create(80), adminTrust: TeamRatingValue.create(70), overall: 77, lastUpdated: new Date('2024-01-01'), eventCount: 10, }); // Team 2 const team2 = Team.create({ id: 'team-2', name: 'Team Beta', tag: 'TB', description: 'Beta team', ownerId: 'driver-2', leagues: [], }); mockTeamRepo.setTeam(team2); mockRatingRepo.setSnapshot('team-2', { teamId: 'team-2', driving: TeamRatingValue.create(90), adminTrust: TeamRatingValue.create(85), overall: 88, lastUpdated: new Date('2024-01-02'), eventCount: 15, }); // Team 3 const team3 = Team.create({ id: 'team-3', name: 'Team Gamma', tag: 'TG', description: 'Gamma team', ownerId: 'driver-3', leagues: [], }); mockTeamRepo.setTeam(team3); mockRatingRepo.setSnapshot('team-3', { teamId: 'team-3', driving: TeamRatingValue.create(60), adminTrust: TeamRatingValue.create(65), overall: 61.5, lastUpdated: new Date('2024-01-03'), eventCount: 3, }); const result = await useCase.getAllTeamRankings(); expect(result).toHaveLength(3); // Should be sorted by overall rating descending expect(result[0]).toBeDefined(); expect(result[0]!.teamId).toBe('team-2'); expect(result[0]!.overallRank).toBe(1); expect(result[0]!.overallRating).toBe(88); expect(result[1]).toBeDefined(); expect(result[1]!.teamId).toBe('team-1'); expect(result[1]!.overallRank).toBe(2); expect(result[1]!.overallRating).toBe(77); expect(result[2]).toBeDefined(); expect(result[2]!.teamId).toBe('team-3'); expect(result[2]!.overallRank).toBe(3); expect(result[2]!.overallRating).toBe(61.5); }); it('should handle teams without snapshots gracefully', async () => { // Team with snapshot const team1 = Team.create({ id: 'team-1', name: 'Team With Rating', tag: 'TWR', description: 'Has rating', ownerId: 'driver-1', leagues: [], }); mockTeamRepo.setTeam(team1); mockRatingRepo.setSnapshot('team-1', { teamId: 'team-1', driving: TeamRatingValue.create(70), adminTrust: TeamRatingValue.create(70), overall: 70, lastUpdated: new Date('2024-01-01'), eventCount: 5, }); // Team without snapshot const team2 = Team.create({ id: 'team-2', name: 'Team Without Rating', tag: 'TWR', description: 'No rating', ownerId: 'driver-2', leagues: [], }); mockTeamRepo.setTeam(team2); const result = await useCase.getAllTeamRankings(); expect(result).toHaveLength(1); expect(result[0]).toBeDefined(); expect(result[0]!.teamId).toBe('team-1'); expect(result[0]!.overallRank).toBe(1); }); }); describe('getTeamRanking', () => { it('should return null when team does not exist', async () => { const result = await useCase.getTeamRanking('non-existent-team'); expect(result).toBeNull(); }); it('should return null when team exists but has no snapshot', async () => { const team = Team.create({ id: 'team-123', name: 'Test Team', tag: 'TT', description: 'A test team', ownerId: 'driver-123', leagues: [], }); mockTeamRepo.setTeam(team); const result = await useCase.getTeamRanking('team-123'); expect(result).toBeNull(); }); it('should return team ranking with correct rank', async () => { // Setup multiple teams const teams = [ { id: 'team-1', name: 'Team A', rating: 85 }, { id: 'team-2', name: 'Team B', rating: 90 }, { id: 'team-3', name: 'Team C', rating: 75 }, ]; for (const t of teams) { const team = Team.create({ id: t.id, name: t.name, tag: t.name.substring(0, 2).toUpperCase(), description: `${t.name} description`, ownerId: `driver-${t.id}`, leagues: [], }); mockTeamRepo.setTeam(team); mockRatingRepo.setSnapshot(t.id, { teamId: t.id, driving: TeamRatingValue.create(t.rating), adminTrust: TeamRatingValue.create(t.rating), overall: t.rating, lastUpdated: new Date('2024-01-01'), eventCount: 5, }); } // Get ranking for team-2 (should be rank 1 with rating 90) const result = await useCase.getTeamRanking('team-2'); expect(result).toBeDefined(); expect(result?.teamId).toBe('team-2'); expect(result?.teamName).toBe('Team B'); expect(result?.overallRating).toBe(90); expect(result?.overallRank).toBe(1); }); it('should calculate correct rank for middle team', async () => { // Setup teams const teams = [ { id: 'team-1', name: 'Team A', rating: 90 }, { id: 'team-2', name: 'Team B', rating: 80 }, { id: 'team-3', name: 'Team C', rating: 70 }, ]; for (const t of teams) { const team = Team.create({ id: t.id, name: t.name, tag: t.name.substring(0, 2).toUpperCase(), description: `${t.name} description`, ownerId: `driver-${t.id}`, leagues: [], }); mockTeamRepo.setTeam(team); mockRatingRepo.setSnapshot(t.id, { teamId: t.id, driving: TeamRatingValue.create(t.rating), adminTrust: TeamRatingValue.create(t.rating), overall: t.rating, lastUpdated: new Date('2024-01-01'), eventCount: 5, }); } // Get ranking for team-2 (should be rank 2) const result = await useCase.getTeamRanking('team-2'); expect(result).toBeDefined(); expect(result?.overallRank).toBe(2); }); it('should return complete team ranking data', async () => { const teamId = 'team-123'; const team = Team.create({ id: teamId, name: 'Complete Team', tag: 'CT', description: 'Complete team description', ownerId: 'driver-123', leagues: [], }); mockTeamRepo.setTeam(team); const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(82), adminTrust: TeamRatingValue.create(78), overall: 80.8, lastUpdated: new Date('2024-01-15T10:30:00Z'), eventCount: 25, }; mockRatingRepo.setSnapshot(teamId, snapshot); const result = await useCase.getTeamRanking(teamId); expect(result).toEqual({ teamId, teamName: 'Complete Team', drivingRating: 82, adminTrustRating: 78, overallRating: 80.8, eventCount: 25, lastUpdated: new Date('2024-01-15T10:30:00Z'), overallRank: 1, }); }); }); describe('error handling', () => { it('should handle repository errors gracefully', async () => { // Mock repository to throw error mockTeamRepo.findAll = async () => { throw new Error('Repository connection failed'); }; await expect(useCase.getAllTeamRankings()).rejects.toThrow('Repository connection failed'); }); }); });