Files
gridpilot.gg/core/racing/application/use-cases/ImportRaceResultsUseCase.test.ts
2025-12-21 00:43:42 +01:00

244 lines
7.5 KiB
TypeScript

import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import {
ImportRaceResultsUseCase,
type ImportRaceResultsInput,
type ImportRaceResultsResult,
type ImportRaceResultsErrorCode,
} from './ImportRaceResultsUseCase';
import { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import { IResultRepository } from '../../domain/repositories/IResultRepository';
import { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('ImportRaceResultsUseCase', () => {
let useCase: ImportRaceResultsUseCase;
let raceRepository: {
findById: Mock;
};
let leagueRepository: {
findById: Mock;
};
let resultRepository: {
existsByRaceId: Mock;
createMany: Mock;
};
let driverRepository: {
findByIRacingId: Mock;
};
let standingRepository: {
recalculate: Mock;
};
let logger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
let output: UseCaseOutputPort<ImportRaceResultsResult> & { present: Mock };
beforeEach(() => {
raceRepository = {
findById: vi.fn(),
};
leagueRepository = {
findById: vi.fn(),
};
resultRepository = {
existsByRaceId: vi.fn(),
createMany: vi.fn(),
};
driverRepository = {
findByIRacingId: vi.fn(),
};
standingRepository = {
recalculate: vi.fn(),
};
logger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
output = {
present: vi.fn(),
} as unknown as UseCaseOutputPort<ImportRaceResultsResult> & { present: Mock };
useCase = new ImportRaceResultsUseCase(
raceRepository as unknown as IRaceRepository,
leagueRepository as unknown as ILeagueRepository,
resultRepository as unknown as IResultRepository,
driverRepository as unknown as IDriverRepository,
standingRepository as unknown as IStandingRepository,
logger as unknown as Logger,
output,
);
});
it('should return race not found error', async () => {
const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };
raceRepository.findById.mockResolvedValue(null);
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsErrorCode,
{ message: string }
>;
expect(error).toEqual({
code: 'RACE_NOT_FOUND',
details: { message: 'Race race-1 not found' },
});
expect(output.present).not.toHaveBeenCalled();
});
it('should return league not found error', async () => {
const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue(null);
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsErrorCode,
{ message: string }
>;
expect(error).toEqual({
code: 'LEAGUE_NOT_FOUND',
details: { message: 'League league-1 not found' },
});
expect(output.present).not.toHaveBeenCalled();
});
it('should return results exist error', async () => {
const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
resultRepository.existsByRaceId.mockResolvedValue(true);
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsErrorCode,
{ message: string }
>;
expect(error).toEqual({
code: 'RESULTS_EXIST',
details: { message: 'Results already exist for this race' },
});
expect(output.present).not.toHaveBeenCalled();
});
it('should return driver not found error', async () => {
const input: ImportRaceResultsInput = {
raceId: 'race-1',
rows: [
{
id: 'result-1',
driverExternalId: '123',
position: 1,
fastestLap: 100,
incidents: 0,
startPosition: 1,
},
],
};
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
resultRepository.existsByRaceId.mockResolvedValue(false);
driverRepository.findByIRacingId.mockResolvedValue(null);
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsErrorCode,
{ message: string }
>;
expect(error).toEqual({
code: 'DRIVER_NOT_FOUND',
details: { message: 'Driver with iRacing ID 123 not found' },
});
expect(output.present).not.toHaveBeenCalled();
});
it('should import results successfully', async () => {
const input: ImportRaceResultsInput = {
raceId: 'race-1',
rows: [
{
id: 'result-1',
driverExternalId: '123',
position: 1,
fastestLap: 100,
incidents: 0,
startPosition: 1,
},
],
};
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
resultRepository.existsByRaceId.mockResolvedValue(false);
driverRepository.findByIRacingId.mockResolvedValue({ id: 'driver-1' });
resultRepository.createMany.mockResolvedValue(undefined);
standingRepository.recalculate.mockResolvedValue(undefined);
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presented = output.present.mock.calls[0]?.[0] as ImportRaceResultsResult;
expect(presented).toEqual({
raceId: 'race-1',
leagueId: 'league-1',
driversProcessed: 1,
resultsRecorded: 1,
errors: [],
});
});
it('should return repository error when persistence fails', async () => {
const input: ImportRaceResultsInput = {
raceId: 'race-1',
rows: [
{
id: 'result-1',
driverExternalId: '123',
position: 1,
fastestLap: 100,
incidents: 0,
startPosition: 1,
},
],
};
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
resultRepository.existsByRaceId.mockResolvedValue(false);
driverRepository.findByIRacingId.mockResolvedValue({ id: 'driver-1' });
resultRepository.createMany.mockRejectedValue(new Error('DB failure'));
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsErrorCode,
{ message: string }
>;
expect(error.code).toBe('REPOSITORY_ERROR');
expect(error.details.message).toBe('DB failure');
expect(output.present).not.toHaveBeenCalled();
});
});