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 & { 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 & { 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 => ({ raceId: 'race-1', leagueId: 'league-1', driverId: 'driver-1', ...overrides, }); const unwrapErr = ( result: Result< void, ApplicationErrorCode< RegisterForRaceErrorCode, { message: string; } > >, ): ApplicationErrorCode => 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({ 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(); }); });