Files
gridpilot.gg/core/racing/application/use-cases/RegisterForRaceUseCase.test.ts
2025-12-23 15:38:50 +01:00

125 lines
4.8 KiB
TypeScript

import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import {
RegisterForRaceErrorCode,
RegisterForRaceInput,
RegisterForRaceResult,
RegisterForRaceUseCase,
} from './RegisterForRaceUseCase';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result } from '@core/shared/application/Result';
describe('RegisterForRaceUseCase', () => {
let useCase: RegisterForRaceUseCase;
let registrationRepository: { isRegistered: Mock; register: Mock };
let membershipRepository: { getMembership: Mock };
let logger: { debug: Mock; warn: Mock; error: Mock; info: Mock };
let output: UseCaseOutputPort<RegisterForRaceResult> & { present: Mock };
beforeEach(() => {
registrationRepository = { isRegistered: vi.fn(), register: vi.fn() };
membershipRepository = { getMembership: vi.fn() };
logger = { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() };
output = { present: vi.fn() } as unknown as UseCaseOutputPort<RegisterForRaceResult> & { present: Mock };
useCase = new RegisterForRaceUseCase(
registrationRepository as unknown as IRaceRegistrationRepository,
membershipRepository as unknown as ILeagueMembershipRepository,
logger as unknown as Logger,
output,
);
});
const buildInput = (overrides: Partial<RegisterForRaceInput> = {}): RegisterForRaceInput => ({
raceId: 'race-1',
leagueId: 'league-1',
driverId: 'driver-1',
...overrides,
});
const unwrapErr = (
result: Result<
void,
ApplicationErrorCode<
RegisterForRaceErrorCode,
{
message: string;
}
>
>,
): ApplicationErrorCode<RegisterForRaceErrorCode, { message: string }> => result.unwrapErr();
it('returns already registered error when driver is already registered', async () => {
registrationRepository.isRegistered.mockResolvedValue(true);
const result = await useCase.execute(buildInput());
expect(result.isErr()).toBe(true);
const error = unwrapErr(result);
expect(error.code).toBe('ALREADY_REGISTERED');
expect(error.details.message).toBe('Already registered for this race');
expect(output.present).not.toHaveBeenCalled();
});
it('returns not active member error when membership is missing', async () => {
registrationRepository.isRegistered.mockResolvedValue(false);
membershipRepository.getMembership.mockResolvedValue(null);
const result = await useCase.execute(buildInput());
expect(result.isErr()).toBe(true);
const error = unwrapErr(result);
expect(error.code).toBe('NOT_ACTIVE_MEMBER');
expect(error.details.message).toBe('Must be an active league member to register for races');
expect(output.present).not.toHaveBeenCalled();
});
it('returns not active member error for inactive membership', async () => {
registrationRepository.isRegistered.mockResolvedValue(false);
membershipRepository.getMembership.mockResolvedValue({ status: 'inactive' });
const result = await useCase.execute(buildInput());
expect(result.isErr()).toBe(true);
const error = unwrapErr(result);
expect(error.code).toBe('NOT_ACTIVE_MEMBER');
expect(error.details.message).toBe('Must be an active league member to register for races');
expect(output.present).not.toHaveBeenCalled();
});
it('registers successfully and presents result', async () => {
registrationRepository.isRegistered.mockResolvedValue(false);
membershipRepository.getMembership.mockResolvedValue({ status: 'active' });
registrationRepository.register.mockResolvedValue(undefined);
const result = await useCase.execute(buildInput());
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presentedRaw = output.present.mock.calls[0]?.[0];
expect(presentedRaw).toBeDefined();
const presented = presentedRaw as RegisterForRaceResult;
expect(presented).toEqual<RegisterForRaceResult>({
raceId: 'race-1',
driverId: 'driver-1',
status: 'registered',
});
});
it('wraps unexpected repository errors', async () => {
const error = new Error('db is down');
registrationRepository.isRegistered.mockRejectedValue(error);
const result = await useCase.execute(buildInput());
expect(result.isErr()).toBe(true);
const err = unwrapErr(result);
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('db is down');
expect(output.present).not.toHaveBeenCalled();
});
});