144 lines
4.6 KiB
TypeScript
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();
|
|
});
|
|
}); |