integration tests
This commit is contained in:
@@ -3,767 +3,143 @@
|
||||
*
|
||||
* Tests the orchestration logic of race detail page-related Use Cases:
|
||||
* - GetRaceDetailUseCase: Retrieves comprehensive race details
|
||||
* - GetRaceParticipantsUseCase: Retrieves race participants count
|
||||
* - GetRaceWinnerUseCase: Retrieves race winner and podium
|
||||
* - GetRaceStatisticsUseCase: Retrieves race statistics
|
||||
* - GetRaceLapTimesUseCase: Retrieves race lap times
|
||||
* - GetRaceQualifyingUseCase: Retrieves race qualifying results
|
||||
* - GetRacePointsUseCase: Retrieves race points distribution
|
||||
* - GetRaceHighlightsUseCase: Retrieves race highlights
|
||||
* - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
|
||||
* - Uses In-Memory adapters for fast, deterministic testing
|
||||
*
|
||||
* Adheres to Clean Architecture:
|
||||
* - Tests Core Use Cases directly
|
||||
* - Uses In-Memory adapters for repositories
|
||||
* - Follows Given/When/Then pattern
|
||||
*
|
||||
* 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 { GetRaceDetailUseCase } from '../../../core/races/use-cases/GetRaceDetailUseCase';
|
||||
import { GetRaceParticipantsUseCase } from '../../../core/races/use-cases/GetRaceParticipantsUseCase';
|
||||
import { GetRaceWinnerUseCase } from '../../../core/races/use-cases/GetRaceWinnerUseCase';
|
||||
import { GetRaceStatisticsUseCase } from '../../../core/races/use-cases/GetRaceStatisticsUseCase';
|
||||
import { GetRaceLapTimesUseCase } from '../../../core/races/use-cases/GetRaceLapTimesUseCase';
|
||||
import { GetRaceQualifyingUseCase } from '../../../core/races/use-cases/GetRaceQualifyingUseCase';
|
||||
import { GetRacePointsUseCase } from '../../../core/races/use-cases/GetRacePointsUseCase';
|
||||
import { GetRaceHighlightsUseCase } from '../../../core/races/use-cases/GetRaceHighlightsUseCase';
|
||||
import { RaceDetailQuery } from '../../../core/races/ports/RaceDetailQuery';
|
||||
import { RaceParticipantsQuery } from '../../../core/races/ports/RaceParticipantsQuery';
|
||||
import { RaceWinnerQuery } from '../../../core/races/ports/RaceWinnerQuery';
|
||||
import { RaceStatisticsQuery } from '../../../core/races/ports/RaceStatisticsQuery';
|
||||
import { RaceLapTimesQuery } from '../../../core/races/ports/RaceLapTimesQuery';
|
||||
import { RaceQualifyingQuery } from '../../../core/races/ports/RaceQualifyingQuery';
|
||||
import { RacePointsQuery } from '../../../core/races/ports/RacePointsQuery';
|
||||
import { RaceHighlightsQuery } from '../../../core/races/ports/RaceHighlightsQuery';
|
||||
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
||||
import { InMemoryRaceRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryLeagueRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
import { InMemoryRaceRegistrationRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||
import { InMemoryResultRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
||||
import { InMemoryLeagueMembershipRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
||||
import { GetRaceDetailUseCase } from '../../../core/racing/application/use-cases/GetRaceDetailUseCase';
|
||||
import { Race } from '../../../core/racing/domain/entities/Race';
|
||||
import { League } from '../../../core/racing/domain/entities/League';
|
||||
import { Driver } from '../../../core/racing/domain/entities/Driver';
|
||||
import { Logger } from '../../../core/shared/domain/Logger';
|
||||
|
||||
describe('Race Detail Use Case Orchestration', () => {
|
||||
let raceRepository: InMemoryRaceRepository;
|
||||
let eventPublisher: InMemoryEventPublisher;
|
||||
let leagueRepository: InMemoryLeagueRepository;
|
||||
let driverRepository: InMemoryDriverRepository;
|
||||
let raceRegistrationRepository: InMemoryRaceRegistrationRepository;
|
||||
let resultRepository: InMemoryResultRepository;
|
||||
let leagueMembershipRepository: InMemoryLeagueMembershipRepository;
|
||||
let getRaceDetailUseCase: GetRaceDetailUseCase;
|
||||
let getRaceParticipantsUseCase: GetRaceParticipantsUseCase;
|
||||
let getRaceWinnerUseCase: GetRaceWinnerUseCase;
|
||||
let getRaceStatisticsUseCase: GetRaceStatisticsUseCase;
|
||||
let getRaceLapTimesUseCase: GetRaceLapTimesUseCase;
|
||||
let getRaceQualifyingUseCase: GetRaceQualifyingUseCase;
|
||||
let getRacePointsUseCase: GetRacePointsUseCase;
|
||||
let getRaceHighlightsUseCase: GetRaceHighlightsUseCase;
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeAll(() => {
|
||||
// TODO: Initialize In-Memory repositories and event publisher
|
||||
// raceRepository = new InMemoryRaceRepository();
|
||||
// eventPublisher = new InMemoryEventPublisher();
|
||||
// getRaceDetailUseCase = new GetRaceDetailUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceParticipantsUseCase = new GetRaceParticipantsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceWinnerUseCase = new GetRaceWinnerUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceStatisticsUseCase = new GetRaceStatisticsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceLapTimesUseCase = new GetRaceLapTimesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceQualifyingUseCase = new GetRaceQualifyingUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRacePointsUseCase = new GetRacePointsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceHighlightsUseCase = new GetRaceHighlightsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
mockLogger = {
|
||||
info: () => {},
|
||||
debug: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
} as unknown as Logger;
|
||||
|
||||
raceRepository = new InMemoryRaceRepository(mockLogger);
|
||||
leagueRepository = new InMemoryLeagueRepository(mockLogger);
|
||||
driverRepository = new InMemoryDriverRepository(mockLogger);
|
||||
raceRegistrationRepository = new InMemoryRaceRegistrationRepository(mockLogger);
|
||||
resultRepository = new InMemoryResultRepository(mockLogger, raceRepository);
|
||||
leagueMembershipRepository = new InMemoryLeagueMembershipRepository(mockLogger);
|
||||
|
||||
getRaceDetailUseCase = new GetRaceDetailUseCase(
|
||||
raceRepository,
|
||||
leagueRepository,
|
||||
driverRepository,
|
||||
raceRegistrationRepository,
|
||||
resultRepository,
|
||||
leagueMembershipRepository
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// TODO: Clear all In-Memory repositories before each test
|
||||
// raceRepository.clear();
|
||||
// eventPublisher.clear();
|
||||
beforeEach(async () => {
|
||||
// Clear repositories
|
||||
(raceRepository as any).races.clear();
|
||||
leagueRepository.clear();
|
||||
await driverRepository.clear();
|
||||
(raceRegistrationRepository as any).registrations.clear();
|
||||
(resultRepository as any).results.clear();
|
||||
leagueMembershipRepository.clear();
|
||||
});
|
||||
|
||||
describe('GetRaceDetailUseCase - Success Path', () => {
|
||||
describe('GetRaceDetailUseCase', () => {
|
||||
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
|
||||
// Given: A race and league exist
|
||||
const leagueId = 'l1';
|
||||
const league = League.create({ id: leagueId, name: 'Pro League', description: 'Desc', ownerId: 'o1' });
|
||||
await leagueRepository.create(league);
|
||||
|
||||
const raceId = 'r1';
|
||||
const race = Race.create({
|
||||
id: raceId,
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() + 86400000),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
status: 'scheduled'
|
||||
});
|
||||
await raceRepository.create(race);
|
||||
|
||||
// When: GetRaceDetailUseCase.execute() is called
|
||||
const result = await getRaceDetailUseCase.execute({ raceId });
|
||||
|
||||
// Then: The result should contain race and league information
|
||||
expect(result.isOk()).toBe(true);
|
||||
const data = result.unwrap();
|
||||
expect(data.race.id).toBe(raceId);
|
||||
expect(data.league?.id).toBe(leagueId);
|
||||
expect(data.isUserRegistered).toBe(false);
|
||||
});
|
||||
|
||||
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 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
|
||||
});
|
||||
|
||||
it('should retrieve race detail with page title', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with page title
|
||||
// Given: A race exists
|
||||
// When: GetRaceDetailUseCase.execute() is called with race ID
|
||||
// Then: The result should include page title
|
||||
// And: EventPublisher should emit RaceDetailAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race detail with page description', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with page description
|
||||
// Given: A race exists
|
||||
// When: GetRaceDetailUseCase.execute() is called with race ID
|
||||
// Then: The result should include page description
|
||||
// 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 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
|
||||
const result = await getRaceDetailUseCase.execute({ raceId: 'non-existent' });
|
||||
|
||||
// Then: Should return RACE_NOT_FOUND error
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
|
||||
});
|
||||
|
||||
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 identify if a driver is registered', async () => {
|
||||
// Given: A race and a registered driver
|
||||
const leagueId = 'l1';
|
||||
const raceId = 'r1';
|
||||
const driverId = 'd1';
|
||||
|
||||
const race = Race.create({
|
||||
id: raceId,
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() + 86400000),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
status: 'scheduled'
|
||||
});
|
||||
await raceRepository.create(race);
|
||||
|
||||
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
|
||||
});
|
||||
});
|
||||
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
|
||||
await driverRepository.create(driver);
|
||||
|
||||
describe('GetRaceParticipantsUseCase - Success Path', () => {
|
||||
it('should retrieve race participants count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with participants
|
||||
// Given: A race exists with participants
|
||||
// When: GetRaceParticipantsUseCase.execute() is called with race ID
|
||||
// Then: The result should show participants count
|
||||
// And: EventPublisher should emit RaceParticipantsAccessedEvent
|
||||
});
|
||||
// Mock registration (using any to bypass private access if needed, but InMemoryRaceRegistrationRepository has register method)
|
||||
await raceRegistrationRepository.register({
|
||||
raceId: raceId as any,
|
||||
driverId: driverId as any,
|
||||
registeredAt: new Date()
|
||||
} as any);
|
||||
|
||||
it('should retrieve race participants count for race with no participants', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no participants
|
||||
// Given: A race exists with no participants
|
||||
// When: GetRaceParticipantsUseCase.execute() is called with race ID
|
||||
// Then: The result should show 0 participants
|
||||
// And: EventPublisher should emit RaceParticipantsAccessedEvent
|
||||
});
|
||||
// When: GetRaceDetailUseCase.execute() is called with driverId
|
||||
const result = await getRaceDetailUseCase.execute({ raceId, driverId });
|
||||
|
||||
it('should retrieve race participants count for upcoming race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Upcoming race with participants
|
||||
// Given: An upcoming race exists with participants
|
||||
// When: GetRaceParticipantsUseCase.execute() is called with race ID
|
||||
// Then: The result should show participants count
|
||||
// And: EventPublisher should emit RaceParticipantsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race participants count for completed race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Completed race with participants
|
||||
// Given: A completed race exists with participants
|
||||
// When: GetRaceParticipantsUseCase.execute() is called with race ID
|
||||
// Then: The result should show participants count
|
||||
// And: EventPublisher should emit RaceParticipantsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceParticipantsUseCase - 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: GetRaceParticipantsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceParticipantsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceWinnerUseCase - Success Path', () => {
|
||||
it('should retrieve race winner for completed race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Completed race with winner
|
||||
// Given: A completed race exists with winner
|
||||
// When: GetRaceWinnerUseCase.execute() is called with race ID
|
||||
// Then: The result should show race winner
|
||||
// And: EventPublisher should emit RaceWinnerAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race podium for completed race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Completed race with podium
|
||||
// Given: A completed race exists with podium
|
||||
// When: GetRaceWinnerUseCase.execute() is called with race ID
|
||||
// Then: The result should show top 3 finishers
|
||||
// And: EventPublisher should emit RaceWinnerAccessedEvent
|
||||
});
|
||||
|
||||
it('should not retrieve winner for upcoming race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Upcoming race without winner
|
||||
// Given: An upcoming race exists
|
||||
// When: GetRaceWinnerUseCase.execute() is called with race ID
|
||||
// Then: The result should not show winner or podium
|
||||
// And: EventPublisher should emit RaceWinnerAccessedEvent
|
||||
});
|
||||
|
||||
it('should not retrieve winner for in-progress race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: In-progress race without winner
|
||||
// Given: An in-progress race exists
|
||||
// When: GetRaceWinnerUseCase.execute() is called with race ID
|
||||
// Then: The result should not show winner or podium
|
||||
// And: EventPublisher should emit RaceWinnerAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceWinnerUseCase - 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: GetRaceWinnerUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceWinnerUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceStatisticsUseCase - Success Path', () => {
|
||||
it('should retrieve race statistics with lap count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with lap count
|
||||
// Given: A race exists with lap count
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show lap count
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with incidents count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with incidents count
|
||||
// Given: A race exists with incidents count
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show incidents count
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with penalties count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with penalties count
|
||||
// Given: A race exists with penalties count
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show penalties count
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with protests count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with protests count
|
||||
// Given: A race exists with protests count
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show protests count
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with stewarding actions count', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with stewarding actions count
|
||||
// Given: A race exists with stewarding actions count
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show stewarding actions count
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with all metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with all statistics
|
||||
// Given: A race exists with all statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show all statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with empty metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no statistics
|
||||
// Given: A race exists with no statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceStatisticsUseCase - 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: GetRaceStatisticsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceStatisticsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceLapTimesUseCase - Success Path', () => {
|
||||
it('should retrieve race lap times with average lap time', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with average lap time
|
||||
// Given: A race exists with average lap time
|
||||
// When: GetRaceLapTimesUseCase.execute() is called with race ID
|
||||
// Then: The result should show average lap time
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race lap times with fastest lap', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with fastest lap
|
||||
// Given: A race exists with fastest lap
|
||||
// When: GetRaceLapTimesUseCase.execute() is called with race ID
|
||||
// Then: The result should show fastest lap
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race lap times with best sector times', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with best sector times
|
||||
// Given: A race exists with best sector times
|
||||
// When: GetRaceLapTimesUseCase.execute() is called with race ID
|
||||
// Then: The result should show best sector times
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race lap times with all metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with all lap time metrics
|
||||
// Given: A race exists with all lap time metrics
|
||||
// When: GetRaceLapTimesUseCase.execute() is called with race ID
|
||||
// Then: The result should show all lap time metrics
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race lap times with empty metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no lap times
|
||||
// Given: A race exists with no lap times
|
||||
// When: GetRaceLapTimesUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default lap times
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceLapTimesUseCase - 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: GetRaceLapTimesUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceLapTimesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceQualifyingUseCase - Success Path', () => {
|
||||
it('should retrieve race qualifying results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with qualifying results
|
||||
// Given: A race exists with qualifying results
|
||||
// When: GetRaceQualifyingUseCase.execute() is called with race ID
|
||||
// Then: The result should show qualifying results
|
||||
// And: EventPublisher should emit RaceQualifyingAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race starting grid', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with starting grid
|
||||
// Given: A race exists with starting grid
|
||||
// When: GetRaceQualifyingUseCase.execute() is called with race ID
|
||||
// Then: The result should show starting grid
|
||||
// And: EventPublisher should emit RaceQualifyingAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race qualifying results with pole position', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with pole position
|
||||
// Given: A race exists with pole position
|
||||
// When: GetRaceQualifyingUseCase.execute() is called with race ID
|
||||
// Then: The result should show pole position
|
||||
// And: EventPublisher should emit RaceQualifyingAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race qualifying results with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no qualifying results
|
||||
// Given: A race exists with no qualifying results
|
||||
// When: GetRaceQualifyingUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default qualifying results
|
||||
// And: EventPublisher should emit RaceQualifyingAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceQualifyingUseCase - 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: GetRaceQualifyingUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceQualifyingUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePointsUseCase - Success Path', () => {
|
||||
it('should retrieve race points distribution', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with points distribution
|
||||
// Given: A race exists with points distribution
|
||||
// When: GetRacePointsUseCase.execute() is called with race ID
|
||||
// Then: The result should show points distribution
|
||||
// And: EventPublisher should emit RacePointsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race championship implications', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with championship implications
|
||||
// Given: A race exists with championship implications
|
||||
// When: GetRacePointsUseCase.execute() is called with race ID
|
||||
// Then: The result should show championship implications
|
||||
// And: EventPublisher should emit RacePointsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race points with empty distribution', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no points distribution
|
||||
// Given: A race exists with no points distribution
|
||||
// When: GetRacePointsUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default points distribution
|
||||
// And: EventPublisher should emit RacePointsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePointsUseCase - 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: GetRacePointsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRacePointsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceHighlightsUseCase - Success Path', () => {
|
||||
it('should retrieve race highlights', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with highlights
|
||||
// Given: A race exists with highlights
|
||||
// When: GetRaceHighlightsUseCase.execute() is called with race ID
|
||||
// Then: The result should show highlights
|
||||
// And: EventPublisher should emit RaceHighlightsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race video link', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with video link
|
||||
// Given: A race exists with video link
|
||||
// When: GetRaceHighlightsUseCase.execute() is called with race ID
|
||||
// Then: The result should show video link
|
||||
// And: EventPublisher should emit RaceHighlightsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race gallery', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with gallery
|
||||
// Given: A race exists with gallery
|
||||
// When: GetRaceHighlightsUseCase.execute() is called with race ID
|
||||
// Then: The result should show gallery
|
||||
// And: EventPublisher should emit RaceHighlightsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race highlights with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no highlights
|
||||
// Given: A race exists with no highlights
|
||||
// When: GetRaceHighlightsUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default highlights
|
||||
// And: EventPublisher should emit RaceHighlightsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceHighlightsUseCase - 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: GetRaceHighlightsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceHighlightsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('Race Detail Page Data Orchestration', () => {
|
||||
it('should correctly orchestrate data for race detail page', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race detail page data orchestration
|
||||
// Given: A race exists with all information
|
||||
// When: Multiple use cases are executed for the same race
|
||||
// Then: Each use case should return its 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
|
||||
// - Car: Clearly displayed
|
||||
// - League: Clearly displayed
|
||||
// - Date: Formatted correctly
|
||||
// - Time: Formatted correctly
|
||||
// - Duration: Formatted correctly
|
||||
// - 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
|
||||
});
|
||||
|
||||
it('should correctly handle race with no statistics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no statistics
|
||||
// Given: A race exists with no statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called
|
||||
// Then: The result should show empty or default statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly 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: GetRaceLapTimesUseCase.execute() is called
|
||||
// Then: The result should show empty or default lap times
|
||||
// And: EventPublisher should emit RaceLapTimesAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly 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: GetRaceQualifyingUseCase.execute() is called
|
||||
// Then: The result should show empty or default qualifying results
|
||||
// And: EventPublisher should emit RaceQualifyingAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no highlights', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no highlights
|
||||
// Given: A race exists with no highlights
|
||||
// When: GetRaceHighlightsUseCase.execute() is called
|
||||
// Then: The result should show empty or default highlights
|
||||
// And: EventPublisher should emit RaceHighlightsAccessedEvent
|
||||
// Then: isUserRegistered should be true
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap().isUserRegistered).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,722 +2,158 @@
|
||||
* Integration Test: Race Results Use Case Orchestration
|
||||
*
|
||||
* Tests the orchestration logic of race results page-related Use Cases:
|
||||
* - GetRaceResultsUseCase: Retrieves complete race results (all finishers)
|
||||
* - GetRaceStatisticsUseCase: Retrieves race statistics (fastest lap, average lap time, etc.)
|
||||
* - GetRaceResultsDetailUseCase: Retrieves complete race results (all finishers)
|
||||
* - GetRacePenaltiesUseCase: Retrieves race penalties and incidents
|
||||
* - GetRaceStewardingActionsUseCase: Retrieves race stewarding actions
|
||||
* - GetRacePointsDistributionUseCase: Retrieves race points distribution
|
||||
* - GetRaceChampionshipImplicationsUseCase: Retrieves race championship implications
|
||||
* - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
|
||||
* - Uses In-Memory adapters for fast, deterministic testing
|
||||
*
|
||||
* Adheres to Clean Architecture:
|
||||
* - Tests Core Use Cases directly
|
||||
* - Uses In-Memory adapters for repositories
|
||||
* - Follows Given/When/Then pattern
|
||||
*
|
||||
* 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 { GetRaceResultsUseCase } from '../../../core/races/use-cases/GetRaceResultsUseCase';
|
||||
import { GetRaceStatisticsUseCase } from '../../../core/races/use-cases/GetRaceStatisticsUseCase';
|
||||
import { GetRacePenaltiesUseCase } from '../../../core/races/use-cases/GetRacePenaltiesUseCase';
|
||||
import { GetRaceStewardingActionsUseCase } from '../../../core/races/use-cases/GetRaceStewardingActionsUseCase';
|
||||
import { GetRacePointsDistributionUseCase } from '../../../core/races/use-cases/GetRacePointsDistributionUseCase';
|
||||
import { GetRaceChampionshipImplicationsUseCase } from '../../../core/races/use-cases/GetRaceChampionshipImplicationsUseCase';
|
||||
import { RaceResultsQuery } from '../../../core/races/ports/RaceResultsQuery';
|
||||
import { RaceStatisticsQuery } from '../../../core/races/ports/RaceStatisticsQuery';
|
||||
import { RacePenaltiesQuery } from '../../../core/races/ports/RacePenaltiesQuery';
|
||||
import { RaceStewardingActionsQuery } from '../../../core/races/ports/RaceStewardingActionsQuery';
|
||||
import { RacePointsDistributionQuery } from '../../../core/races/ports/RacePointsDistributionQuery';
|
||||
import { RaceChampionshipImplicationsQuery } from '../../../core/races/ports/RaceChampionshipImplicationsQuery';
|
||||
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
||||
import { InMemoryRaceRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryLeagueRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
import { InMemoryResultRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
||||
import { InMemoryPenaltyRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryPenaltyRepository';
|
||||
import { GetRaceResultsDetailUseCase } from '../../../core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
||||
import { GetRacePenaltiesUseCase } from '../../../core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
||||
import { Race } from '../../../core/racing/domain/entities/Race';
|
||||
import { League } from '../../../core/racing/domain/entities/League';
|
||||
import { Driver } from '../../../core/racing/domain/entities/Driver';
|
||||
import { Result as RaceResult } from '../../../core/racing/domain/entities/result/Result';
|
||||
import { Penalty } from '../../../core/racing/domain/entities/penalty/Penalty';
|
||||
import { Logger } from '../../../core/shared/domain/Logger';
|
||||
|
||||
describe('Race Results Use Case Orchestration', () => {
|
||||
let raceRepository: InMemoryRaceRepository;
|
||||
let eventPublisher: InMemoryEventPublisher;
|
||||
let getRaceResultsUseCase: GetRaceResultsUseCase;
|
||||
let getRaceStatisticsUseCase: GetRaceStatisticsUseCase;
|
||||
let leagueRepository: InMemoryLeagueRepository;
|
||||
let driverRepository: InMemoryDriverRepository;
|
||||
let resultRepository: InMemoryResultRepository;
|
||||
let penaltyRepository: InMemoryPenaltyRepository;
|
||||
let getRaceResultsDetailUseCase: GetRaceResultsDetailUseCase;
|
||||
let getRacePenaltiesUseCase: GetRacePenaltiesUseCase;
|
||||
let getRaceStewardingActionsUseCase: GetRaceStewardingActionsUseCase;
|
||||
let getRacePointsDistributionUseCase: GetRacePointsDistributionUseCase;
|
||||
let getRaceChampionshipImplicationsUseCase: GetRaceChampionshipImplicationsUseCase;
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeAll(() => {
|
||||
// TODO: Initialize In-Memory repositories and event publisher
|
||||
// raceRepository = new InMemoryRaceRepository();
|
||||
// eventPublisher = new InMemoryEventPublisher();
|
||||
// getRaceResultsUseCase = new GetRaceResultsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceStatisticsUseCase = new GetRaceStatisticsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRacePenaltiesUseCase = new GetRacePenaltiesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceStewardingActionsUseCase = new GetRaceStewardingActionsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRacePointsDistributionUseCase = new GetRacePointsDistributionUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// getRaceChampionshipImplicationsUseCase = new GetRaceChampionshipImplicationsUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
mockLogger = {
|
||||
info: () => {},
|
||||
debug: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
} as unknown as Logger;
|
||||
|
||||
raceRepository = new InMemoryRaceRepository(mockLogger);
|
||||
leagueRepository = new InMemoryLeagueRepository(mockLogger);
|
||||
driverRepository = new InMemoryDriverRepository(mockLogger);
|
||||
resultRepository = new InMemoryResultRepository(mockLogger, raceRepository);
|
||||
penaltyRepository = new InMemoryPenaltyRepository(mockLogger);
|
||||
|
||||
getRaceResultsDetailUseCase = new GetRaceResultsDetailUseCase(
|
||||
raceRepository,
|
||||
leagueRepository,
|
||||
resultRepository,
|
||||
driverRepository,
|
||||
penaltyRepository
|
||||
);
|
||||
|
||||
getRacePenaltiesUseCase = new GetRacePenaltiesUseCase(
|
||||
penaltyRepository,
|
||||
driverRepository
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// TODO: Clear all In-Memory repositories before each test
|
||||
// raceRepository.clear();
|
||||
// eventPublisher.clear();
|
||||
beforeEach(async () => {
|
||||
(raceRepository as any).races.clear();
|
||||
leagueRepository.clear();
|
||||
await driverRepository.clear();
|
||||
(resultRepository as any).results.clear();
|
||||
(penaltyRepository as any).penalties.clear();
|
||||
});
|
||||
|
||||
describe('GetRaceResultsUseCase - Success Path', () => {
|
||||
describe('GetRaceResultsDetailUseCase', () => {
|
||||
it('should retrieve complete race results with all finishers', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Driver views complete race results
|
||||
// Given: A completed race exists with multiple finishers
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain all finishers
|
||||
// And: The list should be ordered by position
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
// Given: A completed race with results
|
||||
const leagueId = 'l1';
|
||||
const league = League.create({ id: leagueId, name: 'Pro League', description: 'Desc', ownerId: 'o1' });
|
||||
await leagueRepository.create(league);
|
||||
|
||||
it('should retrieve race results with race winner', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with winner
|
||||
// Given: A completed race exists with winner
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show race winner
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
const raceId = 'r1';
|
||||
const race = Race.create({
|
||||
id: raceId,
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() - 86400000),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
status: 'completed'
|
||||
});
|
||||
await raceRepository.create(race);
|
||||
|
||||
it('should retrieve race results with podium', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with podium
|
||||
// Given: A completed race exists with podium
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show top 3 finishers
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
const driverId = 'd1';
|
||||
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
|
||||
await driverRepository.create(driver);
|
||||
|
||||
it('should retrieve race results with driver information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with driver information
|
||||
// Given: A completed race exists with driver information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show driver name, team, car
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
const raceResult = RaceResult.create({
|
||||
id: 'res1',
|
||||
raceId,
|
||||
driverId,
|
||||
position: 1,
|
||||
lapsCompleted: 20,
|
||||
totalTime: 3600,
|
||||
fastestLap: 105,
|
||||
points: 25
|
||||
});
|
||||
await resultRepository.create(raceResult);
|
||||
|
||||
it('should retrieve race results with position information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with position information
|
||||
// Given: A completed race exists with position information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show position, race time, gaps
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
// When: GetRaceResultsDetailUseCase.execute() is called
|
||||
const result = await getRaceResultsDetailUseCase.execute({ raceId });
|
||||
|
||||
it('should retrieve race results with lap information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with lap information
|
||||
// Given: A completed race exists with lap information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show laps completed, fastest lap, average lap time
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with points information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with points information
|
||||
// Given: A completed race exists with points information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show points earned
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with penalties information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with penalties information
|
||||
// Given: A completed race exists with penalties information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show penalties
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with incidents information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with incidents information
|
||||
// Given: A completed race exists with incidents information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show incidents
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with stewarding actions information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with stewarding actions information
|
||||
// Given: A completed race exists with stewarding actions information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show stewarding actions
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with protests information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with protests information
|
||||
// Given: A completed race exists with protests information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should show protests
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race results with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no results
|
||||
// Given: A race exists with no results
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
// Then: The result should contain race and results
|
||||
expect(result.isOk()).toBe(true);
|
||||
const data = result.unwrap();
|
||||
expect(data.race.id).toBe(raceId);
|
||||
expect(data.results).toHaveLength(1);
|
||||
expect(data.results[0].driverId.toString()).toBe(driverId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceResultsUseCase - Edge Cases', () => {
|
||||
it('should handle race with missing driver information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing driver data
|
||||
// Given: A completed race exists with missing driver information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
describe('GetRacePenaltiesUseCase', () => {
|
||||
it('should retrieve race penalties with driver information', async () => {
|
||||
// Given: A race with penalties
|
||||
const raceId = 'r1';
|
||||
const driverId = 'd1';
|
||||
const stewardId = 's1';
|
||||
|
||||
it('should handle race with missing team information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing team data
|
||||
// Given: A completed race exists with missing team information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
|
||||
await driverRepository.create(driver);
|
||||
|
||||
const steward = Driver.create({ id: stewardId, iracingId: '200', name: 'Steward', country: 'UK' });
|
||||
await driverRepository.create(steward);
|
||||
|
||||
it('should handle race with missing car information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing car data
|
||||
// Given: A completed race exists with missing car information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
const penalty = Penalty.create({
|
||||
id: 'p1',
|
||||
raceId,
|
||||
driverId,
|
||||
type: 'time',
|
||||
value: 5,
|
||||
reason: 'Track limits',
|
||||
issuedBy: stewardId,
|
||||
status: 'applied'
|
||||
});
|
||||
await penaltyRepository.create(penalty);
|
||||
|
||||
it('should handle race with missing position information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing position data
|
||||
// Given: A completed race exists with missing position information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing lap information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing lap data
|
||||
// Given: A completed race exists with missing lap information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing points information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing points data
|
||||
// Given: A completed race exists with missing points information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing penalties information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing penalties data
|
||||
// Given: A completed race exists with missing penalties information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing incidents information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing incidents data
|
||||
// Given: A completed race exists with missing incidents information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing stewarding actions information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing stewarding actions data
|
||||
// Given: A completed race exists with missing stewarding actions information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle race with missing protests information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results with missing protests data
|
||||
// Given: A completed race exists with missing protests information
|
||||
// When: GetRaceResultsUseCase.execute() is called with race ID
|
||||
// Then: The result should contain results with available information
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceResultsUseCase - 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: GetRaceResultsUseCase.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: GetRaceResultsUseCase.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: GetRaceResultsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceStatisticsUseCase - Success Path', () => {
|
||||
it('should retrieve race statistics with fastest lap', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with fastest lap
|
||||
// Given: A completed race exists with fastest lap
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show fastest lap
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with average lap time', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with average lap time
|
||||
// Given: A completed race exists with average lap time
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show average lap time
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with total incidents', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with total incidents
|
||||
// Given: A completed race exists with total incidents
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show total incidents
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with total penalties', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with total penalties
|
||||
// Given: A completed race exists with total penalties
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show total penalties
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with total protests', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with total protests
|
||||
// Given: A completed race exists with total protests
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show total protests
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with total stewarding actions', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with total stewarding actions
|
||||
// Given: A completed race exists with total stewarding actions
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show total stewarding actions
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with all metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with all statistics
|
||||
// Given: A completed race exists with all statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show all statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race statistics with empty metrics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no statistics
|
||||
// Given: A completed race exists with no statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called with race ID
|
||||
// Then: The result should show empty or default statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceStatisticsUseCase - 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: GetRaceStatisticsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceStatisticsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePenaltiesUseCase - Success Path', () => {
|
||||
it('should retrieve race penalties with penalty information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with penalties
|
||||
// Given: A completed race exists with penalties
|
||||
// When: GetRacePenaltiesUseCase.execute() is called with race ID
|
||||
// Then: The result should show penalty information
|
||||
// And: EventPublisher should emit RacePenaltiesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race penalties with incident information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with incidents
|
||||
// Given: A completed race exists with incidents
|
||||
// When: GetRacePenaltiesUseCase.execute() is called with race ID
|
||||
// Then: The result should show incident information
|
||||
// And: EventPublisher should emit RacePenaltiesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race penalties with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no penalties
|
||||
// Given: A completed race exists with no penalties
|
||||
// When: GetRacePenaltiesUseCase.execute() is called with race ID
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacePenaltiesAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePenaltiesUseCase - 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: GetRacePenaltiesUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRacePenaltiesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
const result = await getRacePenaltiesUseCase.execute({ raceId });
|
||||
|
||||
describe('GetRaceStewardingActionsUseCase - Success Path', () => {
|
||||
it('should retrieve race stewarding actions with action information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with stewarding actions
|
||||
// Given: A completed race exists with stewarding actions
|
||||
// When: GetRaceStewardingActionsUseCase.execute() is called with race ID
|
||||
// Then: The result should show stewarding action information
|
||||
// And: EventPublisher should emit RaceStewardingActionsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race stewarding actions with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no stewarding actions
|
||||
// Given: A completed race exists with no stewarding actions
|
||||
// When: GetRaceStewardingActionsUseCase.execute() is called with race ID
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceStewardingActionsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceStewardingActionsUseCase - 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: GetRaceStewardingActionsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceStewardingActionsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePointsDistributionUseCase - Success Path', () => {
|
||||
it('should retrieve race points distribution', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with points distribution
|
||||
// Given: A completed race exists with points distribution
|
||||
// When: GetRacePointsDistributionUseCase.execute() is called with race ID
|
||||
// Then: The result should show points distribution
|
||||
// And: EventPublisher should emit RacePointsDistributionAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race points distribution with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no points distribution
|
||||
// Given: A completed race exists with no points distribution
|
||||
// When: GetRacePointsDistributionUseCase.execute() is called with race ID
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacePointsDistributionAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRacePointsDistributionUseCase - 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: GetRacePointsDistributionUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRacePointsDistributionUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceChampionshipImplicationsUseCase - Success Path', () => {
|
||||
it('should retrieve race championship implications', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with championship implications
|
||||
// Given: A completed race exists with championship implications
|
||||
// When: GetRaceChampionshipImplicationsUseCase.execute() is called with race ID
|
||||
// Then: The result should show championship implications
|
||||
// And: EventPublisher should emit RaceChampionshipImplicationsAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve race championship implications with empty results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no championship implications
|
||||
// Given: A completed race exists with no championship implications
|
||||
// When: GetRaceChampionshipImplicationsUseCase.execute() is called with race ID
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceChampionshipImplicationsAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRaceChampionshipImplicationsUseCase - 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: GetRaceChampionshipImplicationsUseCase.execute() is called with non-existent race ID
|
||||
// Then: Should throw RaceNotFoundError
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetRaceChampionshipImplicationsUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('Race Results Page Data Orchestration', () => {
|
||||
it('should correctly orchestrate data for race results page', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results page data orchestration
|
||||
// Given: A completed race exists with all information
|
||||
// When: Multiple use cases are executed for the same race
|
||||
// Then: Each use case should return its respective data
|
||||
// And: EventPublisher should emit appropriate events for each use case
|
||||
});
|
||||
|
||||
it('should correctly format race results for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race results formatting
|
||||
// Given: A completed race exists with all information
|
||||
// When: GetRaceResultsUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Driver name: Clearly displayed
|
||||
// - Team: Clearly displayed
|
||||
// - Car: Clearly displayed
|
||||
// - Position: Clearly displayed
|
||||
// - Race time: Formatted correctly
|
||||
// - Gaps: Formatted correctly
|
||||
// - Laps completed: Clearly displayed
|
||||
// - Points earned: Clearly displayed
|
||||
// - Fastest lap: Formatted correctly
|
||||
// - Average lap time: Formatted correctly
|
||||
// - Penalties: Clearly displayed
|
||||
// - Incidents: Clearly displayed
|
||||
// - Stewarding actions: Clearly displayed
|
||||
// - Protests: Clearly displayed
|
||||
});
|
||||
|
||||
it('should correctly format race statistics for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race statistics formatting
|
||||
// Given: A completed race exists with all statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Fastest lap: Formatted correctly
|
||||
// - Average lap time: Formatted correctly
|
||||
// - Total incidents: Clearly displayed
|
||||
// - Total penalties: Clearly displayed
|
||||
// - Total protests: Clearly displayed
|
||||
// - Total stewarding actions: Clearly displayed
|
||||
});
|
||||
|
||||
it('should correctly format race penalties for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race penalties formatting
|
||||
// Given: A completed race exists with penalties
|
||||
// When: GetRacePenaltiesUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Penalty ID: Clearly displayed
|
||||
// - Penalty type: Clearly displayed
|
||||
// - Penalty severity: Clearly displayed
|
||||
// - Penalty recipient: Clearly displayed
|
||||
// - Penalty reason: Clearly displayed
|
||||
// - Penalty timestamp: Formatted correctly
|
||||
});
|
||||
|
||||
it('should correctly format race stewarding actions for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race stewarding actions formatting
|
||||
// Given: A completed race exists with stewarding actions
|
||||
// When: GetRaceStewardingActionsUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Stewarding action ID: Clearly displayed
|
||||
// - Stewarding action type: Clearly displayed
|
||||
// - Stewarding action recipient: Clearly displayed
|
||||
// - Stewarding action reason: Clearly displayed
|
||||
// - Stewarding action timestamp: Formatted correctly
|
||||
});
|
||||
|
||||
it('should correctly format race points distribution for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race points distribution formatting
|
||||
// Given: A completed race exists with points distribution
|
||||
// When: GetRacePointsDistributionUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Points distribution: Clearly displayed
|
||||
// - Championship implications: Clearly displayed
|
||||
});
|
||||
|
||||
it('should correctly format race championship implications for display', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race championship implications formatting
|
||||
// Given: A completed race exists with championship implications
|
||||
// When: GetRaceChampionshipImplicationsUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Championship implications: Clearly displayed
|
||||
// - Points changes: Clearly displayed
|
||||
// - Position changes: Clearly displayed
|
||||
});
|
||||
|
||||
it('should correctly handle race with no results', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no results
|
||||
// Given: A race exists with no results
|
||||
// When: GetRaceResultsUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceResultsAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no statistics', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no statistics
|
||||
// Given: A race exists with no statistics
|
||||
// When: GetRaceStatisticsUseCase.execute() is called
|
||||
// Then: The result should show empty or default statistics
|
||||
// And: EventPublisher should emit RaceStatisticsAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no penalties', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no penalties
|
||||
// Given: A race exists with no penalties
|
||||
// When: GetRacePenaltiesUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacePenaltiesAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no stewarding actions', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no stewarding actions
|
||||
// Given: A race exists with no stewarding actions
|
||||
// When: GetRaceStewardingActionsUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceStewardingActionsAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no points distribution', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no points distribution
|
||||
// Given: A race exists with no points distribution
|
||||
// When: GetRacePointsDistributionUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacePointsDistributionAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle race with no championship implications', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race with no championship implications
|
||||
// Given: A race exists with no championship implications
|
||||
// When: GetRaceChampionshipImplicationsUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RaceChampionshipImplicationsAccessedEvent
|
||||
// Then: It should return penalties and drivers
|
||||
expect(result.isOk()).toBe(true);
|
||||
const data = result.unwrap();
|
||||
expect(data.penalties).toHaveLength(1);
|
||||
expect(data.drivers.some(d => d.id === driverId)).toBe(true);
|
||||
expect(data.drivers.some(d => d.id === stewardId)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,682 +3,97 @@
|
||||
*
|
||||
* Tests the orchestration logic of all races page-related Use Cases:
|
||||
* - GetAllRacesUseCase: Retrieves comprehensive list of all races
|
||||
* - FilterRacesUseCase: Filters races by league, car, track, date range
|
||||
* - SearchRacesUseCase: Searches races by track name and league name
|
||||
* - SortRacesUseCase: Sorts races by date, league, car
|
||||
* - PaginateRacesUseCase: Paginates race results
|
||||
* - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
|
||||
* - Uses In-Memory adapters for fast, deterministic testing
|
||||
*
|
||||
* Adheres to Clean Architecture:
|
||||
* - Tests Core Use Cases directly
|
||||
* - Uses In-Memory adapters for repositories
|
||||
* - Follows Given/When/Then pattern
|
||||
*
|
||||
* 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 { GetAllRacesUseCase } from '../../../core/races/use-cases/GetAllRacesUseCase';
|
||||
import { FilterRacesUseCase } from '../../../core/races/use-cases/FilterRacesUseCase';
|
||||
import { SearchRacesUseCase } from '../../../core/races/use-cases/SearchRacesUseCase';
|
||||
import { SortRacesUseCase } from '../../../core/races/use-cases/SortRacesUseCase';
|
||||
import { PaginateRacesUseCase } from '../../../core/races/use-cases/PaginateRacesUseCase';
|
||||
import { AllRacesQuery } from '../../../core/races/ports/AllRacesQuery';
|
||||
import { RaceFilterCommand } from '../../../core/races/ports/RaceFilterCommand';
|
||||
import { RaceSearchCommand } from '../../../core/races/ports/RaceSearchCommand';
|
||||
import { RaceSortCommand } from '../../../core/races/ports/RaceSortCommand';
|
||||
import { RacePaginationCommand } from '../../../core/races/ports/RacePaginationCommand';
|
||||
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
||||
import { InMemoryRaceRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryLeagueRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { GetAllRacesUseCase } from '../../../core/racing/application/use-cases/GetAllRacesUseCase';
|
||||
import { Race } from '../../../core/racing/domain/entities/Race';
|
||||
import { League } from '../../../core/racing/domain/entities/League';
|
||||
import { Logger } from '../../../core/shared/domain/Logger';
|
||||
|
||||
describe('All Races Use Case Orchestration', () => {
|
||||
let raceRepository: InMemoryRaceRepository;
|
||||
let eventPublisher: InMemoryEventPublisher;
|
||||
let leagueRepository: InMemoryLeagueRepository;
|
||||
let getAllRacesUseCase: GetAllRacesUseCase;
|
||||
let filterRacesUseCase: FilterRacesUseCase;
|
||||
let searchRacesUseCase: SearchRacesUseCase;
|
||||
let sortRacesUseCase: SortRacesUseCase;
|
||||
let paginateRacesUseCase: PaginateRacesUseCase;
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeAll(() => {
|
||||
// TODO: Initialize In-Memory repositories and event publisher
|
||||
// raceRepository = new InMemoryRaceRepository();
|
||||
// eventPublisher = new InMemoryEventPublisher();
|
||||
// getAllRacesUseCase = new GetAllRacesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// filterRacesUseCase = new FilterRacesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// searchRacesUseCase = new SearchRacesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// sortRacesUseCase = new SortRacesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
// paginateRacesUseCase = new PaginateRacesUseCase({
|
||||
// raceRepository,
|
||||
// eventPublisher,
|
||||
// });
|
||||
mockLogger = {
|
||||
info: () => {},
|
||||
debug: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
} as unknown as Logger;
|
||||
|
||||
raceRepository = new InMemoryRaceRepository(mockLogger);
|
||||
leagueRepository = new InMemoryLeagueRepository(mockLogger);
|
||||
|
||||
getAllRacesUseCase = new GetAllRacesUseCase(
|
||||
raceRepository,
|
||||
leagueRepository,
|
||||
mockLogger
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// TODO: Clear all In-Memory repositories before each test
|
||||
// raceRepository.clear();
|
||||
// eventPublisher.clear();
|
||||
beforeEach(async () => {
|
||||
(raceRepository as any).races.clear();
|
||||
leagueRepository.clear();
|
||||
});
|
||||
|
||||
describe('GetAllRacesUseCase - Success Path', () => {
|
||||
describe('GetAllRacesUseCase', () => {
|
||||
it('should retrieve comprehensive list of all races', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Driver views all races
|
||||
// Given: Multiple races exist with different tracks, cars, leagues, and dates
|
||||
// And: Races include upcoming, in-progress, and completed races
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain all races
|
||||
// And: Each race should display track name, date, car, league, and winner (if completed)
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve all races with complete information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: All races with complete information
|
||||
// Given: Multiple races exist with complete information
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with all available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve all races with minimal information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: All races with minimal data
|
||||
// Given: Races exist with basic information only
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should retrieve all races when no races exist', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: No races exist
|
||||
// Given: No races exist in the system
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetAllRacesUseCase - Edge Cases', () => {
|
||||
it('should handle races with missing track information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Races with missing track data
|
||||
// Given: Races exist with missing track information
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle races with missing car information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Races with missing car data
|
||||
// Given: Races exist with missing car information
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle races with missing league information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Races with missing league data
|
||||
// Given: Races exist with missing league information
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should handle races with missing winner information', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Races with missing winner data
|
||||
// Given: Races exist with missing winner information
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: The result should contain races with available information
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetAllRacesUseCase - Error Handling', () => {
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Repository throws error
|
||||
// Given: RaceRepository throws an error during query
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('FilterRacesUseCase - Success Path', () => {
|
||||
it('should filter races by league', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races by league
|
||||
// Given: Multiple races exist across different leagues
|
||||
// When: FilterRacesUseCase.execute() is called with league filter
|
||||
// Then: The result should contain only races from the specified league
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races by car', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races by car
|
||||
// Given: Multiple races exist with different cars
|
||||
// When: FilterRacesUseCase.execute() is called with car filter
|
||||
// Then: The result should contain only races with the specified car
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races by track', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races by track
|
||||
// Given: Multiple races exist at different tracks
|
||||
// When: FilterRacesUseCase.execute() is called with track filter
|
||||
// Then: The result should contain only races at the specified track
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races by date range', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races by date range
|
||||
// Given: Multiple races exist across different dates
|
||||
// When: FilterRacesUseCase.execute() is called with date range
|
||||
// Then: The result should contain only races within the date range
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races by multiple criteria', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races by multiple criteria
|
||||
// Given: Multiple races exist with different attributes
|
||||
// When: FilterRacesUseCase.execute() is called with multiple filters
|
||||
// Then: The result should contain only races matching all criteria
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races with empty result when no matches', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter with no matches
|
||||
// Given: Races exist but none match the filter criteria
|
||||
// When: FilterRacesUseCase.execute() is called with filter
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races with pagination', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races with pagination
|
||||
// Given: Many races exist matching filter criteria
|
||||
// When: FilterRacesUseCase.execute() is called with filter and pagination
|
||||
// Then: The result should contain only the specified page of filtered races
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should filter races with limit', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Filter races with limit
|
||||
// Given: Many races exist matching filter criteria
|
||||
// When: FilterRacesUseCase.execute() is called with filter and limit
|
||||
// Then: The result should contain only the specified number of filtered races
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('FilterRacesUseCase - Edge Cases', () => {
|
||||
it('should handle empty filter criteria', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Empty filter criteria
|
||||
// Given: Races exist
|
||||
// When: FilterRacesUseCase.execute() is called with empty filter
|
||||
// Then: The result should contain all races (no filtering applied)
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should handle case-insensitive filtering', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Case-insensitive filtering
|
||||
// Given: Races exist with mixed case names
|
||||
// When: FilterRacesUseCase.execute() is called with different case filter
|
||||
// Then: The result should match regardless of case
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
|
||||
it('should handle partial matches in text filters', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Partial matches in text filters
|
||||
// Given: Races exist with various names
|
||||
// When: FilterRacesUseCase.execute() is called with partial text
|
||||
// Then: The result should include races with partial matches
|
||||
// And: EventPublisher should emit RacesFilteredEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('FilterRacesUseCase - Error Handling', () => {
|
||||
it('should handle invalid filter parameters', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Invalid filter parameters
|
||||
// Given: Invalid filter values (e.g., empty strings, null)
|
||||
// When: FilterRacesUseCase.execute() is called with invalid parameters
|
||||
// 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: RaceRepository throws an error during filter
|
||||
// When: FilterRacesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('SearchRacesUseCase - Success Path', () => {
|
||||
it('should search races by track name', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search races by track name
|
||||
// Given: Multiple races exist at different tracks
|
||||
// When: SearchRacesUseCase.execute() is called with track name
|
||||
// Then: The result should contain races matching the track name
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races by league name', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search races by league name
|
||||
// Given: Multiple races exist in different leagues
|
||||
// When: SearchRacesUseCase.execute() is called with league name
|
||||
// Then: The result should contain races matching the league name
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races with partial matches', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search with partial matches
|
||||
// Given: Races exist with various names
|
||||
// When: SearchRacesUseCase.execute() is called with partial search term
|
||||
// Then: The result should include races with partial matches
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races case-insensitively', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Case-insensitive search
|
||||
// Given: Races exist with mixed case names
|
||||
// When: SearchRacesUseCase.execute() is called with different case search term
|
||||
// Then: The result should match regardless of case
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races with empty result when no matches', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search with no matches
|
||||
// Given: Races exist but none match the search term
|
||||
// When: SearchRacesUseCase.execute() is called with search term
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races with pagination', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search races with pagination
|
||||
// Given: Many races exist matching search term
|
||||
// When: SearchRacesUseCase.execute() is called with search term and pagination
|
||||
// Then: The result should contain only the specified page of search results
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should search races with limit', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Search races with limit
|
||||
// Given: Many races exist matching search term
|
||||
// When: SearchRacesUseCase.execute() is called with search term and limit
|
||||
// Then: The result should contain only the specified number of search results
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('SearchRacesUseCase - Edge Cases', () => {
|
||||
it('should handle empty search term', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Empty search term
|
||||
// Given: Races exist
|
||||
// When: SearchRacesUseCase.execute() is called with empty search term
|
||||
// Then: The result should contain all races (no search applied)
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should handle special characters in search term', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Special characters in search term
|
||||
// Given: Races exist with special characters in names
|
||||
// When: SearchRacesUseCase.execute() is called with special characters
|
||||
// Then: The result should handle special characters appropriately
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
|
||||
it('should handle very long search terms', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Very long search term
|
||||
// Given: Races exist
|
||||
// When: SearchRacesUseCase.execute() is called with very long search term
|
||||
// Then: The result should handle the long term appropriately
|
||||
// And: EventPublisher should emit RacesSearchedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('SearchRacesUseCase - Error Handling', () => {
|
||||
it('should handle invalid search parameters', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Invalid search parameters
|
||||
// Given: Invalid search values (e.g., null, undefined)
|
||||
// When: SearchRacesUseCase.execute() is called with invalid parameters
|
||||
// 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: RaceRepository throws an error during search
|
||||
// When: SearchRacesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('SortRacesUseCase - Success Path', () => {
|
||||
it('should sort races by date', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races by date
|
||||
// Given: Multiple races exist with different dates
|
||||
// When: SortRacesUseCase.execute() is called with date sort
|
||||
// Then: The result should be sorted by date
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should sort races by league', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races by league
|
||||
// Given: Multiple races exist with different leagues
|
||||
// When: SortRacesUseCase.execute() is called with league sort
|
||||
// Then: The result should be sorted by league name alphabetically
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should sort races by car', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races by car
|
||||
// Given: Multiple races exist with different cars
|
||||
// When: SortRacesUseCase.execute() is called with car sort
|
||||
// Then: The result should be sorted by car name alphabetically
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should sort races in ascending order', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races in ascending order
|
||||
// Given: Multiple races exist
|
||||
// When: SortRacesUseCase.execute() is called with ascending sort
|
||||
// Then: The result should be sorted in ascending order
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
const leagueId = 'l1';
|
||||
const league = League.create({ id: leagueId, name: 'Pro League', description: 'Desc', ownerId: 'o1' });
|
||||
await leagueRepository.create(league);
|
||||
|
||||
const race1 = Race.create({
|
||||
id: 'r1',
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() + 86400000),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
status: 'scheduled'
|
||||
});
|
||||
const race2 = Race.create({
|
||||
id: 'r2',
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() - 86400000),
|
||||
track: 'Monza',
|
||||
car: 'GT3',
|
||||
status: 'completed'
|
||||
});
|
||||
await raceRepository.create(race1);
|
||||
await raceRepository.create(race2);
|
||||
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
const result = await getAllRacesUseCase.execute({});
|
||||
|
||||
// Then: The result should contain all races and leagues
|
||||
expect(result.isOk()).toBe(true);
|
||||
const data = result.unwrap();
|
||||
expect(data.races).toHaveLength(2);
|
||||
expect(data.leagues).toHaveLength(1);
|
||||
expect(data.totalCount).toBe(2);
|
||||
});
|
||||
|
||||
it('should sort races in descending order', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races in descending order
|
||||
// Given: Multiple races exist
|
||||
// When: SortRacesUseCase.execute() is called with descending sort
|
||||
// Then: The result should be sorted in descending order
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
it('should return empty list when no races exist', async () => {
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
const result = await getAllRacesUseCase.execute({});
|
||||
|
||||
it('should sort races with pagination', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races with pagination
|
||||
// Given: Many races exist
|
||||
// When: SortRacesUseCase.execute() is called with sort and pagination
|
||||
// Then: The result should contain only the specified page of sorted races
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should sort races with limit', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Sort races with limit
|
||||
// Given: Many races exist
|
||||
// When: SortRacesUseCase.execute() is called with sort and limit
|
||||
// Then: The result should contain only the specified number of sorted races
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('SortRacesUseCase - Edge Cases', () => {
|
||||
it('should handle races with missing sort field', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Races with missing sort field
|
||||
// Given: Races exist with missing sort field values
|
||||
// When: SortRacesUseCase.execute() is called
|
||||
// Then: The result should handle missing values appropriately
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should handle empty race list', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Empty race list
|
||||
// Given: No races exist
|
||||
// When: SortRacesUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
|
||||
it('should handle single race', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Single race
|
||||
// Given: Only one race exists
|
||||
// When: SortRacesUseCase.execute() is called
|
||||
// Then: The result should contain the single race
|
||||
// And: EventPublisher should emit RacesSortedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('SortRacesUseCase - Error Handling', () => {
|
||||
it('should handle invalid sort parameters', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Invalid sort parameters
|
||||
// Given: Invalid sort field or direction
|
||||
// When: SortRacesUseCase.execute() is called with invalid parameters
|
||||
// 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: RaceRepository throws an error during sort
|
||||
// When: SortRacesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('PaginateRacesUseCase - Success Path', () => {
|
||||
it('should paginate races with page and pageSize', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Paginate races
|
||||
// Given: Many races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with page and pageSize
|
||||
// Then: The result should contain only the specified page of races
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with first page', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: First page of races
|
||||
// Given: Many races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with page 1
|
||||
// Then: The result should contain the first page of races
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with middle page', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Middle page of races
|
||||
// Given: Many races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with middle page number
|
||||
// Then: The result should contain the middle page of races
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with last page', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Last page of races
|
||||
// Given: Many races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with last page number
|
||||
// Then: The result should contain the last page of races
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with different page sizes', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Different page sizes
|
||||
// Given: Many races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with different pageSize values
|
||||
// Then: The result should contain the correct number of races per page
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with empty result when page exceeds total', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Page exceeds total
|
||||
// Given: Races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with page beyond total
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should paginate races with empty result when no races exist', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: No races exist
|
||||
// Given: No races exist
|
||||
// When: PaginateRacesUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('PaginateRacesUseCase - Edge Cases', () => {
|
||||
it('should handle page 0', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Page 0
|
||||
// Given: Races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with page 0
|
||||
// Then: Should handle appropriately (either throw error or return first page)
|
||||
// And: EventPublisher should emit RacesPaginatedEvent or NOT emit
|
||||
});
|
||||
|
||||
it('should handle very large page size', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Very large page size
|
||||
// Given: Races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with very large pageSize
|
||||
// Then: The result should contain all races or handle appropriately
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
|
||||
it('should handle page size larger than total races', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Page size larger than total
|
||||
// Given: Few races exist
|
||||
// When: PaginateRacesUseCase.execute() is called with pageSize > total
|
||||
// Then: The result should contain all races
|
||||
// And: EventPublisher should emit RacesPaginatedEvent
|
||||
});
|
||||
});
|
||||
|
||||
describe('PaginateRacesUseCase - Error Handling', () => {
|
||||
it('should handle invalid pagination parameters', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Invalid pagination parameters
|
||||
// Given: Invalid page or pageSize values (negative, null, undefined)
|
||||
// When: PaginateRacesUseCase.execute() is called with invalid parameters
|
||||
// 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: RaceRepository throws an error during pagination
|
||||
// When: PaginateRacesUseCase.execute() is called
|
||||
// Then: Should propagate the error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
});
|
||||
});
|
||||
|
||||
describe('All Races Page Data Orchestration', () => {
|
||||
it('should correctly orchestrate filtering, searching, sorting, and pagination', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Combined operations
|
||||
// Given: Many races exist with various attributes
|
||||
// When: Multiple use cases are executed in sequence
|
||||
// Then: Each use case should work correctly
|
||||
// And: EventPublisher should emit appropriate events for each operation
|
||||
});
|
||||
|
||||
it('should correctly format race information for all races list', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race information formatting
|
||||
// Given: Races exist with all information
|
||||
// When: AllRacesUseCase.execute() is called
|
||||
// Then: The result should format:
|
||||
// - Track name: Clearly displayed
|
||||
// - Date: Formatted correctly
|
||||
// - Car: Clearly displayed
|
||||
// - League: Clearly displayed
|
||||
// - Winner: Clearly displayed (if completed)
|
||||
});
|
||||
|
||||
it('should correctly handle race status in all races list', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Race status in all races
|
||||
// Given: Races exist with different statuses (Upcoming, In Progress, Completed)
|
||||
// When: AllRacesUseCase.execute() is called
|
||||
// Then: The result should show appropriate status for each race
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle empty states', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Empty states
|
||||
// Given: No races exist
|
||||
// When: AllRacesUseCase.execute() is called
|
||||
// Then: The result should be empty
|
||||
// And: EventPublisher should emit AllRacesAccessedEvent
|
||||
});
|
||||
|
||||
it('should correctly handle loading states', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Loading states
|
||||
// Given: Races are being loaded
|
||||
// When: AllRacesUseCase.execute() is called
|
||||
// Then: The use case should handle loading state appropriately
|
||||
// And: EventPublisher should emit appropriate events
|
||||
});
|
||||
|
||||
it('should correctly handle error states', async () => {
|
||||
// TODO: Implement test
|
||||
// Scenario: Error states
|
||||
// Given: Repository throws error
|
||||
// When: AllRacesUseCase.execute() is called
|
||||
// Then: The use case should handle error appropriately
|
||||
// And: EventPublisher should NOT emit any events
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap().races).toHaveLength(0);
|
||||
expect(result.unwrap().totalCount).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,699 +2,88 @@
|
||||
* 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
|
||||
* - GetAllRacesUseCase: Used to retrieve upcoming and recent races
|
||||
*
|
||||
* Adheres to Clean Architecture:
|
||||
* - Tests Core Use Cases directly
|
||||
* - Uses In-Memory adapters for repositories
|
||||
* - Follows Given/When/Then pattern
|
||||
*
|
||||
* 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';
|
||||
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
||||
import { InMemoryRaceRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryLeagueRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { GetAllRacesUseCase } from '../../../core/racing/application/use-cases/GetAllRacesUseCase';
|
||||
import { Race } from '../../../core/racing/domain/entities/Race';
|
||||
import { League } from '../../../core/racing/domain/entities/League';
|
||||
import { Logger } from '../../../core/shared/domain/Logger';
|
||||
|
||||
describe('Races Main Use Case Orchestration', () => {
|
||||
let raceRepository: InMemoryRaceRepository;
|
||||
let eventPublisher: InMemoryEventPublisher;
|
||||
let getUpcomingRacesUseCase: GetUpcomingRacesUseCase;
|
||||
let getRecentRaceResultsUseCase: GetRecentRaceResultsUseCase;
|
||||
let getRaceDetailUseCase: GetRaceDetailUseCase;
|
||||
let leagueRepository: InMemoryLeagueRepository;
|
||||
let getAllRacesUseCase: GetAllRacesUseCase;
|
||||
let mockLogger: Logger;
|
||||
|
||||
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,
|
||||
// });
|
||||
mockLogger = {
|
||||
info: () => {},
|
||||
debug: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
} as unknown as Logger;
|
||||
|
||||
raceRepository = new InMemoryRaceRepository(mockLogger);
|
||||
leagueRepository = new InMemoryLeagueRepository(mockLogger);
|
||||
|
||||
getAllRacesUseCase = new GetAllRacesUseCase(
|
||||
raceRepository,
|
||||
leagueRepository,
|
||||
mockLogger
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// TODO: Clear all In-Memory repositories before each test
|
||||
// raceRepository.clear();
|
||||
// eventPublisher.clear();
|
||||
beforeEach(async () => {
|
||||
(raceRepository as any).races.clear();
|
||||
leagueRepository.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
|
||||
describe('Races Main Page Data', () => {
|
||||
it('should retrieve upcoming and recent races', async () => {
|
||||
// Given: Upcoming and completed races exist
|
||||
const leagueId = 'l1';
|
||||
const league = League.create({ id: leagueId, name: 'Pro League', description: 'Desc', ownerId: 'o1' });
|
||||
await leagueRepository.create(league);
|
||||
|
||||
const upcomingRace = Race.create({
|
||||
id: 'r1',
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() + 86400000),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
status: 'scheduled'
|
||||
});
|
||||
const completedRace = Race.create({
|
||||
id: 'r2',
|
||||
leagueId,
|
||||
scheduledAt: new Date(Date.now() - 86400000),
|
||||
track: 'Monza',
|
||||
car: 'GT3',
|
||||
status: 'completed'
|
||||
});
|
||||
await raceRepository.create(upcomingRace);
|
||||
await raceRepository.create(completedRace);
|
||||
|
||||
// When: GetAllRacesUseCase.execute() is called
|
||||
const result = await getAllRacesUseCase.execute({});
|
||||
|
||||
// Then: The result should contain both races
|
||||
expect(result.isOk()).toBe(true);
|
||||
const data = result.unwrap();
|
||||
expect(data.races).toHaveLength(2);
|
||||
expect(data.races.some(r => r.status.isScheduled())).toBe(true);
|
||||
expect(data.races.some(r => r.status.isCompleted())).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user