import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest'; import { GetRaceProtestsUseCase, type GetRaceProtestsInput, type GetRaceProtestsResult, type GetRaceProtestsErrorCode, } from './GetRaceProtestsUseCase'; import type { IProtestRepository } from '../../domain/repositories/IProtestRepository'; import type { IDriverRepository } from '../../domain/repositories/IDriverRepository'; import { Protest } from '../../domain/entities/Protest'; import { Driver } from '../../domain/entities/Driver'; import type { UseCaseOutputPort } from '@core/shared/application'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; describe('GetRaceProtestsUseCase', () => { let useCase: GetRaceProtestsUseCase; let protestRepository: { findByRaceId: Mock }; let driverRepository: { findById: Mock }; let output: UseCaseOutputPort & { present: Mock }; beforeEach(() => { protestRepository = { findByRaceId: vi.fn() }; driverRepository = { findById: vi.fn() }; output = { present: vi.fn() } as unknown as UseCaseOutputPort & { present: Mock }; useCase = new GetRaceProtestsUseCase( protestRepository as unknown as IProtestRepository, driverRepository as unknown as IDriverRepository, output, ); }); it('should return protests with drivers', async () => { const raceId = 'race-1'; const protest = Protest.create({ id: 'protest-1', raceId, protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', incident: { lap: 1, description: 'Incident' }, status: 'pending', filedAt: new Date(), reviewedBy: 'driver-3', }); const driver1 = Driver.create({ id: 'driver-1', iracingId: 'ir-1', name: 'Driver 1', country: 'US', }); const driver2 = Driver.create({ id: 'driver-2', iracingId: 'ir-2', name: 'Driver 2', country: 'UK', }); const driver3 = Driver.create({ id: 'driver-3', iracingId: 'ir-3', name: 'Driver 3', country: 'DE', }); protestRepository.findByRaceId.mockResolvedValue([protest]); driverRepository.findById.mockImplementation((id: string) => { if (id === 'driver-1') return Promise.resolve(driver1); if (id === 'driver-2') return Promise.resolve(driver2); if (id === 'driver-3') return Promise.resolve(driver3); return Promise.resolve(null); }); const input: GetRaceProtestsInput = { raceId }; const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(result.unwrap()).toBeUndefined(); expect(output.present).toHaveBeenCalledTimes(1); const presentedRaw = output.present.mock.calls[0]?.[0]; expect(presentedRaw).toBeDefined(); const presented = presentedRaw as GetRaceProtestsResult; expect(presented.protests).toHaveLength(1); expect(presented.protests[0]).toEqual(protest); expect(presented.drivers).toHaveLength(3); expect(presented.drivers).toEqual(expect.arrayContaining([driver1, driver2, driver3])); }); it('should return empty when no protests', async () => { protestRepository.findByRaceId.mockResolvedValue([]); driverRepository.findById.mockResolvedValue(null); const input: GetRaceProtestsInput = { raceId: 'race-1' }; const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(result.unwrap()).toBeUndefined(); expect(output.present).toHaveBeenCalledTimes(1); const presentedRaw = output.present.mock.calls[0]?.[0]; expect(presentedRaw).toBeDefined(); const presented = presentedRaw as GetRaceProtestsResult; expect(presented.protests).toEqual([]); expect(presented.drivers).toEqual([]); }); it('should return REPOSITORY_ERROR when repository throws', async () => { protestRepository.findByRaceId.mockRejectedValue(new Error('DB error')); const input: GetRaceProtestsInput = { raceId: 'race-1' }; const result = await useCase.execute(input); expect(result.isErr()).toBe(true); const err = result.unwrapErr() as ApplicationErrorCode< GetRaceProtestsErrorCode, { message: string } >; expect(err.code).toBe('REPOSITORY_ERROR'); expect(err.details.message).toBe('DB error'); expect(output.present).not.toHaveBeenCalled(); }); });