Files
gridpilot.gg/core/racing/application/use-cases/ImportRaceResultsApiUseCase.test.ts
2026-01-16 19:46:49 +01:00

204 lines
7.9 KiB
TypeScript

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { StandingRepository } from '../../domain/repositories/StandingRepository';
import {
ImportRaceResultsApiUseCase,
type ImportRaceResultsApiErrorCode,
type ImportRaceResultsApiInput,
type ImportRaceResultsApiResult,
} from './ImportRaceResultsApiUseCase';
describe('ImportRaceResultsApiUseCase', () => {
let useCase: ImportRaceResultsApiUseCase;
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 };
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(),
};
useCase = new ImportRaceResultsApiUseCase(raceRepository as unknown as RaceRepository,
leagueRepository as unknown as LeagueRepository,
resultRepository as unknown as ResultRepository,
driverRepository as unknown as DriverRepository,
standingRepository as unknown as StandingRepository,
logger as unknown as Logger);
});
it('should return parse error for invalid JSON', async () => {
const input: ImportRaceResultsApiInput = { raceId: 'race-1', resultsFileContent: 'invalid json' };
const result: Result<
void,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsApiErrorCode,
{ message: string }
>;
expect(err.code).toBe('PARSE_ERROR');
expect(err.details?.message).toBe('Invalid JSON in results file content');
});
it('should return race not found error', async () => {
const input: ImportRaceResultsApiInput = { raceId: 'race-1', resultsFileContent: '[]' };
raceRepository.findById.mockResolvedValue(null);
const result: Result<
void,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsApiErrorCode,
{ message: string }
>;
expect(err.code).toBe('RACE_NOT_FOUND');
expect(err.details?.message).toBe('Race race-1 not found');
});
it('should return league not found error', async () => {
const input: ImportRaceResultsApiInput = { raceId: 'race-1', resultsFileContent: '[]' };
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue(null);
const result: Result<
void,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsApiErrorCode,
{ message: string }
>;
expect(err.code).toBe('LEAGUE_NOT_FOUND');
expect(err.details?.message).toBe('League league-1 not found');
});
it('should return results exist error', async () => {
const input: ImportRaceResultsApiInput = { raceId: 'race-1', resultsFileContent: '[]' };
raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
resultRepository.existsByRaceId.mockResolvedValue(true);
const result: Result<
void,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsApiErrorCode,
{ message: string }
>;
expect(err.code).toBe('RESULTS_EXIST');
expect(err.details?.message).toBe('Results already exist for this race');
});
it('should return driver not found error', async () => {
const input: ImportRaceResultsApiInput = {
raceId: 'race-1',
resultsFileContent:
'[{"id":"result-1","raceId":"race-1","driverId":"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: Result<
void,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
ImportRaceResultsApiErrorCode,
{ message: string }
>;
expect(err.code).toBe('DRIVER_NOT_FOUND');
expect(err.details?.message).toBe('Driver with iRacing ID 123 not found');
});
it('should import results successfully', async () => {
const input: ImportRaceResultsApiInput = {
raceId: 'race-1',
resultsFileContent:
'[{"id":"result-1","raceId":"race-1","driverId":"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: Result<
ImportRaceResultsApiResult,
ApplicationErrorCode<ImportRaceResultsApiErrorCode, { message: string }>
> = await useCase.execute(input);
expect(result.isOk()).toBe(true);
const presented = result.unwrap();
expect(presented.success).toBe(true);
expect(presented.raceId).toBe('race-1');
expect(presented.leagueId).toBe('league-1');
expect(presented.driversProcessed).toBe(1);
expect(presented.resultsRecorded).toBe(1);
expect(presented.errors).toEqual([]);
expect(resultRepository.createMany).toHaveBeenCalledTimes(1);
const createdManyArg = resultRepository.createMany.mock.calls[0]?.[0] as unknown[];
expect(createdManyArg).toHaveLength(1);
const created = createdManyArg[0] as unknown as {
id: string;
raceId: { toString(): string };
driverId: { toString(): string };
position: { toNumber(): number };
fastestLap: { toNumber(): number };
incidents: { toNumber(): number };
startPosition: { toNumber(): number };
};
expect(created.id).toBe('result-1');
expect(created.raceId.toString()).toBe('race-1');
expect(created.driverId.toString()).toBe('driver-1');
expect(created.position.toNumber()).toBe(1);
expect(created.fastestLap.toNumber()).toBe(100);
expect(created.incidents.toNumber()).toBe(0);
expect(created.startPosition.toNumber()).toBe(1);
expect(standingRepository.recalculate).toHaveBeenCalledWith('league-1');
});
});