import { describe, it, expect, vi } from 'vitest'; import { SendFinalResultsUseCase } from './SendFinalResultsUseCase'; import type { INotificationService } from '../../../notifications/application/ports/INotificationService'; import type { IRaceEventRepository } from '../../domain/repositories/IRaceEventRepository'; import type { IResultRepository } from '../../domain/repositories/IResultRepository'; import type { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed'; describe('SendFinalResultsUseCase', () => { it('sends final results notifications to all participating drivers', async () => { const mockNotificationService = { sendNotification: vi.fn(), } as unknown as INotificationService; const mockRaceEvent = { id: 'race-1', name: 'Test Race', getMainRaceSession: vi.fn().mockReturnValue({ id: 'session-1' }), }; const mockRaceEventRepository = { findById: vi.fn().mockResolvedValue(mockRaceEvent), } as unknown as IRaceEventRepository; const mockResults = [ { driverId: 'driver-1', position: 1, incidents: 0, getPositionChange: vi.fn().mockReturnValue(2), }, { driverId: 'driver-2', position: 2, incidents: 1, getPositionChange: vi.fn().mockReturnValue(-1), }, ]; const mockResultRepository = { findByRaceId: vi.fn().mockResolvedValue(mockResults), } as unknown as IResultRepository; const useCase = new SendFinalResultsUseCase( mockNotificationService, mockRaceEventRepository, mockResultRepository, ); const event: RaceEventStewardingClosedEvent = { eventType: 'RaceEventStewardingClosed', aggregateId: 'race-1', occurredAt: new Date(), eventData: { raceEventId: 'race-1', seasonId: 'season-1', leagueId: 'league-1', driverIds: ['driver-1', 'driver-2'], hadPenaltiesApplied: false, closedAt: new Date(), }, }; const result = await useCase.execute(event); expect(result.isOk()).toBe(true); expect(mockRaceEventRepository.findById).toHaveBeenCalledWith('race-1'); expect(mockResultRepository.findByRaceId).toHaveBeenCalledWith('session-1'); expect(mockNotificationService.sendNotification).toHaveBeenCalledTimes(2); // Check first notification expect(mockNotificationService.sendNotification).toHaveBeenCalledWith( expect.objectContaining({ recipientId: 'driver-1', type: 'race_final_results', title: 'Final Results: Test Race', body: expect.stringContaining('Final result: P1 (+2 positions). Clean race! +35 rating.'), data: expect.objectContaining({ raceEventId: 'race-1', sessionId: 'session-1', leagueId: 'league-1', position: 1, positionChange: 2, incidents: 0, finalRatingChange: 35, hadPenaltiesApplied: false, }), }), ); // Check second notification expect(mockNotificationService.sendNotification).toHaveBeenCalledWith( expect.objectContaining({ recipientId: 'driver-2', type: 'race_final_results', title: 'Final Results: Test Race', body: expect.stringContaining('Final result: P2 (-1 positions). 1 incident +20 rating.'), data: expect.objectContaining({ position: 2, positionChange: -1, incidents: 1, finalRatingChange: 20, }), }), ); }); it('skips sending notifications if race event not found', async () => { const mockNotificationService = { sendNotification: vi.fn(), } as unknown as INotificationService; const mockRaceEventRepository = { findById: vi.fn().mockResolvedValue(null), } as unknown as IRaceEventRepository; const mockResultRepository = { findByRaceId: vi.fn(), } as unknown as IResultRepository; const useCase = new SendFinalResultsUseCase( mockNotificationService, mockRaceEventRepository, mockResultRepository, ); const event: RaceEventStewardingClosedEvent = { eventType: 'RaceEventStewardingClosed', aggregateId: 'race-1', occurredAt: new Date(), eventData: { raceEventId: 'race-1', seasonId: 'season-1', leagueId: 'league-1', driverIds: ['driver-1'], hadPenaltiesApplied: false, closedAt: new Date(), }, }; const result = await useCase.execute(event); expect(result.isOk()).toBe(true); expect(mockNotificationService.sendNotification).not.toHaveBeenCalled(); }); it('skips sending notifications if no main race session', async () => { const mockNotificationService = { sendNotification: vi.fn(), } as unknown as INotificationService; const mockRaceEvent = { id: 'race-1', name: 'Test Race', getMainRaceSession: vi.fn().mockReturnValue(null), }; const mockRaceEventRepository = { findById: vi.fn().mockResolvedValue(mockRaceEvent), } as unknown as IRaceEventRepository; const mockResultRepository = { findByRaceId: vi.fn(), } as unknown as IResultRepository; const useCase = new SendFinalResultsUseCase( mockNotificationService, mockRaceEventRepository, mockResultRepository, ); const event: RaceEventStewardingClosedEvent = { eventType: 'RaceEventStewardingClosed', aggregateId: 'race-1', occurredAt: new Date(), eventData: { raceEventId: 'race-1', seasonId: 'season-1', leagueId: 'league-1', driverIds: ['driver-1'], hadPenaltiesApplied: false, closedAt: new Date(), }, }; const result = await useCase.execute(event); expect(result.isOk()).toBe(true); expect(mockNotificationService.sendNotification).not.toHaveBeenCalled(); }); });