Files
gridpilot.gg/core/racing/application/use-cases/SendPerformanceSummaryUseCase.test.ts
2025-12-20 12:22:48 +01:00

144 lines
4.6 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { NotificationService } from '../../../notifications/application/ports/NotificationService';
import type { MainRaceCompletedEvent } from '../../domain/events/MainRaceCompleted';
import type { IRaceEventRepository } from '../../domain/repositories/IRaceEventRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import { SendPerformanceSummaryUseCase } from './SendPerformanceSummaryUseCase';
describe('SendPerformanceSummaryUseCase', () => {
it('sends performance summary notifications to all participating drivers', async () => {
const mockNotificationService = {
sendNotification: vi.fn(),
} as unknown as NotificationService;
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 SendPerformanceSummaryUseCase(
mockNotificationService,
mockRaceEventRepository,
mockResultRepository,
);
const event: MainRaceCompletedEvent = {
eventType: 'MainRaceCompleted',
aggregateId: 'race-1',
occurredAt: new Date(),
eventData: {
raceEventId: 'race-1',
sessionId: 'session-1',
seasonId: 'season-1',
leagueId: 'league-1',
driverIds: ['driver-1', 'driver-2'],
completedAt: 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_performance_summary',
title: 'Race Complete: Test Race',
body: expect.stringContaining('You finished P1 (+2 positions). Clean race! Provisional +35 rating.'),
data: expect.objectContaining({
raceEventId: 'race-1',
sessionId: 'session-1',
leagueId: 'league-1',
position: 1,
positionChange: 2,
incidents: 0,
provisionalRatingChange: 35,
}),
}),
);
// Check second notification
expect(mockNotificationService.sendNotification).toHaveBeenCalledWith(
expect.objectContaining({
recipientId: 'driver-2',
type: 'race_performance_summary',
title: 'Race Complete: Test Race',
body: expect.stringContaining('You finished P2 (-1 positions). 1 incident Provisional +20 rating.'),
data: expect.objectContaining({
position: 2,
positionChange: -1,
incidents: 1,
provisionalRatingChange: 20,
}),
}),
);
});
it('skips sending notifications if race event not found', async () => {
const mockNotificationService = {
sendNotification: vi.fn(),
} as unknown as NotificationService;
const mockRaceEventRepository = {
findById: vi.fn().mockResolvedValue(null),
} as unknown as IRaceEventRepository;
const mockResultRepository = {
findByRaceId: vi.fn(),
} as unknown as IResultRepository;
const useCase = new SendPerformanceSummaryUseCase(
mockNotificationService,
mockRaceEventRepository,
mockResultRepository,
);
const event: MainRaceCompletedEvent = {
eventType: 'MainRaceCompleted',
aggregateId: 'race-1',
occurredAt: new Date(),
eventData: {
raceEventId: 'race-1',
sessionId: 'session-1',
seasonId: 'season-1',
leagueId: 'league-1',
driverIds: ['driver-1'],
completedAt: new Date(),
},
};
const result = await useCase.execute(event);
expect(result.isOk()).toBe(true);
expect(mockNotificationService.sendNotification).not.toHaveBeenCalled();
});
});