import { describe, it, expect, beforeEach, vi, Mock } from 'vitest'; import { CloseRaceEventStewardingUseCase, type CloseRaceEventStewardingResult } from './CloseRaceEventStewardingUseCase'; import type { IRaceEventRepository } from '../../domain/repositories/IRaceEventRepository'; import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository'; import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository'; import type { DomainEventPublisher } from '@core/shared/domain/DomainEvent'; import type { Logger } from '@core/shared/application'; import { RaceEvent } from '../../domain/entities/RaceEvent'; import { Session } from '../../domain/entities/Session'; import { SessionType } from '../../domain/value-objects/SessionType'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; describe('CloseRaceEventStewardingUseCase', () => { let useCase: CloseRaceEventStewardingUseCase; let raceEventRepository: { findAwaitingStewardingClose: Mock; findById: Mock; update: Mock; }; let raceRegistrationRepository: { getRegisteredDrivers: Mock; }; let penaltyRepository: { findByRaceId: Mock; }; let domainEventPublisher: { publish: Mock; }; let logger: { error: Mock; }; let output: UseCaseOutputPort & { present: Mock }; beforeEach(() => { raceEventRepository = { findAwaitingStewardingClose: vi.fn(), findById: vi.fn(), update: vi.fn(), }; raceRegistrationRepository = { getRegisteredDrivers: vi.fn(), }; penaltyRepository = { findByRaceId: vi.fn(), }; domainEventPublisher = { publish: vi.fn(), }; logger = { error: vi.fn(), }; output = { present: vi.fn() } as unknown as UseCaseOutputPort & { present: Mock }; useCase = new CloseRaceEventStewardingUseCase( logger as unknown as Logger, raceEventRepository as unknown as IRaceEventRepository, raceRegistrationRepository as unknown as IRaceRegistrationRepository, penaltyRepository as unknown as IPenaltyRepository, domainEventPublisher as unknown as DomainEventPublisher, output, ); }); it('should close stewarding for expired events successfully', async () => { const raceEvent = RaceEvent.create({ id: 'event-1', seasonId: 'season-1', leagueId: 'league-1', name: 'Test Event', sessions: [ Session.create({ id: 'session-1', raceEventId: 'event-1', sessionType: SessionType.main(), scheduledAt: new Date(), track: 'Test Track', car: 'Test Car', status: 'completed', }), ], status: 'awaiting_stewarding', stewardingClosesAt: new Date(Date.now() - 1000), // expired }); raceEventRepository.findAwaitingStewardingClose.mockResolvedValue([raceEvent]); raceEventRepository.findById.mockResolvedValue(raceEvent.closeStewarding()); raceRegistrationRepository.getRegisteredDrivers.mockResolvedValue(['driver-1', 'driver-2']); penaltyRepository.findByRaceId.mockResolvedValue([]); domainEventPublisher.publish.mockResolvedValue(undefined); const result = await useCase.execute({ raceId: 'event-1', closedById: 'admin-1' }); expect(result.isOk()).toBe(true); expect(result.unwrap()).toBeUndefined(); expect(raceEventRepository.findAwaitingStewardingClose).toHaveBeenCalled(); expect(raceEventRepository.update).toHaveBeenCalledWith( expect.objectContaining({ status: 'closed' }) ); expect(domainEventPublisher.publish).toHaveBeenCalled(); expect(output.present).toHaveBeenCalledTimes(1); const presentedRace = (output.present as Mock).mock.calls[0]?.[0]?.race as unknown as { id?: unknown; status?: unknown; }; const presentedId = presentedRace?.id && typeof presentedRace.id === 'object' && typeof presentedRace.id.toString === 'function' ? presentedRace.id.toString() : presentedRace?.id; expect(presentedId).toBe('event-1'); expect(presentedRace?.status).toBe('closed'); }); it('should handle no expired events', async () => { raceEventRepository.findAwaitingStewardingClose.mockResolvedValue([]); const result = await useCase.execute({ raceId: 'event-1', closedById: 'admin-1' }); expect(result.isOk()).toBe(true); expect(result.unwrap()).toBeUndefined(); expect(raceEventRepository.update).not.toHaveBeenCalled(); expect(domainEventPublisher.publish).not.toHaveBeenCalled(); expect(output.present).not.toHaveBeenCalled(); }); it('should return error when repository throws', async () => { raceEventRepository.findAwaitingStewardingClose.mockRejectedValue(new Error('DB error')); const result = await useCase.execute({ raceId: 'event-1', closedById: 'admin-1' }); expect(result.isErr()).toBe(true); const err = result.unwrapErr(); expect(err.code).toBe('REPOSITORY_ERROR'); if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) { expect(err.details.message).toContain('DB error'); } expect(output.present).not.toHaveBeenCalled(); }); });