refactor racing use cases
This commit is contained in:
@@ -1,78 +1,123 @@
|
||||
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
||||
import { RegisterForRaceUseCase } from './RegisterForRaceUseCase';
|
||||
import {
|
||||
RegisterForRaceErrorCode,
|
||||
RegisterForRaceInput,
|
||||
RegisterForRaceResult,
|
||||
RegisterForRaceUseCase,
|
||||
} from './RegisterForRaceUseCase';
|
||||
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
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,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return already registered error', async () => {
|
||||
registrationRepository.isRegistered.mockResolvedValue(true);
|
||||
|
||||
const result = await useCase.execute({ raceId: 'race-1', leagueId: 'league-1', driverId: 'driver-1' });
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.unwrapErr()).toEqual({
|
||||
code: 'ALREADY_REGISTERED',
|
||||
details: { message: 'Already registered for this race' },
|
||||
});
|
||||
const buildInput = (overrides: Partial<RegisterForRaceInput> = {}): RegisterForRaceInput => ({
|
||||
raceId: 'race-1',
|
||||
leagueId: 'league-1',
|
||||
driverId: 'driver-1',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
it('should return not active member error', async () => {
|
||||
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({ raceId: 'race-1', leagueId: 'league-1', driverId: 'driver-1' });
|
||||
const result = await useCase.execute(buildInput());
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.unwrapErr()).toEqual({
|
||||
code: 'NOT_ACTIVE_MEMBER',
|
||||
details: { message: 'Must be an active league member to register for races' },
|
||||
});
|
||||
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('should return not active member error for inactive membership', async () => {
|
||||
it('returns not active member error for inactive membership', async () => {
|
||||
registrationRepository.isRegistered.mockResolvedValue(false);
|
||||
membershipRepository.getMembership.mockResolvedValue({ status: 'inactive' });
|
||||
|
||||
const result = await useCase.execute({ raceId: 'race-1', leagueId: 'league-1', driverId: 'driver-1' });
|
||||
const result = await useCase.execute(buildInput());
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.unwrapErr()).toEqual({
|
||||
code: 'NOT_ACTIVE_MEMBER',
|
||||
details: { message: 'Must be an active league member to register for races' },
|
||||
});
|
||||
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('should register successfully', async () => {
|
||||
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({ raceId: 'race-1', leagueId: 'league-1', driverId: 'driver-1' });
|
||||
const result = await useCase.execute(buildInput());
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(registrationRepository.register).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
raceId: 'race-1',
|
||||
driverId: 'driver-1',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const presented = output.present.mock.calls[0][0] 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user