/** * Integration Test: Driver Rankings Use Case Orchestration * * Tests the orchestration logic of driver rankings-related Use Cases: * - GetDriverRankingsUseCase: Retrieves comprehensive list of all drivers with search, filter, and sort capabilities * - 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 { GetDriverRankingsUseCase } from '../../../core/leaderboards/application/use-cases/GetDriverRankingsUseCase'; import { ValidationError } from '../../../core/shared/errors/ValidationError'; describe('Driver Rankings Use Case Orchestration', () => { let leaderboardsRepository: InMemoryLeaderboardsRepository; let eventPublisher: InMemoryLeaderboardsEventPublisher; let getDriverRankingsUseCase: GetDriverRankingsUseCase; beforeAll(() => { leaderboardsRepository = new InMemoryLeaderboardsRepository(); eventPublisher = new InMemoryLeaderboardsEventPublisher(); getDriverRankingsUseCase = new GetDriverRankingsUseCase({ leaderboardsRepository, eventPublisher, }); }); beforeEach(() => { leaderboardsRepository.clear(); eventPublisher.clear(); }); describe('GetDriverRankingsUseCase - Success Path', () => { it('should retrieve all drivers with complete data', async () => { // Scenario: System has multiple drivers with complete data // Given: Multiple drivers exist with various ratings, names, 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, }); // When: GetDriverRankingsUseCase.execute() is called with default query const result = await getDriverRankingsUseCase.execute({}); // Then: The result should contain all drivers expect(result.drivers).toHaveLength(3); // And: Each driver entry should include rank, name, rating, team affiliation, and race count 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: Drivers should be sorted by rating (highest first) expect(result.drivers[0].rating).toBe(5.0); expect(result.drivers[1].rating).toBe(4.8); expect(result.drivers[2].rating).toBe(4.5); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should retrieve drivers with pagination', async () => { // Scenario: System has many drivers requiring pagination // Given: More than 20 drivers exist for (let i = 1; i <= 25; i++) { leaderboardsRepository.addDriver({ id: `driver-${i}`, name: `Driver ${i}`, rating: 5.0 - i * 0.1, raceCount: 10 + i, }); } // When: GetDriverRankingsUseCase.execute() is called with page=1, limit=20 const result = await getDriverRankingsUseCase.execute({ page: 1, limit: 20 }); // Then: The result should contain 20 drivers expect(result.drivers).toHaveLength(20); // And: The result should include pagination metadata (total, page, limit) expect(result.pagination.total).toBe(25); expect(result.pagination.page).toBe(1); expect(result.pagination.limit).toBe(20); expect(result.pagination.totalPages).toBe(2); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should retrieve drivers with different page sizes', async () => { // Scenario: User requests different page sizes // Given: More than 50 drivers exist for (let i = 1; i <= 60; i++) { leaderboardsRepository.addDriver({ id: `driver-${i}`, name: `Driver ${i}`, rating: 5.0 - i * 0.1, raceCount: 10 + i, }); } // When: GetDriverRankingsUseCase.execute() is called with limit=50 const result = await getDriverRankingsUseCase.execute({ limit: 50 }); // Then: The result should contain 50 drivers expect(result.drivers).toHaveLength(50); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should retrieve drivers 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, }); // When: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.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: No duplicate ranks should appear const ranks = result.drivers.map((d) => d.rank); expect(new Set(ranks).size).toBe(ranks.length); // And: All ranks should be sequential for (let i = 0; i < ranks.length; i++) { expect(ranks[i]).toBe(i + 1); } // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should retrieve drivers with accurate data', async () => { // Scenario: Verify data accuracy // Given: Drivers exist with valid ratings, names, and team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, teamId: 'team-1', teamName: 'Racing Team A', raceCount: 50, }); // When: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.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 driver ranks should be sequential expect(result.drivers[0].rank).toBe(1); // And: All driver names should be non-empty strings expect(result.drivers[0].name).toBeTruthy(); expect(typeof result.drivers[0].name).toBe('string'); // And: All team affiliations should be valid expect(result.drivers[0].teamId).toBe('team-1'); expect(result.drivers[0].teamName).toBe('Racing Team A'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); }); describe('GetDriverRankingsUseCase - Search Functionality', () => { it('should search for drivers by name', async () => { // Scenario: User searches for a specific driver // Given: Drivers exist with names: "John Smith", "Jane Doe", "Bob Johnson" leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Jane Doe', rating: 4.8, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Bob Johnson', rating: 4.5, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with search="John" const result = await getDriverRankingsUseCase.execute({ search: 'John' }); // Then: The result should contain drivers whose names contain "John" expect(result.drivers).toHaveLength(2); expect(result.drivers.map((d) => d.name)).toContain('John Smith'); expect(result.drivers.map((d) => d.name)).toContain('Bob Johnson'); // And: The result should not contain drivers whose names do not contain "John" expect(result.drivers.map((d) => d.name)).not.toContain('Jane Doe'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should search for drivers by partial name', async () => { // Scenario: User searches with partial name // Given: Drivers exist with names: "Alexander", "Alex", "Alexandra" leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Alexander', rating: 5.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Alex', rating: 4.8, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Alexandra', rating: 4.5, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with search="Alex" const result = await getDriverRankingsUseCase.execute({ search: 'Alex' }); // Then: The result should contain all drivers whose names start with "Alex" expect(result.drivers).toHaveLength(3); expect(result.drivers.map((d) => d.name)).toContain('Alexander'); expect(result.drivers.map((d) => d.name)).toContain('Alex'); expect(result.drivers.map((d) => d.name)).toContain('Alexandra'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should handle case-insensitive search', async () => { // Scenario: Search is case-insensitive // Given: Drivers exist with names: "John Smith", "JOHN DOE", "johnson" leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'JOHN DOE', rating: 4.8, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'johnson', rating: 4.5, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with search="john" const result = await getDriverRankingsUseCase.execute({ search: 'john' }); // Then: The result should contain all drivers whose names contain "john" (case-insensitive) expect(result.drivers).toHaveLength(3); expect(result.drivers.map((d) => d.name)).toContain('John Smith'); expect(result.drivers.map((d) => d.name)).toContain('JOHN DOE'); expect(result.drivers.map((d) => d.name)).toContain('johnson'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should return empty result when no drivers match search', async () => { // Scenario: Search returns no results // Given: Drivers exist leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with search="NonExistentDriver" const result = await getDriverRankingsUseCase.execute({ search: 'NonExistentDriver' }); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); }); describe('GetDriverRankingsUseCase - Filter Functionality', () => { it('should filter drivers by rating range', async () => { // Scenario: User filters drivers by rating // Given: Drivers exist with ratings: 3.5, 4.0, 4.5, 5.0 leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 3.5, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-4', name: 'Driver D', rating: 5.0, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with minRating=4.0 const result = await getDriverRankingsUseCase.execute({ minRating: 4.0 }); // Then: The result should only contain drivers with rating >= 4.0 expect(result.drivers).toHaveLength(3); expect(result.drivers.every((d) => d.rating >= 4.0)).toBe(true); // And: Drivers with rating < 4.0 should not be visible expect(result.drivers.map((d) => d.name)).not.toContain('Driver A'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should filter drivers by team', async () => { // Scenario: User filters drivers by team // Given: Drivers exist with various team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 5.0, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.8, teamId: 'team-2', teamName: 'Team 2', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with teamId="team-1" const result = await getDriverRankingsUseCase.execute({ teamId: 'team-1' }); // Then: The result should only contain drivers from that team expect(result.drivers).toHaveLength(2); expect(result.drivers.every((d) => d.teamId === 'team-1')).toBe(true); // And: Drivers from other teams should not be visible expect(result.drivers.map((d) => d.name)).not.toContain('Driver B'); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should filter drivers by multiple criteria', async () => { // Scenario: User applies multiple filters // Given: Drivers exist with various ratings and team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 5.0, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.8, teamId: 'team-2', teamName: 'Team 2', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-4', name: 'Driver D', rating: 3.5, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with minRating=4.0 and teamId="team-1" const result = await getDriverRankingsUseCase.execute({ minRating: 4.0, teamId: 'team-1' }); // Then: The result should only contain drivers from that team with rating >= 4.0 expect(result.drivers).toHaveLength(2); expect(result.drivers.every((d) => d.teamId === 'team-1' && d.rating >= 4.0)).toBe(true); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should handle empty filter results', async () => { // Scenario: Filters return no results // Given: Drivers exist leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 3.5, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with minRating=10.0 (impossible) const result = await getDriverRankingsUseCase.execute({ minRating: 10.0 }); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); }); describe('GetDriverRankingsUseCase - Sort Functionality', () => { it('should sort drivers by rating (high to low)', async () => { // Scenario: User sorts drivers by rating // Given: Drivers exist with ratings: 3.5, 4.0, 4.5, 5.0 leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 3.5, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-4', name: 'Driver D', rating: 5.0, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with sortBy="rating", sortOrder="desc" const result = await getDriverRankingsUseCase.execute({ sortBy: 'rating', sortOrder: 'desc' }); // Then: The result should be sorted by rating in descending order expect(result.drivers[0].rating).toBe(5.0); expect(result.drivers[1].rating).toBe(4.5); expect(result.drivers[2].rating).toBe(4.0); expect(result.drivers[3].rating).toBe(3.5); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should sort drivers by name (A-Z)', async () => { // Scenario: User sorts drivers by name // Given: Drivers exist with names: "Zoe", "Alice", "Bob" leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Zoe', rating: 5.0, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Alice', rating: 4.8, raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Bob', rating: 4.5, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called with sortBy="name", sortOrder="asc" const result = await getDriverRankingsUseCase.execute({ sortBy: 'name', sortOrder: 'asc' }); // Then: The result should be sorted alphabetically 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 DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should sort drivers by rank (low to high)', async () => { // Scenario: User sorts drivers by rank // Given: Drivers exist with various ranks 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, }); // When: GetDriverRankingsUseCase.execute() is called with sortBy="rank", sortOrder="asc" const result = await getDriverRankingsUseCase.execute({ sortBy: 'rank', sortOrder: 'asc' }); // Then: The result should be sorted by rank in ascending order expect(result.drivers[0].rank).toBe(1); expect(result.drivers[1].rank).toBe(2); expect(result.drivers[2].rank).toBe(3); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should sort drivers by race count (high to low)', async () => { // Scenario: User sorts drivers by race count // Given: Drivers exist with various race counts leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 5.0, raceCount: 50, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.8, raceCount: 30, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Driver C', rating: 4.5, raceCount: 40, }); // When: GetDriverRankingsUseCase.execute() is called with sortBy="raceCount", sortOrder="desc" const result = await getDriverRankingsUseCase.execute({ sortBy: 'raceCount', sortOrder: 'desc' }); // Then: The result should be sorted by race count in descending order expect(result.drivers[0].raceCount).toBe(50); expect(result.drivers[1].raceCount).toBe(40); expect(result.drivers[2].raceCount).toBe(30); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); }); describe('GetDriverRankingsUseCase - Edge Cases', () => { it('should handle system with no drivers', async () => { // Scenario: System has no drivers // Given: No drivers exist in the system // When: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.execute({}); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).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: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.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 (e.g., 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 DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should handle drivers with no team affiliation', async () => { // Scenario: Drivers without team affiliation // Given: Drivers exist with and without team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'Driver A', rating: 5.0, teamId: 'team-1', teamName: 'Team 1', raceCount: 10, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'Driver B', rating: 4.8, raceCount: 10, }); // When: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.execute({}); // Then: All drivers should be returned expect(result.drivers).toHaveLength(2); // And: Drivers without team should show empty or default team value expect(result.drivers[0].teamId).toBe('team-1'); expect(result.drivers[0].teamName).toBe('Team 1'); expect(result.drivers[1].teamId).toBeUndefined(); expect(result.drivers[1].teamName).toBeUndefined(); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); it('should handle pagination with empty results', async () => { // Scenario: Pagination with no results // Given: No drivers exist // When: GetDriverRankingsUseCase.execute() is called with page=1, limit=20 const result = await getDriverRankingsUseCase.execute({ page: 1, limit: 20 }); // Then: The result should contain empty drivers list expect(result.drivers).toHaveLength(0); // And: Pagination metadata should show total=0 expect(result.pagination.total).toBe(0); expect(result.pagination.page).toBe(1); expect(result.pagination.limit).toBe(20); expect(result.pagination.totalPages).toBe(0); // And: EventPublisher should emit DriverRankingsAccessedEvent expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(1); }); }); describe('GetDriverRankingsUseCase - Error Handling', () => { it('should handle driver repository errors gracefully', async () => { // Scenario: Driver 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: GetDriverRankingsUseCase.execute() is called try { await getDriverRankingsUseCase.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.getDriverRankingsAccessedEventCount()).toBe(0); // Restore original method leaderboardsRepository.findAllDrivers = originalFindAllDrivers; }); it('should handle invalid query parameters', async () => { // Scenario: Invalid query parameters // Given: Invalid parameters (e.g., negative page, invalid sort field) // When: GetDriverRankingsUseCase.execute() is called with invalid parameters try { await getDriverRankingsUseCase.execute({ page: -1 }); // Should not reach here expect(true).toBe(false); } catch (error) { // Then: Should throw ValidationError expect(error).toBeInstanceOf(ValidationError); } // And: EventPublisher should NOT emit any events expect(eventPublisher.getDriverRankingsAccessedEventCount()).toBe(0); }); }); describe('Driver Rankings 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: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.execute({}); // Then: Driver rankings should be: // - Rank 1: Driver with rating 5.0 // - Rank 2: Driver with rating 4.8 // - Rank 3: Driver with rating 4.5 // - Rank 4: Driver with rating 4.2 // - Rank 5: Driver with rating 4.0 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 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: GetDriverRankingsUseCase.execute() is called const result = await getDriverRankingsUseCase.execute({}); // Then: Driver entry should include: // - Rank: Sequential number // - Name: Driver's full name // - Rating: Driver's rating (formatted) // - Team: Team name and logo (if available) // - Race Count: Number of races completed const driver = result.drivers[0]; expect(driver.rank).toBe(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 handle pagination metadata', async () => { // Scenario: Pagination metadata calculation // Given: 50 drivers exist for (let i = 1; i <= 50; i++) { leaderboardsRepository.addDriver({ id: `driver-${i}`, name: `Driver ${i}`, rating: 5.0 - i * 0.1, raceCount: 10 + i, }); } // When: GetDriverRankingsUseCase.execute() is called with page=2, limit=20 const result = await getDriverRankingsUseCase.execute({ page: 2, limit: 20 }); // Then: Pagination metadata should include: // - Total: 50 // - Page: 2 // - Limit: 20 // - Total Pages: 3 expect(result.pagination.total).toBe(50); expect(result.pagination.page).toBe(2); expect(result.pagination.limit).toBe(20); expect(result.pagination.totalPages).toBe(3); }); it('should correctly apply search, filter, and sort together', async () => { // Scenario: Combined query operations // Given: Drivers exist with various names, ratings, and team affiliations leaderboardsRepository.addDriver({ id: 'driver-1', name: 'John Smith', rating: 5.0, teamId: 'team-1', teamName: 'Team 1', raceCount: 50, }); leaderboardsRepository.addDriver({ id: 'driver-2', name: 'John Doe', rating: 4.8, teamId: 'team-2', teamName: 'Team 2', raceCount: 45, }); leaderboardsRepository.addDriver({ id: 'driver-3', name: 'Jane Jenkins', rating: 4.5, teamId: 'team-1', teamName: 'Team 1', raceCount: 40, }); leaderboardsRepository.addDriver({ id: 'driver-4', name: 'Bob Smith', rating: 3.5, teamId: 'team-1', teamName: 'Team 1', raceCount: 30, }); // When: GetDriverRankingsUseCase.execute() is called with: // - search: "John" // - minRating: 4.0 // - teamId: "team-1" // - sortBy: "rating" // - sortOrder: "desc" const result = await getDriverRankingsUseCase.execute({ search: 'John', minRating: 4.0, teamId: 'team-1', sortBy: 'rating', sortOrder: 'desc', }); // Then: The result should: // - Only contain drivers from team-1 // - Only contain drivers with rating >= 4.0 // - Only contain drivers whose names contain "John" // - Be sorted by rating in descending order expect(result.drivers).toHaveLength(1); expect(result.drivers[0].name).toBe('John Smith'); expect(result.drivers[0].teamId).toBe('team-1'); expect(result.drivers[0].rating).toBe(5.0); }); }); });