refactor racing use cases

This commit is contained in:
2025-12-21 00:43:42 +01:00
parent e9d6f90bb2
commit c12656d671
308 changed files with 14401 additions and 7419 deletions

View File

@@ -1,12 +1,15 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { CompleteRaceUseCaseWithRatings } from './CompleteRaceUseCaseWithRatings';
import {
CompleteRaceUseCaseWithRatings,
type CompleteRaceWithRatingsInput,
type CompleteRaceWithRatingsResult,
} from './CompleteRaceUseCaseWithRatings';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import { RatingUpdateService } from '@core/identity/domain/services/RatingUpdateService';
import type { CompleteRaceCommandDTO } from '../dto/CompleteRaceCommandDTO';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
describe('CompleteRaceUseCaseWithRatings', () => {
let useCase: CompleteRaceUseCaseWithRatings;
@@ -30,6 +33,7 @@ describe('CompleteRaceUseCaseWithRatings', () => {
let ratingUpdateService: {
updateDriverRatingsAfterRace: Mock;
};
let output: { present: Mock };
beforeEach(() => {
raceRepository = {
@@ -52,18 +56,21 @@ describe('CompleteRaceUseCaseWithRatings', () => {
ratingUpdateService = {
updateDriverRatingsAfterRace: vi.fn(),
};
output = { present: vi.fn() };
useCase = new CompleteRaceUseCaseWithRatings(
raceRepository as unknown as IRaceRepository,
raceRegistrationRepository as unknown as IRaceRegistrationRepository,
resultRepository as unknown as IResultRepository,
standingRepository as unknown as IStandingRepository,
driverRatingProvider as unknown as DriverRatingProvider,
driverRatingProvider,
ratingUpdateService as unknown as RatingUpdateService,
output as unknown as UseCaseOutputPort<CompleteRaceWithRatingsResult>,
);
});
it('should complete race successfully when race exists and has registered drivers', async () => {
const command: CompleteRaceCommandDTO = {
it('completes race with ratings when race exists and has registered drivers', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
@@ -75,7 +82,12 @@ describe('CompleteRaceUseCaseWithRatings', () => {
};
raceRepository.findById.mockResolvedValue(mockRace);
raceRegistrationRepository.getRegisteredDrivers.mockResolvedValue(['driver-1', 'driver-2']);
driverRatingProvider.getRatings.mockReturnValue(new Map([['driver-1', 1600], ['driver-2', 1500]]));
driverRatingProvider.getRatings.mockReturnValue(
new Map([
['driver-1', 1600],
['driver-2', 1500],
]),
);
resultRepository.create.mockResolvedValue(undefined);
standingRepository.findByDriverIdAndLeagueId.mockResolvedValue(null);
standingRepository.save.mockResolvedValue(undefined);
@@ -94,10 +106,15 @@ describe('CompleteRaceUseCaseWithRatings', () => {
expect(ratingUpdateService.updateDriverRatingsAfterRace).toHaveBeenCalled();
expect(mockRace.complete).toHaveBeenCalled();
expect(raceRepository.update).toHaveBeenCalledWith({ id: 'race-1', status: 'completed' });
expect(output.present).toHaveBeenCalledTimes(1);
expect(output.present).toHaveBeenCalledWith({
raceId: 'race-1',
ratingsUpdatedForDriverIds: ['driver-1', 'driver-2'],
});
});
it('should return error when race does not exist', async () => {
const command: CompleteRaceCommandDTO = {
it('returns error when race does not exist', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
@@ -107,10 +124,32 @@ describe('CompleteRaceUseCaseWithRatings', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
expect(output.present).not.toHaveBeenCalled();
});
it('should return error when no registered drivers', async () => {
const command: CompleteRaceCommandDTO = {
it('returns error when race is already completed', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
const mockRace = {
id: 'race-1',
leagueId: 'league-1',
status: 'completed',
complete: vi.fn(),
};
raceRepository.findById.mockResolvedValue(mockRace);
const result = await useCase.execute(command);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('ALREADY_COMPLETED');
expect(output.present).not.toHaveBeenCalled();
expect(raceRegistrationRepository.getRegisteredDrivers).not.toHaveBeenCalled();
});
it('returns error when no registered drivers', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
@@ -127,10 +166,39 @@ describe('CompleteRaceUseCaseWithRatings', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('NO_REGISTERED_DRIVERS');
expect(output.present).not.toHaveBeenCalled();
});
it('should return error when repository throws', async () => {
const command: CompleteRaceCommandDTO = {
it('returns rating update error when rating service throws', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
const mockRace = {
id: 'race-1',
leagueId: 'league-1',
status: 'scheduled',
complete: vi.fn().mockReturnValue({ id: 'race-1', status: 'completed' }),
};
raceRepository.findById.mockResolvedValue(mockRace);
raceRegistrationRepository.getRegisteredDrivers.mockResolvedValue(['driver-1']);
driverRatingProvider.getRatings.mockReturnValue(new Map([['driver-1', 1600]]));
resultRepository.create.mockResolvedValue(undefined);
standingRepository.findByDriverIdAndLeagueId.mockResolvedValue(null);
standingRepository.save.mockResolvedValue(undefined);
ratingUpdateService.updateDriverRatingsAfterRace.mockRejectedValue(new Error('Rating error'));
const result = await useCase.execute(command);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr();
expect(error.code).toBe('RATING_UPDATE_FAILED');
expect(error.details?.message).toBe('Rating error');
expect(output.present).not.toHaveBeenCalled();
});
it('returns repository error when persistence fails', async () => {
const command: CompleteRaceWithRatingsInput = {
raceId: 'race-1',
};
@@ -148,6 +216,9 @@ describe('CompleteRaceUseCaseWithRatings', () => {
const result = await useCase.execute(command);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('UNKNOWN_ERROR');
const error = result.unwrapErr();
expect(error.code).toBe('REPOSITORY_ERROR');
expect(error.details?.message).toBe('DB error');
expect(output.present).not.toHaveBeenCalled();
});
});
});