139 lines
5.2 KiB
TypeScript
139 lines
5.2 KiB
TypeScript
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<CloseRaceEventStewardingResult> & { 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<CloseRaceEventStewardingResult> & { 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();
|
|
});
|
|
}); |