refactor racing use cases
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, Mock } from 'vitest';
|
||||
|
||||
import { Season } from '@core/racing/domain/entities/season/Season';
|
||||
import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
|
||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||
import {
|
||||
CreateSeasonForLeagueUseCase,
|
||||
type CreateSeasonForLeagueCommand,
|
||||
type CreateSeasonForLeagueInput,
|
||||
type CreateSeasonForLeagueResult,
|
||||
} from '@core/racing/application/use-cases/CreateSeasonForLeagueUseCase';
|
||||
import type { LeagueConfigFormModel } from '@core/racing/application/dto/LeagueConfigFormDTO';
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
|
||||
function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>): LeagueConfigFormModel {
|
||||
return {
|
||||
@@ -66,6 +70,8 @@ function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>)
|
||||
};
|
||||
}
|
||||
|
||||
type CreateSeasonErrorCode = ApplicationErrorCode<'LEAGUE_NOT_FOUND' | 'VALIDATION_ERROR' | 'REPOSITORY_ERROR'>;
|
||||
|
||||
describe('CreateSeasonForLeagueUseCase', () => {
|
||||
const mockLeagueFindById = vi.fn();
|
||||
const mockLeagueRepo: ILeagueRepository = {
|
||||
@@ -91,15 +97,18 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
||||
listActiveByLeague: vi.fn(),
|
||||
};
|
||||
|
||||
let output: { present: Mock } & UseCaseOutputPort<CreateSeasonForLeagueResult>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
output = { present: vi.fn() } as unknown as typeof output;
|
||||
});
|
||||
|
||||
it('creates a planned Season for an existing league with config-derived props', async () => {
|
||||
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
||||
mockSeasonAdd.mockResolvedValue(undefined);
|
||||
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo, output);
|
||||
|
||||
const config = createLeagueConfigFormModel({
|
||||
basics: {
|
||||
@@ -124,17 +133,22 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const command: CreateSeasonForLeagueCommand = {
|
||||
const command: CreateSeasonForLeagueInput = {
|
||||
leagueId: 'league-1',
|
||||
name: 'Season from Config',
|
||||
gameId: 'iracing',
|
||||
config,
|
||||
};
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.value!.seasonId).toBeDefined();
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const presented = output.present.mock.calls[0][0] as CreateSeasonForLeagueResult;
|
||||
expect(presented.season).toBeInstanceOf(Season);
|
||||
expect(presented.league.id).toBe('league-1');
|
||||
});
|
||||
|
||||
it('clones configuration from a source season when sourceSeasonId is provided', async () => {
|
||||
@@ -150,18 +164,64 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
||||
mockSeasonFindById.mockResolvedValue(sourceSeason);
|
||||
mockSeasonAdd.mockResolvedValue(undefined);
|
||||
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo, output);
|
||||
|
||||
const command: CreateSeasonForLeagueCommand = {
|
||||
const command: CreateSeasonForLeagueInput = {
|
||||
leagueId: 'league-1',
|
||||
name: 'Cloned Season',
|
||||
gameId: 'iracing',
|
||||
sourceSeasonId: 'source-season',
|
||||
};
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.value!.seasonId).toBeDefined();
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const presented = output.present.mock.calls[0][0] as CreateSeasonForLeagueResult;
|
||||
expect(presented.season.maxDrivers).toBe(40);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error when league not found and does not call output', async () => {
|
||||
mockLeagueFindById.mockResolvedValue(null);
|
||||
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo, output);
|
||||
|
||||
const command: CreateSeasonForLeagueInput = {
|
||||
leagueId: 'missing-league',
|
||||
name: 'Any',
|
||||
gameId: 'iracing',
|
||||
};
|
||||
|
||||
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
const error = result.unwrapErr();
|
||||
expect(error.code).toBe('LEAGUE_NOT_FOUND');
|
||||
expect(error.details?.message).toBe('League not found: missing-league');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns validation error when source season is missing and does not call output', async () => {
|
||||
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
||||
mockSeasonFindById.mockResolvedValue(undefined);
|
||||
|
||||
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo, output);
|
||||
|
||||
const command: CreateSeasonForLeagueInput = {
|
||||
leagueId: 'league-1',
|
||||
name: 'Cloned Season',
|
||||
gameId: 'iracing',
|
||||
sourceSeasonId: 'missing-source',
|
||||
};
|
||||
|
||||
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
const error = result.unwrapErr();
|
||||
expect(error.code).toBe('VALIDATION_ERROR');
|
||||
expect(error.details?.message).toBe('Source Season not found: missing-source');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user