/** * Integration Test: Global Leaderboards Use Case Orchestration * * Tests the orchestration logic of global leaderboards-related Use Cases: * - GetGlobalLeaderboardsUseCase: Retrieves top drivers and teams for the main leaderboards page * - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers) * - Uses In-Memory adapters for fast, deterministic testing * * Focus: Business logic orchestration, NOT UI rendering */ import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import { InMemoryLeaderboardsRepository } from '../../../adapters/leaderboards/persistence/inmemory/InMemoryLeaderboardsRepository'; import { InMemoryLeaderboardsEventPublisher } from '../../../adapters/leaderboards/events/InMemoryLeaderboardsEventPublisher'; import { GetGlobalLeaderboardsUseCase } from '../../../core/leaderboards/application/use-cases/GetGlobalLeaderboardsUseCase'; describe('Global Leaderboards Use Case Orchestration', () => { let leaderboardsRepository: InMemoryLeaderboardsRepository; let eventPublisher: InMemoryLeaderboardsEventPublisher; let getGlobalLeaderboardsUseCase: GetGlobalLeaderboardsUseCase; beforeAll(() => { leaderboardsRepository = new InMemoryLeaderboardsRepository(); eventPublisher = new InMemoryLeaderboardsEventPublisher(); getGlobalLeaderboardsUseCase = new GetGlobalLeaderboardsUseCase({ leaderboardsRepository, eventPublisher, }); }); beforeEach(() => { leaderboardsRepository.clear(); eventPublisher.clear(); }); describe('GetGlobalLeaderboardsUseCase - Success Path', () => { it('should retrieve top drivers and teams with complete data', async () => { // Scenario: System has multiple drivers and teams with complete data // Given: Multiple drivers exist with various ratings and team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, teamId: 'team-1', teamName: 'Racing Team A', raceCount: 50, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Jane Doe', rating: 4.8, teamId: 'team-2', teamName: 'Speed Squad', raceCount: 45, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Bob Johnson', rating: 4.5, teamId: 'team-1', teamName: 'Racing Team A', raceCount: 40, }); // And: Multiple teams exist with various ratings and member counts leaderboardsRepository.addTeam({ id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 5, raceCount: 100, }); leaderboardsRepository.addTeam({ id: 'team-2', name: 'Speed Squad', rating: 4.7, memberCount: 3, raceCount: 80, }); leaderboardsRepository.addTeam({ id: 'team-3', name: 'Champions League', rating: 4.3, memberCount: 4, raceCount: 60, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain top 10 drivers (but we only have 3) expect(result.drivers).toHaveLength(3); // And: The result should contain top 10 teams (but we only have 3) expect(result.teams).toHaveLength(3); // And: Driver entries should include rank, name, rating, and team affiliation expect(result.drivers[0]).toMatchObject({ rank: 1, id: 'driver-1', name: 'John Smith', rating: 5.0, teamId: 'team-1', teamName: 'Racing Team A', raceCount: 50, }); // And: Team entries should include rank, name, rating, and member count expect(result.teams[0]).toMatchObject({ rank: 1, id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 5, raceCount: 100, }); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should retrieve top drivers and teams with minimal data', async () => { // Scenario: System has minimal data // Given: Only a few drivers exist leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 10, }); // And: Only a few teams exist leaderboardsRepository.addTeam({ id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 2, raceCount: 20, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain all available drivers expect(result.drivers).toHaveLength(1); expect(result.drivers[0].name).toBe('John Smith'); // And: The result should contain all available teams expect(result.teams).toHaveLength(1); expect(result.teams[0].name).toBe('Racing Team A'); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should retrieve top drivers and teams when there are many', async () => { // Scenario: System has many drivers and teams // Given: More than 10 drivers exist for (let i = 1; i <= 15; i++) { leaderboardsRepository.addDriver({ id: `driver-${i}`, name: `Driver ${i}`, rating: 5.0 - i * 0.1, raceCount: 10 + i, }); } // And: More than 10 teams exist for (let i = 1; i <= 15; i++) { leaderboardsRepository.addTeam({ id: `team-${i}`, name: `Team ${i}`, rating: 5.0 - i * 0.1, memberCount: 2 + i, raceCount: 20 + i, }); } // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain only top 10 drivers expect(result.drivers).toHaveLength(10); // And: The result should contain only top 10 teams expect(result.teams).toHaveLength(10); // And: Drivers should be sorted by rating (highest first) expect(result.drivers[0].rating).toBe(4.9); // Driver 1 expect(result.drivers[9].rating).toBe(4.0); // Driver 10 // And: Teams should be sorted by rating (highest first) expect(result.teams[0].rating).toBe(4.9); // Team 1 expect(result.teams[9].rating).toBe(4.0); // Team 10 // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should retrieve top drivers and teams with consistent ranking order', async () => { // Scenario: Verify ranking consistency // Given: Multiple drivers exist with various ratings leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 5.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.8, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, raceCount: 10, }); // And: Multiple teams exist with various ratings leaderboardsRepository.addTeam({ id: 'team-1', name: 'Team A', rating: 4.9, memberCount: 2, raceCount: 20, }); leaderboardsRepository.addTeam({ id: 'team-2', name: 'Team B', rating: 4.7, memberCount: 2, raceCount: 20, }); leaderboardsRepository.addTeam({ id: 'team-3', name: 'Team C', rating: 4.3, memberCount: 2, raceCount: 20, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Driver ranks should be sequential (1, 2, 3...) expect(result.drivers[0].rank).toBe(1); expect(result.drivers[1].rank).toBe(2); expect(result.drivers[2].rank).toBe(3); // And: Team ranks should be sequential (1, 2, 3...) expect(result.teams[0].rank).toBe(1); expect(result.teams[1].rank).toBe(2); expect(result.teams[2].rank).toBe(3); // And: No duplicate ranks should appear const driverRanks = result.drivers.map((d) => d.rank); const teamRanks = result.teams.map((t) => t.rank); expect(new Set(driverRanks).size).toBe(driverRanks.length); expect(new Set(teamRanks).size).toBe(teamRanks.length); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should retrieve top drivers and teams with accurate data', async () => { // Scenario: Verify data accuracy // Given: Drivers exist with valid ratings and names leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 50, }); // And: Teams exist with valid ratings and member counts leaderboardsRepository.addTeam({ id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 5, raceCount: 100, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: All driver ratings should be valid numbers expect(result.drivers[0].rating).toBeGreaterThan(0); expect(typeof result.drivers[0].rating).toBe('number'); // And: All team ratings should be valid numbers expect(result.teams[0].rating).toBeGreaterThan(0); expect(typeof result.teams[0].rating).toBe('number'); // And: All team member counts should be valid numbers expect(result.teams[0].memberCount).toBeGreaterThan(0); expect(typeof result.teams[0].memberCount).toBe('number'); // And: All names should be non-empty strings expect(result.drivers[0].name).toBeTruthy(); expect(typeof result.drivers[0].name).toBe('string'); expect(result.teams[0].name).toBeTruthy(); expect(typeof result.teams[0].name).toBe('string'); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); }); describe('GetGlobalLeaderboardsUseCase - Edge Cases', () => { it('should handle system with no drivers', async () => { // Scenario: System has no drivers // Given: No drivers exist in the system // And: Teams exist leaderboardsRepository.addTeam({ id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 5, raceCount: 100, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: The result should contain top teams expect(result.teams).toHaveLength(1); expect(result.teams[0].name).toBe('Racing Team A'); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should handle system with no teams', async () => { // Scenario: System has no teams // Given: Drivers exist leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 50, }); // And: No teams exist in the system // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain top drivers expect(result.drivers).toHaveLength(1); expect(result.drivers[0].name).toBe('John Smith'); // And: The result should contain empty teams list expect(result.teams).toHaveLength(0); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should handle system with no data at all', async () => { // Scenario: System has absolutely no data // Given: No drivers exist // And: No teams exist // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: The result should contain empty teams list expect(result.teams).toHaveLength(0); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should handle drivers with same rating', async () => { // Scenario: Multiple drivers with identical ratings // Given: Multiple drivers exist with the same rating leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Zoe', rating: 5.0, raceCount: 50, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Alice', rating: 5.0, raceCount: 45, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Bob', rating: 5.0, raceCount: 40, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Drivers should be sorted by rating expect(result.drivers[0].rating).toBe(5.0); expect(result.drivers[1].rating).toBe(5.0); expect(result.drivers[2].rating).toBe(5.0); // And: Drivers with same rating should have consistent ordering (by name) expect(result.drivers[0].name).toBe('Alice'); expect(result.drivers[1].name).toBe('Bob'); expect(result.drivers[2].name).toBe('Zoe'); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); it('should handle teams with same rating', async () => { // Scenario: Multiple teams with identical ratings // Given: Multiple teams exist with the same rating leaderboardsRepository.addTeam({ id: 'team-1', name: 'Zeta Team', rating: 4.9, memberCount: 5, raceCount: 100, }); leaderboardsRepository.addTeam({ id: 'team-2', name: 'Alpha Team', rating: 4.9, memberCount: 3, raceCount: 80, }); leaderboardsRepository.addTeam({ id: 'team-3', name: 'Beta Team', rating: 4.9, memberCount: 4, raceCount: 60, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Teams should be sorted by rating expect(result.teams[0].rating).toBe(4.9); expect(result.teams[1].rating).toBe(4.9); expect(result.teams[2].rating).toBe(4.9); // And: Teams with same rating should have consistent ordering (by name) expect(result.teams[0].name).toBe('Alpha Team'); expect(result.teams[1].name).toBe('Beta Team'); expect(result.teams[2].name).toBe('Zeta Team'); // And: EventPublisher should emit GlobalLeaderboardsAccessedEvent expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(1); }); }); describe('GetGlobalLeaderboardsUseCase - Error Handling', () => { it('should handle repository errors gracefully', async () => { // Scenario: Repository throws error // Given: LeaderboardsRepository throws an error during query const originalFindAllDrivers = leaderboardsRepository.findAllDrivers.bind(leaderboardsRepository); leaderboardsRepository.findAllDrivers = async () => { throw new Error('Repository error'); }; // When: GetGlobalLeaderboardsUseCase.execute() is called try { await getGlobalLeaderboardsUseCase.execute(); // Should not reach here expect(true).toBe(false); } catch (error) { // Then: Should propagate the error appropriately expect(error).toBeInstanceOf(Error); expect((error as Error).message).toBe('Repository error'); } // And: EventPublisher should NOT emit any events expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(0); // Restore original method leaderboardsRepository.findAllDrivers = originalFindAllDrivers; }); it('should handle team repository errors gracefully', async () => { // Scenario: Team repository throws error // Given: LeaderboardsRepository throws an error during query const originalFindAllTeams = leaderboardsRepository.findAllTeams.bind(leaderboardsRepository); leaderboardsRepository.findAllTeams = async () => { throw new Error('Team repository error'); }; // When: GetGlobalLeaderboardsUseCase.execute() is called try { await getGlobalLeaderboardsUseCase.execute(); // Should not reach here expect(true).toBe(false); } catch (error) { // Then: Should propagate the error appropriately expect(error).toBeInstanceOf(Error); expect((error as Error).message).toBe('Team repository error'); } // And: EventPublisher should NOT emit any events expect(eventPublisher.getGlobalLeaderboardsAccessedEventCount()).toBe(0); // Restore original method leaderboardsRepository.findAllTeams = originalFindAllTeams; }); }); describe('Global Leaderboards Data Orchestration', () => { it('should correctly calculate driver rankings based on rating', async () => { // Scenario: Driver ranking calculation // Given: Drivers exist with ratings: 5.0, 4.8, 4.5, 4.2, 4.0 const ratings = [5.0, 4.8, 4.5, 4.2, 4.0]; ratings.forEach((rating, index) => { leaderboardsRepository.addDriver({ id: `driver-${index}`, name: `Driver ${index}`, rating, raceCount: 10 + index, }); }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Driver rankings should be correct expect(result.drivers[0].rank).toBe(1); expect(result.drivers[0].rating).toBe(5.0); expect(result.drivers[1].rank).toBe(2); expect(result.drivers[1].rating).toBe(4.8); expect(result.drivers[2].rank).toBe(3); expect(result.drivers[2].rating).toBe(4.5); expect(result.drivers[3].rank).toBe(4); expect(result.drivers[3].rating).toBe(4.2); expect(result.drivers[4].rank).toBe(5); expect(result.drivers[4].rating).toBe(4.0); }); it('should correctly calculate team rankings based on rating', async () => { // Scenario: Team ranking calculation // Given: Teams exist with ratings: 4.9, 4.7, 4.6, 4.3, 4.1 const ratings = [4.9, 4.7, 4.6, 4.3, 4.1]; ratings.forEach((rating, index) => { leaderboardsRepository.addTeam({ id: `team-${index}`, name: `Team ${index}`, rating, memberCount: 2 + index, raceCount: 20 + index, }); }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Team rankings should be correct expect(result.teams[0].rank).toBe(1); expect(result.teams[0].rating).toBe(4.9); expect(result.teams[1].rank).toBe(2); expect(result.teams[1].rating).toBe(4.7); expect(result.teams[2].rank).toBe(3); expect(result.teams[2].rating).toBe(4.6); expect(result.teams[3].rank).toBe(4); expect(result.teams[3].rating).toBe(4.3); expect(result.teams[4].rank).toBe(5); expect(result.teams[4].rating).toBe(4.1); }); it('should correctly format driver entries with team affiliation', async () => { // Scenario: Driver entry formatting // Given: A driver exists with team affiliation leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, teamId: 'team-1', teamName: 'Racing Team A', raceCount: 50, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Driver entry should include all required fields const driver = result.drivers[0]; expect(driver.rank).toBe(1); expect(driver.id).toBe('driver-1'); expect(driver.name).toBe('John Smith'); expect(driver.rating).toBe(5.0); expect(driver.teamId).toBe('team-1'); expect(driver.teamName).toBe('Racing Team A'); expect(driver.raceCount).toBe(50); }); it('should correctly format team entries with member count', async () => { // Scenario: Team entry formatting // Given: A team exists with members leaderboardsRepository.addTeam({ id: 'team-1', name: 'Racing Team A', rating: 4.9, memberCount: 5, raceCount: 100, }); // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Team entry should include all required fields const team = result.teams[0]; expect(team.rank).toBe(1); expect(team.id).toBe('team-1'); expect(team.name).toBe('Racing Team A'); expect(team.rating).toBe(4.9); expect(team.memberCount).toBe(5); expect(team.raceCount).toBe(100); }); it('should limit results to top 10 drivers and teams', async () => { // Scenario: Result limiting // Given: More than 10 drivers exist for (let i = 1; i <= 15; i++) { leaderboardsRepository.addDriver({ id: `driver-${i}`, name: `Driver ${i}`, rating: 5.0 - i * 0.1, raceCount: 10 + i, }); } // And: More than 10 teams exist for (let i = 1; i <= 15; i++) { leaderboardsRepository.addTeam({ id: `team-${i}`, name: `Team ${i}`, rating: 5.0 - i * 0.1, memberCount: 2 + i, raceCount: 20 + i, }); } // When: GetGlobalLeaderboardsUseCase.execute() is called const result = await getGlobalLeaderboardsUseCase.execute(); // Then: Only top 10 drivers should be returned expect(result.drivers).toHaveLength(10); // And: Only top 10 teams should be returned expect(result.teams).toHaveLength(10); // And: Results should be sorted by rating (highest first) expect(result.drivers[0].rating).toBe(4.9); // Driver 1 expect(result.drivers[9].rating).toBe(4.0); // Driver 10 expect(result.teams[0].rating).toBe(4.9); // Team 1 expect(result.teams[9].rating).toBe(4.0); // Team 10 }); }); });