/** * Integration Test: Races Main Use Case Orchestration * * Tests the orchestration logic of races main page-related Use Cases: * - GetUpcomingRacesUseCase: Retrieves upcoming races for the main page * - GetRecentRaceResultsUseCase: Retrieves recent race results for the main page * - GetRaceDetailUseCase: Retrieves race details for navigation * - 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, afterAll, beforeEach } from 'vitest'; import { InMemoryRaceRepository } from '../../../adapters/races/persistence/inmemory/InMemoryRaceRepository'; import { InMemoryEventPublisher } from '../../../adapters/events/InMemoryEventPublisher'; import { GetUpcomingRacesUseCase } from '../../../core/races/use-cases/GetUpcomingRacesUseCase'; import { GetRecentRaceResultsUseCase } from '../../../core/races/use-cases/GetRecentRaceResultsUseCase'; import { GetRaceDetailUseCase } from '../../../core/races/use-cases/GetRaceDetailUseCase'; import { UpcomingRacesQuery } from '../../../core/races/ports/UpcomingRacesQuery'; import { RecentRaceResultsQuery } from '../../../core/races/ports/RecentRaceResultsQuery'; import { RaceDetailQuery } from '../../../core/races/ports/RaceDetailQuery'; describe('Races Main Use Case Orchestration', () => { let raceRepository: InMemoryRaceRepository; let eventPublisher: InMemoryEventPublisher; let getUpcomingRacesUseCase: GetUpcomingRacesUseCase; let getRecentRaceResultsUseCase: GetRecentRaceResultsUseCase; let getRaceDetailUseCase: GetRaceDetailUseCase; beforeAll(() => { // TODO: Initialize In-Memory repositories and event publisher // raceRepository = new InMemoryRaceRepository(); // eventPublisher = new InMemoryEventPublisher(); // getUpcomingRacesUseCase = new GetUpcomingRacesUseCase({ // raceRepository, // eventPublisher, // }); // getRecentRaceResultsUseCase = new GetRecentRaceResultsUseCase({ // raceRepository, // eventPublisher, // }); // getRaceDetailUseCase = new GetRaceDetailUseCase({ // raceRepository, // eventPublisher, // }); }); beforeEach(() => { // TODO: Clear all In-Memory repositories before each test // raceRepository.clear(); // eventPublisher.clear(); }); describe('GetUpcomingRacesUseCase - Success Path', () => { it('should retrieve upcoming races with complete information', async () => { // TODO: Implement test // Scenario: Driver views upcoming races // Given: Multiple upcoming races exist with different tracks, cars, and leagues // And: Each race has track name, date, time, car, and league // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should contain all upcoming races // And: Each race should display track name, date, time, car, and league // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races sorted by date', async () => { // TODO: Implement test // Scenario: Upcoming races are sorted by date // Given: Multiple upcoming races exist with different dates // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should be sorted by date (earliest first) // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with minimal information', async () => { // TODO: Implement test // Scenario: Upcoming races with minimal data // Given: Upcoming races exist with basic information only // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with league filtering', async () => { // TODO: Implement test // Scenario: Filter upcoming races by league // Given: Multiple upcoming races exist across different leagues // When: GetUpcomingRacesUseCase.execute() is called with league filter // Then: The result should contain only races from the specified league // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with car filtering', async () => { // TODO: Implement test // Scenario: Filter upcoming races by car // Given: Multiple upcoming races exist with different cars // When: GetUpcomingRacesUseCase.execute() is called with car filter // Then: The result should contain only races with the specified car // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with track filtering', async () => { // TODO: Implement test // Scenario: Filter upcoming races by track // Given: Multiple upcoming races exist at different tracks // When: GetUpcomingRacesUseCase.execute() is called with track filter // Then: The result should contain only races at the specified track // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with date range filtering', async () => { // TODO: Implement test // Scenario: Filter upcoming races by date range // Given: Multiple upcoming races exist across different dates // When: GetUpcomingRacesUseCase.execute() is called with date range // Then: The result should contain only races within the date range // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with pagination', async () => { // TODO: Implement test // Scenario: Paginate upcoming races // Given: Many upcoming races exist (more than page size) // When: GetUpcomingRacesUseCase.execute() is called with pagination // Then: The result should contain only the specified page of races // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with limit', async () => { // TODO: Implement test // Scenario: Limit upcoming races // Given: Many upcoming races exist // When: GetUpcomingRacesUseCase.execute() is called with limit // Then: The result should contain only the specified number of races // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should retrieve upcoming races with empty result when no races exist', async () => { // TODO: Implement test // Scenario: No upcoming races exist // Given: No upcoming races exist in the system // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should be empty // And: EventPublisher should emit UpcomingRacesAccessedEvent }); }); describe('GetUpcomingRacesUseCase - Edge Cases', () => { it('should handle races with missing track information', async () => { // TODO: Implement test // Scenario: Upcoming races with missing track data // Given: Upcoming races exist with missing track information // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should handle races with missing car information', async () => { // TODO: Implement test // Scenario: Upcoming races with missing car data // Given: Upcoming races exist with missing car information // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit UpcomingRacesAccessedEvent }); it('should handle races with missing league information', async () => { // TODO: Implement test // Scenario: Upcoming races with missing league data // Given: Upcoming races exist with missing league information // When: GetUpcomingRacesUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit UpcomingRacesAccessedEvent }); }); describe('GetUpcomingRacesUseCase - Error Handling', () => { it('should handle repository errors gracefully', async () => { // TODO: Implement test // Scenario: Repository throws error // Given: RaceRepository throws an error during query // When: GetUpcomingRacesUseCase.execute() is called // Then: Should propagate the error appropriately // And: EventPublisher should NOT emit any events }); it('should handle invalid pagination parameters', async () => { // TODO: Implement test // Scenario: Invalid pagination parameters // Given: Invalid page or pageSize values // When: GetUpcomingRacesUseCase.execute() is called with invalid parameters // Then: Should throw ValidationError // And: EventPublisher should NOT emit any events }); }); describe('GetRecentRaceResultsUseCase - Success Path', () => { it('should retrieve recent race results with complete information', async () => { // TODO: Implement test // Scenario: Driver views recent race results // Given: Multiple recent race results exist with different tracks, cars, and leagues // And: Each race has track name, date, winner, car, and league // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain all recent race results // And: Each race should display track name, date, winner, car, and league // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results sorted by date (newest first)', async () => { // TODO: Implement test // Scenario: Recent race results are sorted by date // Given: Multiple recent race results exist with different dates // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should be sorted by date (newest first) // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with minimal information', async () => { // TODO: Implement test // Scenario: Recent race results with minimal data // Given: Recent race results exist with basic information only // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with league filtering', async () => { // TODO: Implement test // Scenario: Filter recent race results by league // Given: Multiple recent race results exist across different leagues // When: GetRecentRaceResultsUseCase.execute() is called with league filter // Then: The result should contain only races from the specified league // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with car filtering', async () => { // TODO: Implement test // Scenario: Filter recent race results by car // Given: Multiple recent race results exist with different cars // When: GetRecentRaceResultsUseCase.execute() is called with car filter // Then: The result should contain only races with the specified car // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with track filtering', async () => { // TODO: Implement test // Scenario: Filter recent race results by track // Given: Multiple recent race results exist at different tracks // When: GetRecentRaceResultsUseCase.execute() is called with track filter // Then: The result should contain only races at the specified track // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with date range filtering', async () => { // TODO: Implement test // Scenario: Filter recent race results by date range // Given: Multiple recent race results exist across different dates // When: GetRecentRaceResultsUseCase.execute() is called with date range // Then: The result should contain only races within the date range // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with pagination', async () => { // TODO: Implement test // Scenario: Paginate recent race results // Given: Many recent race results exist (more than page size) // When: GetRecentRaceResultsUseCase.execute() is called with pagination // Then: The result should contain only the specified page of races // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with limit', async () => { // TODO: Implement test // Scenario: Limit recent race results // Given: Many recent race results exist // When: GetRecentRaceResultsUseCase.execute() is called with limit // Then: The result should contain only the specified number of races // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should retrieve recent race results with empty result when no races exist', async () => { // TODO: Implement test // Scenario: No recent race results exist // Given: No recent race results exist in the system // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should be empty // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); }); describe('GetRecentRaceResultsUseCase - Edge Cases', () => { it('should handle races with missing winner information', async () => { // TODO: Implement test // Scenario: Recent race results with missing winner data // Given: Recent race results exist with missing winner information // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should handle races with missing track information', async () => { // TODO: Implement test // Scenario: Recent race results with missing track data // Given: Recent race results exist with missing track information // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should handle races with missing car information', async () => { // TODO: Implement test // Scenario: Recent race results with missing car data // Given: Recent race results exist with missing car information // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); it('should handle races with missing league information', async () => { // TODO: Implement test // Scenario: Recent race results with missing league data // Given: Recent race results exist with missing league information // When: GetRecentRaceResultsUseCase.execute() is called // Then: The result should contain races with available information // And: EventPublisher should emit RecentRaceResultsAccessedEvent }); }); describe('GetRecentRaceResultsUseCase - Error Handling', () => { it('should handle repository errors gracefully', async () => { // TODO: Implement test // Scenario: Repository throws error // Given: RaceRepository throws an error during query // When: GetRecentRaceResultsUseCase.execute() is called // Then: Should propagate the error appropriately // And: EventPublisher should NOT emit any events }); it('should handle invalid pagination parameters', async () => { // TODO: Implement test // Scenario: Invalid pagination parameters // Given: Invalid page or pageSize values // When: GetRecentRaceResultsUseCase.execute() is called with invalid parameters // Then: Should throw ValidationError // And: EventPublisher should NOT emit any events }); }); describe('GetRaceDetailUseCase - Success Path', () => { it('should retrieve race detail with complete information', async () => { // TODO: Implement test // Scenario: Driver views race detail // Given: A race exists with complete information // And: The race has track, car, league, date, time, duration, status // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should contain complete race information // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with participants count', async () => { // TODO: Implement test // Scenario: Race with participants count // Given: A race exists with participants // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show participants count // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with winner and podium for completed races', async () => { // TODO: Implement test // Scenario: Completed race with winner and podium // Given: A completed race exists with winner and podium // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show winner and podium // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with track layout', async () => { // TODO: Implement test // Scenario: Race with track layout // Given: A race exists with track layout // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show track layout // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with weather information', async () => { // TODO: Implement test // Scenario: Race with weather information // Given: A race exists with weather information // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show weather information // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with race conditions', async () => { // TODO: Implement test // Scenario: Race with conditions // Given: A race exists with conditions // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show race conditions // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with statistics', async () => { // TODO: Implement test // Scenario: Race with statistics // Given: A race exists with statistics (lap count, incidents, penalties, protests, stewarding actions) // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show race statistics // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with lap times', async () => { // TODO: Implement test // Scenario: Race with lap times // Given: A race exists with lap times (average, fastest, best sectors) // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show lap times // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with qualifying results', async () => { // TODO: Implement test // Scenario: Race with qualifying results // Given: A race exists with qualifying results // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show qualifying results // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with starting grid', async () => { // TODO: Implement test // Scenario: Race with starting grid // Given: A race exists with starting grid // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show starting grid // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with points distribution', async () => { // TODO: Implement test // Scenario: Race with points distribution // Given: A race exists with points distribution // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show points distribution // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with championship implications', async () => { // TODO: Implement test // Scenario: Race with championship implications // Given: A race exists with championship implications // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show championship implications // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with highlights', async () => { // TODO: Implement test // Scenario: Race with highlights // Given: A race exists with highlights // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show highlights // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with video link', async () => { // TODO: Implement test // Scenario: Race with video link // Given: A race exists with video link // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show video link // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with gallery', async () => { // TODO: Implement test // Scenario: Race with gallery // Given: A race exists with gallery // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show gallery // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with description', async () => { // TODO: Implement test // Scenario: Race with description // Given: A race exists with description // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show description // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with rules', async () => { // TODO: Implement test // Scenario: Race with rules // Given: A race exists with rules // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show rules // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should retrieve race detail with requirements', async () => { // TODO: Implement test // Scenario: Race with requirements // Given: A race exists with requirements // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show requirements // And: EventPublisher should emit RaceDetailAccessedEvent }); }); describe('GetRaceDetailUseCase - Edge Cases', () => { it('should handle race with missing track information', async () => { // TODO: Implement test // Scenario: Race with missing track data // Given: A race exists with missing track information // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should contain race with available information // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with missing car information', async () => { // TODO: Implement test // Scenario: Race with missing car data // Given: A race exists with missing car information // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should contain race with available information // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with missing league information', async () => { // TODO: Implement test // Scenario: Race with missing league data // Given: A race exists with missing league information // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should contain race with available information // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle upcoming race without winner or podium', async () => { // TODO: Implement test // Scenario: Upcoming race without winner or podium // Given: An upcoming race exists (not completed) // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should not show winner or podium // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no statistics', async () => { // TODO: Implement test // Scenario: Race with no statistics // Given: A race exists with no statistics // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default statistics // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no lap times', async () => { // TODO: Implement test // Scenario: Race with no lap times // Given: A race exists with no lap times // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default lap times // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no qualifying results', async () => { // TODO: Implement test // Scenario: Race with no qualifying results // Given: A race exists with no qualifying results // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default qualifying results // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no highlights', async () => { // TODO: Implement test // Scenario: Race with no highlights // Given: A race exists with no highlights // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default highlights // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no video link', async () => { // TODO: Implement test // Scenario: Race with no video link // Given: A race exists with no video link // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default video link // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no gallery', async () => { // TODO: Implement test // Scenario: Race with no gallery // Given: A race exists with no gallery // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default gallery // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no description', async () => { // TODO: Implement test // Scenario: Race with no description // Given: A race exists with no description // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default description // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no rules', async () => { // TODO: Implement test // Scenario: Race with no rules // Given: A race exists with no rules // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default rules // And: EventPublisher should emit RaceDetailAccessedEvent }); it('should handle race with no requirements', async () => { // TODO: Implement test // Scenario: Race with no requirements // Given: A race exists with no requirements // When: GetRaceDetailUseCase.execute() is called with race ID // Then: The result should show empty or default requirements // And: EventPublisher should emit RaceDetailAccessedEvent }); }); describe('GetRaceDetailUseCase - Error Handling', () => { it('should throw error when race does not exist', async () => { // TODO: Implement test // Scenario: Non-existent race // Given: No race exists with the given ID // When: GetRaceDetailUseCase.execute() is called with non-existent race ID // Then: Should throw RaceNotFoundError // And: EventPublisher should NOT emit any events }); it('should throw error when race ID is invalid', async () => { // TODO: Implement test // Scenario: Invalid race ID // Given: An invalid race ID (e.g., empty string, null, undefined) // When: GetRaceDetailUseCase.execute() is called with invalid race ID // Then: Should throw ValidationError // And: EventPublisher should NOT emit any events }); it('should handle repository errors gracefully', async () => { // TODO: Implement test // Scenario: Repository throws error // Given: A race exists // And: RaceRepository throws an error during query // When: GetRaceDetailUseCase.execute() is called // Then: Should propagate the error appropriately // And: EventPublisher should NOT emit any events }); }); describe('Races Main Page Data Orchestration', () => { it('should correctly orchestrate data for main races page', async () => { // TODO: Implement test // Scenario: Main races page data orchestration // Given: Multiple upcoming races exist // And: Multiple recent race results exist // When: GetUpcomingRacesUseCase.execute() is called // And: GetRecentRaceResultsUseCase.execute() is called // Then: Both use cases should return their respective data // And: EventPublisher should emit appropriate events for each use case }); it('should correctly format race information for display', async () => { // TODO: Implement test // Scenario: Race information formatting // Given: A race exists with all information // When: GetRaceDetailUseCase.execute() is called // Then: The result should format: // - Track name: Clearly displayed // - Date: Formatted correctly // - Time: Formatted correctly // - Car: Clearly displayed // - League: Clearly displayed // - Status: Clearly indicated (Upcoming, In Progress, Completed) }); it('should correctly handle race status transitions', async () => { // TODO: Implement test // Scenario: Race status transitions // Given: A race exists with status "Upcoming" // When: Race status changes to "In Progress" // And: GetRaceDetailUseCase.execute() is called // Then: The result should show the updated status // And: EventPublisher should emit RaceDetailAccessedEvent }); }); });