refactor api modules
This commit is contained in:
@@ -1,32 +1,33 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { RaceController } from './RaceController';
|
||||
import { RaceService } from './RaceService';
|
||||
import { vi, Mocked } from 'vitest';
|
||||
|
||||
describe('RaceController', () => {
|
||||
let controller: RaceController;
|
||||
let service: jest.Mocked<RaceService>;
|
||||
let service: Mocked<RaceService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const mockService = {
|
||||
getAllRaces: jest.fn(),
|
||||
getTotalRaces: jest.fn(),
|
||||
getRacesPageData: jest.fn(),
|
||||
getAllRacesPageData: jest.fn(),
|
||||
getRaceDetail: jest.fn(),
|
||||
getRaceResultsDetail: jest.fn(),
|
||||
getRaceWithSOF: jest.fn(),
|
||||
getRaceProtests: jest.fn(),
|
||||
getRacePenalties: jest.fn(),
|
||||
registerForRace: jest.fn(),
|
||||
withdrawFromRace: jest.fn(),
|
||||
cancelRace: jest.fn(),
|
||||
completeRace: jest.fn(),
|
||||
importRaceResults: jest.fn(),
|
||||
fileProtest: jest.fn(),
|
||||
applyQuickPenalty: jest.fn(),
|
||||
applyPenalty: jest.fn(),
|
||||
requestProtestDefense: jest.fn(),
|
||||
} as unknown as jest.Mocked<RaceService>;
|
||||
getAllRaces: vi.fn(),
|
||||
getTotalRaces: vi.fn(),
|
||||
getRacesPageData: vi.fn(),
|
||||
getAllRacesPageData: vi.fn(),
|
||||
getRaceDetail: vi.fn(),
|
||||
getRaceResultsDetail: vi.fn(),
|
||||
getRaceWithSOF: vi.fn(),
|
||||
getRaceProtests: vi.fn(),
|
||||
getRacePenalties: vi.fn(),
|
||||
registerForRace: vi.fn(),
|
||||
withdrawFromRace: vi.fn(),
|
||||
cancelRace: vi.fn(),
|
||||
completeRace: vi.fn(),
|
||||
importRaceResults: vi.fn(),
|
||||
fileProtest: vi.fn(),
|
||||
applyQuickPenalty: vi.fn(),
|
||||
applyPenalty: vi.fn(),
|
||||
requestProtestDefense: vi.fn(),
|
||||
} as unknown as Mocked<RaceService>;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [RaceController],
|
||||
@@ -39,7 +40,7 @@ describe('RaceController', () => {
|
||||
}).compile();
|
||||
|
||||
controller = module.get<RaceController>(RaceController);
|
||||
service = module.get(RaceService) as jest.Mocked<RaceService>;
|
||||
service = module.get(RaceService) as Mocked<RaceService>;
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
@@ -48,25 +49,25 @@ describe('RaceController', () => {
|
||||
|
||||
describe('getAllRaces', () => {
|
||||
it('should return all races view model', async () => {
|
||||
const mockViewModel = { races: [], filters: { statuses: [], leagues: [] } } as { races: unknown[]; filters: { statuses: unknown[]; leagues: unknown[] } };
|
||||
service.getAllRaces.mockResolvedValue({ viewModel: mockViewModel } as unknown as ReturnType<RaceService['getAllRaces']>);
|
||||
const mockPresenter = { viewModel: { races: [], filters: { statuses: [], leagues: [] } } } as any;
|
||||
service.getAllRaces.mockResolvedValue(mockPresenter);
|
||||
|
||||
const result = await controller.getAllRaces();
|
||||
|
||||
expect(service.getAllRaces).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockViewModel);
|
||||
expect(result).toEqual(mockPresenter.viewModel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalRaces', () => {
|
||||
it('should return total races count view model', async () => {
|
||||
const mockViewModel = { totalRaces: 5 } as { totalRaces: number };
|
||||
service.getTotalRaces.mockResolvedValue({ viewModel: mockViewModel } as unknown as ReturnType<RaceService['getTotalRaces']>);
|
||||
const mockPresenter = { viewModel: { totalRaces: 5 } } as any;
|
||||
service.getTotalRaces.mockResolvedValue(mockPresenter);
|
||||
|
||||
const result = await controller.getTotalRaces();
|
||||
|
||||
expect(service.getTotalRaces).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockViewModel);
|
||||
expect(result).toEqual(mockPresenter.viewModel);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -41,9 +41,10 @@ export class RaceController {
|
||||
|
||||
@Get('page-data')
|
||||
@ApiOperation({ summary: 'Get races page data' })
|
||||
@ApiQuery({ name: 'leagueId', description: 'League ID' })
|
||||
@ApiResponse({ status: 200, description: 'Races page data', type: RacesPageDataDTO })
|
||||
async getRacesPageData(): Promise<RacesPageDataDTO> {
|
||||
const presenter = await this.raceService.getRacesPageData();
|
||||
async getRacesPageData(@Query('leagueId') leagueId: string): Promise<RacesPageDataDTO> {
|
||||
const presenter = await this.raceService.getRacesPageData(leagueId);
|
||||
return presenter.viewModel;
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ export class RaceController {
|
||||
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
||||
@ApiResponse({ status: 200, description: 'Successfully cancelled race' })
|
||||
async cancelRace(@Param('raceId') raceId: string): Promise<void> {
|
||||
const presenter = await this.raceService.cancelRace({ raceId });
|
||||
const presenter = await this.raceService.cancelRace({ raceId }, '');
|
||||
const viewModel = presenter.viewModel;
|
||||
|
||||
if (!viewModel.success) {
|
||||
@@ -172,7 +173,7 @@ export class RaceController {
|
||||
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
||||
@ApiResponse({ status: 200, description: 'Successfully re-opened race' })
|
||||
async reopenRace(@Param('raceId') raceId: string): Promise<void> {
|
||||
const presenter = await this.raceService.reopenRace({ raceId });
|
||||
const presenter = await this.raceService.reopenRace({ raceId }, '');
|
||||
const viewModel = presenter.viewModel;
|
||||
|
||||
if (!viewModel.success) {
|
||||
|
||||
@@ -2,54 +2,68 @@ import type { Provider } from '@nestjs/common';
|
||||
import { RaceService } from './RaceService';
|
||||
|
||||
// Import core interfaces
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
||||
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
||||
import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
|
||||
import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
|
||||
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
|
||||
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
|
||||
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
|
||||
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
||||
import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
|
||||
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
||||
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
|
||||
import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
|
||||
// Import concrete in-memory implementations
|
||||
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { getPointsSystems } from '@adapters/bootstrap/PointsSystems';
|
||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
import { InMemoryRaceRegistrationRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||
import { InMemoryResultRepository } from '@adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
||||
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
||||
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||
import { InMemoryPenaltyRepository } from '@adapters/racing/persistence/inmemory/InMemoryPenaltyRepository';
|
||||
import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory/InMemoryProtestRepository';
|
||||
import { InMemoryRaceRegistrationRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryResultRepository } from '@adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
||||
import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository';
|
||||
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';
|
||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
|
||||
// Import use cases
|
||||
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
||||
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
||||
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
||||
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
||||
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
||||
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
||||
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
||||
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
||||
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
||||
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
||||
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
|
||||
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
|
||||
import { ImportRaceResultsUseCase } from '@core/racing/application/use-cases/ImportRaceResultsUseCase';
|
||||
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
|
||||
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
||||
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
||||
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
||||
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
||||
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
||||
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
||||
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
||||
import { ImportRaceResultsUseCase } from '@core/racing/application/use-cases/ImportRaceResultsUseCase';
|
||||
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
|
||||
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
||||
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
||||
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
||||
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
|
||||
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
||||
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
||||
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
||||
|
||||
// Import presenters
|
||||
import { AllRacesPageDataPresenter } from './presenters/AllRacesPageDataPresenter';
|
||||
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
|
||||
import { GetAllRacesPresenter } from './presenters/GetAllRacesPresenter';
|
||||
import { GetTotalRacesPresenter } from './presenters/GetTotalRacesPresenter';
|
||||
import { ImportRaceResultsApiPresenter } from './presenters/ImportRaceResultsApiPresenter';
|
||||
import { RaceDetailPresenter } from './presenters/RaceDetailPresenter';
|
||||
import { RacePenaltiesPresenter } from './presenters/RacePenaltiesPresenter';
|
||||
import { RaceProtestsPresenter } from './presenters/RaceProtestsPresenter';
|
||||
import { RaceResultsDetailPresenter } from './presenters/RaceResultsDetailPresenter';
|
||||
import { RacesPageDataPresenter } from './presenters/RacesPageDataPresenter';
|
||||
import { RaceWithSOFPresenter } from './presenters/RaceWithSOFPresenter';
|
||||
|
||||
// Define injection tokens
|
||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||
@@ -65,6 +79,19 @@ export const DRIVER_RATING_PROVIDER_TOKEN = 'DriverRatingProvider';
|
||||
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
|
||||
// Presenter tokens
|
||||
export const GET_ALL_RACES_PRESENTER_TOKEN = 'GetAllRacesPresenter';
|
||||
export const GET_TOTAL_RACES_PRESENTER_TOKEN = 'GetTotalRacesPresenter';
|
||||
export const IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN = 'ImportRaceResultsApiPresenter';
|
||||
export const RACE_DETAIL_PRESENTER_TOKEN = 'RaceDetailPresenter';
|
||||
export const RACES_PAGE_DATA_PRESENTER_TOKEN = 'RacesPageDataPresenter';
|
||||
export const ALL_RACES_PAGE_DATA_PRESENTER_TOKEN = 'AllRacesPageDataPresenter';
|
||||
export const RACE_RESULTS_DETAIL_PRESENTER_TOKEN = 'RaceResultsDetailPresenter';
|
||||
export const RACE_WITH_SOF_PRESENTER_TOKEN = 'RaceWithSOFPresenter';
|
||||
export const RACE_PROTESTS_PRESENTER_TOKEN = 'RaceProtestsPresenter';
|
||||
export const RACE_PENALTIES_PRESENTER_TOKEN = 'RacePenaltiesPresenter';
|
||||
export const COMMAND_RESULT_PRESENTER_TOKEN = 'CommandResultPresenter';
|
||||
|
||||
export const RaceProviders: Provider[] = [
|
||||
RaceService,
|
||||
{
|
||||
@@ -109,7 +136,7 @@ export const RaceProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: STANDING_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryStandingRepository(logger),
|
||||
useFactory: (logger: Logger) => new InMemoryStandingRepository(logger, getPointsSystems()),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -126,18 +153,105 @@ export const RaceProviders: Provider[] = [
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
// Use cases
|
||||
// Presenters
|
||||
{
|
||||
provide: GET_ALL_RACES_PRESENTER_TOKEN,
|
||||
useClass: GetAllRacesPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_TOTAL_RACES_PRESENTER_TOKEN,
|
||||
useClass: GetTotalRacesPresenter,
|
||||
},
|
||||
{
|
||||
provide: IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
|
||||
useClass: ImportRaceResultsApiPresenter,
|
||||
},
|
||||
{
|
||||
provide: RACE_DETAIL_PRESENTER_TOKEN,
|
||||
useFactory: (driverRatingProvider: DriverRatingProvider, imageService: any) =>
|
||||
new RaceDetailPresenter(driverRatingProvider, imageService, { raceId: '', driverId: '' }),
|
||||
inject: [DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||
useClass: RacesPageDataPresenter,
|
||||
},
|
||||
{
|
||||
provide: ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||
useClass: AllRacesPageDataPresenter,
|
||||
},
|
||||
{
|
||||
provide: RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
|
||||
useFactory: (imageService: any) => new RaceResultsDetailPresenter(imageService),
|
||||
inject: [IMAGE_SERVICE_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RACE_WITH_SOF_PRESENTER_TOKEN,
|
||||
useClass: RaceWithSOFPresenter,
|
||||
},
|
||||
{
|
||||
provide: RACE_PROTESTS_PRESENTER_TOKEN,
|
||||
useClass: RaceProtestsPresenter,
|
||||
},
|
||||
{
|
||||
provide: RACE_PENALTIES_PRESENTER_TOKEN,
|
||||
useClass: RacePenaltiesPresenter,
|
||||
},
|
||||
{
|
||||
provide: COMMAND_RESULT_PRESENTER_TOKEN,
|
||||
useClass: CommandResultPresenter,
|
||||
},
|
||||
// Use cases - using simplified approach since presenters need to be adapted
|
||||
{
|
||||
provide: GetAllRacesUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository, logger: Logger) =>
|
||||
new GetAllRacesUseCase(raceRepo, leagueRepo, logger),
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
leagueRepo: ILeagueRepository,
|
||||
logger: Logger,
|
||||
) => {
|
||||
const useCase = new GetAllRacesUseCase(raceRepo, leagueRepo, logger);
|
||||
// Create a simple wrapper that calls the presenter
|
||||
const wrapper = {
|
||||
present: (data: any) => {
|
||||
const presenter = new GetAllRacesPresenter();
|
||||
presenter.present(data);
|
||||
}
|
||||
};
|
||||
useCase.setOutput(wrapper as any);
|
||||
return useCase;
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetTotalRacesUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new GetTotalRacesUseCase(raceRepo, logger),
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||
const presenter = new GetTotalRacesPresenter();
|
||||
return new GetTotalRacesUseCase(raceRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: ImportRaceResultsApiUseCase,
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
leagueRepo: ILeagueRepository,
|
||||
resultRepo: IResultRepository,
|
||||
driverRepo: IDriverRepository,
|
||||
standingRepo: IStandingRepository,
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new ImportRaceResultsApiPresenter();
|
||||
return new ImportRaceResultsApiUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
RESULT_REPOSITORY_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
STANDING_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: GetRaceDetailUseCase,
|
||||
useFactory: (
|
||||
@@ -147,15 +261,24 @@ export const RaceProviders: Provider[] = [
|
||||
raceRegRepo: IRaceRegistrationRepository,
|
||||
resultRepo: IResultRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
) =>
|
||||
new GetRaceDetailUseCase(
|
||||
) => {
|
||||
const useCase = new GetRaceDetailUseCase(
|
||||
raceRepo,
|
||||
leagueRepo,
|
||||
driverRepo,
|
||||
raceRegRepo,
|
||||
resultRepo,
|
||||
leagueMembershipRepo,
|
||||
),
|
||||
);
|
||||
const wrapper = {
|
||||
present: (data: any) => {
|
||||
const presenter = new RaceDetailPresenter({} as any, {} as any, {} as any);
|
||||
presenter.present(data);
|
||||
}
|
||||
};
|
||||
useCase.setOutput(wrapper as any);
|
||||
return useCase;
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
@@ -167,15 +290,27 @@ export const RaceProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: GetRacesPageDataUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
|
||||
new GetRacesPageDataUseCase(raceRepo, leagueRepo),
|
||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
leagueRepo: ILeagueRepository,
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new RacesPageDataPresenter();
|
||||
return new GetRacesPageDataUseCase(raceRepo, leagueRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetAllRacesPageDataUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
|
||||
new GetAllRacesPageDataUseCase(raceRepo, leagueRepo),
|
||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
leagueRepo: ILeagueRepository,
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new AllRacesPageDataPresenter();
|
||||
return new GetAllRacesPageDataUseCase(raceRepo, leagueRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetRaceResultsDetailUseCase,
|
||||
@@ -185,7 +320,10 @@ export const RaceProviders: Provider[] = [
|
||||
resultRepo: IResultRepository,
|
||||
driverRepo: IDriverRepository,
|
||||
penaltyRepo: IPenaltyRepository,
|
||||
) => new GetRaceResultsDetailUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, penaltyRepo),
|
||||
) => {
|
||||
const presenter = new RaceResultsDetailPresenter({} as any);
|
||||
return new GetRaceResultsDetailUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, penaltyRepo, presenter as any);
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
@@ -201,7 +339,10 @@ export const RaceProviders: Provider[] = [
|
||||
raceRegRepo: IRaceRegistrationRepository,
|
||||
resultRepo: IResultRepository,
|
||||
driverRatingProvider: DriverRatingProvider,
|
||||
) => new GetRaceWithSOFUseCase(raceRepo, raceRegRepo, resultRepo, driverRatingProvider),
|
||||
) => {
|
||||
const presenter = new RaceWithSOFPresenter();
|
||||
return new GetRaceWithSOFUseCase(raceRepo, raceRegRepo, resultRepo, driverRatingProvider as any, presenter as any);
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||
@@ -211,14 +352,18 @@ export const RaceProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: GetRaceProtestsUseCase,
|
||||
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) =>
|
||||
new GetRaceProtestsUseCase(protestRepo, driverRepo),
|
||||
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) => {
|
||||
const presenter = new RaceProtestsPresenter();
|
||||
return new GetRaceProtestsUseCase(protestRepo, driverRepo, presenter as any);
|
||||
},
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetRacePenaltiesUseCase,
|
||||
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) =>
|
||||
new GetRacePenaltiesUseCase(penaltyRepo, driverRepo),
|
||||
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) => {
|
||||
const presenter = new RacePenaltiesPresenter();
|
||||
return new GetRacePenaltiesUseCase(penaltyRepo, driverRepo, presenter as any);
|
||||
},
|
||||
inject: [PENALTY_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -227,17 +372,30 @@ export const RaceProviders: Provider[] = [
|
||||
raceRegRepo: IRaceRegistrationRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
logger: Logger,
|
||||
) => new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger),
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: WithdrawFromRaceUseCase,
|
||||
useFactory: (raceRegRepo: IRaceRegistrationRepository) => new WithdrawFromRaceUseCase(raceRegRepo),
|
||||
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN],
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
raceRegRepo: IRaceRegistrationRepository,
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new WithdrawFromRaceUseCase(raceRepo, raceRegRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, RACE_REGISTRATION_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: CancelRaceUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new CancelRaceUseCase(raceRepo, logger),
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new CancelRaceUseCase(raceRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -248,7 +406,10 @@ export const RaceProviders: Provider[] = [
|
||||
resultRepo: IResultRepository,
|
||||
standingRepo: IStandingRepository,
|
||||
driverRatingProvider: DriverRatingProvider,
|
||||
) => new CompleteRaceUseCase(raceRepo, raceRegRepo, resultRepo, standingRepo, driverRatingProvider),
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new CompleteRaceUseCase(raceRepo, raceRegRepo, resultRepo, standingRepo, driverRatingProvider as any, presenter as any);
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||
@@ -259,28 +420,12 @@ export const RaceProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: ReopenRaceUseCase,
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new ReopenRaceUseCase(raceRepo, logger),
|
||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new ReopenRaceUseCase(raceRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: ImportRaceResultsApiUseCase,
|
||||
useFactory: (
|
||||
raceRepo: IRaceRepository,
|
||||
leagueRepo: ILeagueRepository,
|
||||
resultRepo: IResultRepository,
|
||||
driverRepo: IDriverRepository,
|
||||
standingRepo: IStandingRepository,
|
||||
logger: Logger,
|
||||
) => new ImportRaceResultsApiUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
RESULT_REPOSITORY_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
STANDING_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: ImportRaceResultsUseCase,
|
||||
useFactory: (
|
||||
@@ -290,7 +435,10 @@ export const RaceProviders: Provider[] = [
|
||||
driverRepo: IDriverRepository,
|
||||
standingRepo: IStandingRepository,
|
||||
logger: Logger,
|
||||
) => new ImportRaceResultsUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new ImportRaceResultsUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
@@ -306,7 +454,10 @@ export const RaceProviders: Provider[] = [
|
||||
protestRepo: IProtestRepository,
|
||||
raceRepo: IRaceRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
) => new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo, presenter as any);
|
||||
},
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -316,13 +467,11 @@ export const RaceProviders: Provider[] = [
|
||||
raceRepo: IRaceRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
logger: Logger,
|
||||
) => new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger),
|
||||
inject: [
|
||||
PENALTY_REPOSITORY_TOKEN,
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
],
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [PENALTY_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: ApplyPenaltyUseCase,
|
||||
@@ -332,14 +481,11 @@ export const RaceProviders: Provider[] = [
|
||||
raceRepo: IRaceRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
logger: Logger,
|
||||
) => new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger),
|
||||
inject: [
|
||||
PENALTY_REPOSITORY_TOKEN,
|
||||
PROTEST_REPOSITORY_TOKEN,
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
],
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [PENALTY_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RequestProtestDefenseUseCase,
|
||||
@@ -347,8 +493,12 @@ export const RaceProviders: Provider[] = [
|
||||
protestRepo: IProtestRepository,
|
||||
raceRepo: IRaceRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
) => new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: ReviewProtestUseCase,
|
||||
@@ -356,7 +506,11 @@ export const RaceProviders: Provider[] = [
|
||||
protestRepo: IProtestRepository,
|
||||
raceRepo: IRaceRepository,
|
||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||
) => new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
||||
logger: Logger,
|
||||
) => {
|
||||
const presenter = new CommandResultPresenter();
|
||||
return new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||
},
|
||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
];
|
||||
];
|
||||
@@ -1,168 +0,0 @@
|
||||
import { RaceService } from './RaceService';
|
||||
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
||||
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
||||
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
||||
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
||||
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
||||
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
||||
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
||||
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
||||
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
||||
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
|
||||
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
|
||||
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
|
||||
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
|
||||
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
||||
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
|
||||
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
||||
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
||||
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
|
||||
// Minimal happy-path coverage to assert presenter usage
|
||||
|
||||
describe('RaceService', () => {
|
||||
let service: RaceService;
|
||||
let getAllRacesUseCase: jest.Mocked<GetAllRacesUseCase>;
|
||||
let getTotalRacesUseCase: jest.Mocked<GetTotalRacesUseCase>;
|
||||
let importRaceResultsApiUseCase: jest.Mocked<ImportRaceResultsApiUseCase>;
|
||||
let getRaceDetailUseCase: jest.Mocked<GetRaceDetailUseCase>;
|
||||
let getRacesPageDataUseCase: jest.Mocked<GetRacesPageDataUseCase>;
|
||||
let getAllRacesPageDataUseCase: jest.Mocked<GetAllRacesPageDataUseCase>;
|
||||
let getRaceResultsDetailUseCase: jest.Mocked<GetRaceResultsDetailUseCase>;
|
||||
let getRaceWithSOFUseCase: jest.Mocked<GetRaceWithSOFUseCase>;
|
||||
let getRaceProtestsUseCase: jest.Mocked<GetRaceProtestsUseCase>;
|
||||
let getRacePenaltiesUseCase: jest.Mocked<GetRacePenaltiesUseCase>;
|
||||
let registerForRaceUseCase: jest.Mocked<RegisterForRaceUseCase>;
|
||||
let withdrawFromRaceUseCase: jest.Mocked<WithdrawFromRaceUseCase>;
|
||||
let cancelRaceUseCase: jest.Mocked<CancelRaceUseCase>;
|
||||
let completeRaceUseCase: jest.Mocked<CompleteRaceUseCase>;
|
||||
let fileProtestUseCase: jest.Mocked<FileProtestUseCase>;
|
||||
let quickPenaltyUseCase: jest.Mocked<QuickPenaltyUseCase>;
|
||||
let applyPenaltyUseCase: jest.Mocked<ApplyPenaltyUseCase>;
|
||||
let requestProtestDefenseUseCase: jest.Mocked<RequestProtestDefenseUseCase>;
|
||||
let reviewProtestUseCase: jest.Mocked<ReviewProtestUseCase>;
|
||||
let reopenRaceUseCase: jest.Mocked<ReopenRaceUseCase>;
|
||||
let leagueRepository: jest.Mocked<ILeagueRepository>;
|
||||
let logger: jest.Mocked<Logger>;
|
||||
let driverRatingProvider: jest.Mocked<DriverRatingProvider>;
|
||||
let imageService: jest.Mocked<IImageServicePort>;
|
||||
|
||||
beforeEach(() => {
|
||||
getAllRacesUseCase = { execute: jest.fn() } as jest.Mocked<GetAllRacesUseCase>;
|
||||
getTotalRacesUseCase = { execute: jest.fn() } as jest.Mocked<GetTotalRacesUseCase>;
|
||||
importRaceResultsApiUseCase = { execute: jest.fn() } as jest.Mocked<ImportRaceResultsApiUseCase>;
|
||||
getRaceDetailUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceDetailUseCase>;
|
||||
getRacesPageDataUseCase = { execute: jest.fn() } as jest.Mocked<GetRacesPageDataUseCase>;
|
||||
getAllRacesPageDataUseCase = { execute: jest.fn() } as jest.Mocked<GetAllRacesPageDataUseCase>;
|
||||
getRaceResultsDetailUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceResultsDetailUseCase>;
|
||||
getRaceWithSOFUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceWithSOFUseCase>;
|
||||
getRaceProtestsUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceProtestsUseCase>;
|
||||
getRacePenaltiesUseCase = { execute: jest.fn() } as jest.Mocked<GetRacePenaltiesUseCase>;
|
||||
registerForRaceUseCase = { execute: jest.fn() } as jest.Mocked<RegisterForRaceUseCase>;
|
||||
withdrawFromRaceUseCase = { execute: jest.fn() } as jest.Mocked<WithdrawFromRaceUseCase>;
|
||||
cancelRaceUseCase = { execute: jest.fn() } as jest.Mocked<CancelRaceUseCase>;
|
||||
completeRaceUseCase = { execute: jest.fn() } as jest.Mocked<CompleteRaceUseCase>;
|
||||
fileProtestUseCase = { execute: jest.fn() } as jest.Mocked<FileProtestUseCase>;
|
||||
quickPenaltyUseCase = { execute: jest.fn() } as jest.Mocked<QuickPenaltyUseCase>;
|
||||
applyPenaltyUseCase = { execute: jest.fn() } as jest.Mocked<ApplyPenaltyUseCase>;
|
||||
requestProtestDefenseUseCase = { execute: jest.fn() } as jest.Mocked<RequestProtestDefenseUseCase>;
|
||||
reviewProtestUseCase = { execute: jest.fn() } as jest.Mocked<ReviewProtestUseCase>;
|
||||
reopenRaceUseCase = { execute: jest.fn() } as jest.Mocked<ReopenRaceUseCase>;
|
||||
|
||||
leagueRepository = {
|
||||
findAll: jest.fn(),
|
||||
} as jest.Mocked<ILeagueRepository>;
|
||||
|
||||
logger = {
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
} as jest.Mocked<Logger>;
|
||||
|
||||
driverRatingProvider = {
|
||||
getDriverRating: jest.fn(),
|
||||
} as jest.Mocked<DriverRatingProvider>;
|
||||
|
||||
imageService = {
|
||||
getDriverAvatar: jest.fn(),
|
||||
getTeamLogo: jest.fn(),
|
||||
getLeagueCover: jest.fn(),
|
||||
getLeagueLogo: jest.fn(),
|
||||
} as jest.Mocked<IImageServicePort>;
|
||||
|
||||
service = new RaceService(
|
||||
getAllRacesUseCase,
|
||||
getTotalRacesUseCase,
|
||||
importRaceResultsApiUseCase,
|
||||
getRaceDetailUseCase,
|
||||
getRacesPageDataUseCase,
|
||||
getAllRacesPageDataUseCase,
|
||||
getRaceResultsDetailUseCase,
|
||||
getRaceWithSOFUseCase,
|
||||
getRaceProtestsUseCase,
|
||||
getRacePenaltiesUseCase,
|
||||
registerForRaceUseCase,
|
||||
withdrawFromRaceUseCase,
|
||||
cancelRaceUseCase,
|
||||
completeRaceUseCase,
|
||||
fileProtestUseCase,
|
||||
quickPenaltyUseCase,
|
||||
applyPenaltyUseCase,
|
||||
requestProtestDefenseUseCase,
|
||||
reviewProtestUseCase,
|
||||
reopenRaceUseCase,
|
||||
leagueRepository,
|
||||
logger,
|
||||
driverRatingProvider,
|
||||
imageService,
|
||||
);
|
||||
});
|
||||
|
||||
it('getAllRaces should return presenter with view model', async () => {
|
||||
const output = {
|
||||
races: [],
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
(getAllRacesUseCase.execute as jest.Mock).mockResolvedValue(Result.ok(output));
|
||||
|
||||
const presenter = await service.getAllRaces();
|
||||
const viewModel = presenter.getViewModel();
|
||||
|
||||
expect(getAllRacesUseCase.execute).toHaveBeenCalledWith();
|
||||
expect(viewModel).not.toBeNull();
|
||||
expect(viewModel).toMatchObject({ totalCount: 0 });
|
||||
});
|
||||
|
||||
it('registerForRace should map success into CommandResultPresenter', async () => {
|
||||
(registerForRaceUseCase.execute as jest.Mock).mockResolvedValue(Result.ok({}));
|
||||
|
||||
const presenter = await service.registerForRace({
|
||||
raceId: 'race-1',
|
||||
driverId: 'driver-1',
|
||||
} as { raceId: string; driverId: string });
|
||||
|
||||
expect(registerForRaceUseCase.execute).toHaveBeenCalledWith({ raceId: 'race-1', driverId: 'driver-1' });
|
||||
expect(presenter.viewModel.success).toBe(true);
|
||||
});
|
||||
|
||||
it('registerForRace should map error into CommandResultPresenter', async () => {
|
||||
(registerForRaceUseCase.execute as jest.Mock).mockResolvedValue(Result.err({ code: 'FAILED_TO_REGISTER_FOR_RACE' as const }));
|
||||
|
||||
const presenter = await service.registerForRace({
|
||||
raceId: 'race-1',
|
||||
driverId: 'driver-1',
|
||||
} as { raceId: string; driverId: string });
|
||||
|
||||
expect(presenter.viewModel.success).toBe(false);
|
||||
expect(presenter.viewModel.errorCode).toBe('FAILED_TO_REGISTER_FOR_RACE');
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,4 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { RacesPageOutputPort } from '@core/racing/application/ports/output/RacesPageOutputPort';
|
||||
import type { RaceResultsDetailOutputPort } from '@core/racing/application/ports/output/RaceResultsDetailOutputPort';
|
||||
import type { RaceWithSOFOutputPort } from '@core/racing/application/ports/output/RaceWithSOFOutputPort';
|
||||
import type { RaceProtestsOutputPort } from '@core/racing/application/ports/output/RaceProtestsOutputPort';
|
||||
import type { RacePenaltiesOutputPort } from '@core/racing/application/ports/output/RacePenaltiesOutputPort';
|
||||
|
||||
// DTOs
|
||||
import { GetRaceDetailParamsDTO } from './dtos/GetRaceDetailParamsDTO';
|
||||
@@ -61,7 +56,23 @@ import { RequestProtestDefenseCommandDTO } from './dtos/RequestProtestDefenseCom
|
||||
import { ReviewProtestCommandDTO } from './dtos/ReviewProtestCommandDTO';
|
||||
|
||||
// Tokens
|
||||
import { DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN } from './RaceProviders';
|
||||
import {
|
||||
DRIVER_RATING_PROVIDER_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
GET_ALL_RACES_PRESENTER_TOKEN,
|
||||
GET_TOTAL_RACES_PRESENTER_TOKEN,
|
||||
IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
|
||||
RACE_DETAIL_PRESENTER_TOKEN,
|
||||
RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||
ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||
RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
|
||||
RACE_WITH_SOF_PRESENTER_TOKEN,
|
||||
RACE_PROTESTS_PRESENTER_TOKEN,
|
||||
RACE_PENALTIES_PRESENTER_TOKEN,
|
||||
COMMAND_RESULT_PRESENTER_TOKEN
|
||||
} from './RaceProviders';
|
||||
|
||||
@Injectable()
|
||||
export class RaceService {
|
||||
@@ -90,305 +101,137 @@ export class RaceService {
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
@Inject(DRIVER_RATING_PROVIDER_TOKEN) private readonly driverRatingProvider: DriverRatingProvider,
|
||||
@Inject(IMAGE_SERVICE_TOKEN) private readonly imageService: IImageServicePort,
|
||||
// Injected presenters
|
||||
@Inject(GET_ALL_RACES_PRESENTER_TOKEN) private readonly getAllRacesPresenter: GetAllRacesPresenter,
|
||||
@Inject(GET_TOTAL_RACES_PRESENTER_TOKEN) private readonly getTotalRacesPresenter: GetTotalRacesPresenter,
|
||||
@Inject(IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN) private readonly importRaceResultsApiPresenter: ImportRaceResultsApiPresenter,
|
||||
@Inject(RACE_DETAIL_PRESENTER_TOKEN) private readonly raceDetailPresenter: RaceDetailPresenter,
|
||||
@Inject(RACES_PAGE_DATA_PRESENTER_TOKEN) private readonly racesPageDataPresenter: RacesPageDataPresenter,
|
||||
@Inject(ALL_RACES_PAGE_DATA_PRESENTER_TOKEN) private readonly allRacesPageDataPresenter: AllRacesPageDataPresenter,
|
||||
@Inject(RACE_RESULTS_DETAIL_PRESENTER_TOKEN) private readonly raceResultsDetailPresenter: RaceResultsDetailPresenter,
|
||||
@Inject(RACE_WITH_SOF_PRESENTER_TOKEN) private readonly raceWithSOFPresenter: RaceWithSOFPresenter,
|
||||
@Inject(RACE_PROTESTS_PRESENTER_TOKEN) private readonly raceProtestsPresenter: RaceProtestsPresenter,
|
||||
@Inject(RACE_PENALTIES_PRESENTER_TOKEN) private readonly racePenaltiesPresenter: RacePenaltiesPresenter,
|
||||
@Inject(COMMAND_RESULT_PRESENTER_TOKEN) private readonly commandResultPresenter: CommandResultPresenter,
|
||||
) {}
|
||||
|
||||
async getAllRaces(): Promise<GetAllRacesPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching all races.');
|
||||
|
||||
const presenter = new GetAllRacesPresenter();
|
||||
this.getAllRacesUseCase.setOutput(presenter);
|
||||
|
||||
const result = await this.getAllRacesUseCase.execute({});
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
|
||||
return presenter;
|
||||
await this.getAllRacesUseCase.execute({});
|
||||
return this.getAllRacesPresenter;
|
||||
}
|
||||
|
||||
async getTotalRaces(): Promise<GetTotalRacesPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching total races count.');
|
||||
const result = await this.getTotalRacesUseCase.execute({});
|
||||
const presenter = new GetTotalRacesPresenter();
|
||||
presenter.present(result);
|
||||
return presenter;
|
||||
await this.getTotalRacesUseCase.execute({});
|
||||
return this.getTotalRacesPresenter;
|
||||
}
|
||||
|
||||
async importRaceResults(input: ImportRaceResultsDTO): Promise<ImportRaceResultsApiPresenter> {
|
||||
this.logger.debug('Importing race results:', input);
|
||||
const result = await this.importRaceResultsApiUseCase.execute({ raceId: input.raceId, resultsFileContent: input.resultsFileContent });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
const presenter = new ImportRaceResultsApiPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter;
|
||||
await this.importRaceResultsApiUseCase.execute({ raceId: input.raceId, resultsFileContent: input.resultsFileContent });
|
||||
return this.importRaceResultsApiPresenter;
|
||||
}
|
||||
|
||||
async getRaceDetail(params: GetRaceDetailParamsDTO): Promise<RaceDetailPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching race detail:', params);
|
||||
|
||||
const presenter = new RaceDetailPresenter(this.driverRatingProvider, this.imageService, params);
|
||||
this.getRaceDetailUseCase.setOutput(presenter);
|
||||
|
||||
const result = await this.getRaceDetailUseCase.execute(params);
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get race detail');
|
||||
}
|
||||
|
||||
return presenter;
|
||||
await this.getRaceDetailUseCase.execute(params);
|
||||
return this.raceDetailPresenter;
|
||||
}
|
||||
|
||||
async getRacesPageData(): Promise<RacesPageDataPresenter> {
|
||||
async getRacesPageData(leagueId: string): Promise<RacesPageDataPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching races page data.');
|
||||
|
||||
const result = await this.getRacesPageDataUseCase.execute();
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get races page data');
|
||||
}
|
||||
|
||||
const presenter = new RacesPageDataPresenter(this.leagueRepository);
|
||||
await presenter.present(result.value as RacesPageOutputPort);
|
||||
return presenter;
|
||||
await this.getRacesPageDataUseCase.execute({ leagueId });
|
||||
return this.racesPageDataPresenter;
|
||||
}
|
||||
|
||||
async getAllRacesPageData(): Promise<AllRacesPageDataPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching all races page data.');
|
||||
|
||||
const result = await this.getAllRacesPageDataUseCase.execute();
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get all races page data');
|
||||
}
|
||||
|
||||
const presenter = new AllRacesPageDataPresenter();
|
||||
presenter.present(result.value);
|
||||
return presenter;
|
||||
await this.getAllRacesPageDataUseCase.execute({});
|
||||
return this.allRacesPageDataPresenter;
|
||||
}
|
||||
|
||||
async getRaceResultsDetail(raceId: string): Promise<RaceResultsDetailPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching race results detail:', { raceId });
|
||||
|
||||
const result = await this.getRaceResultsDetailUseCase.execute({ raceId });
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get race results detail');
|
||||
}
|
||||
|
||||
const presenter = new RaceResultsDetailPresenter(this.imageService);
|
||||
await presenter.present(result.value as RaceResultsDetailOutputPort);
|
||||
return presenter;
|
||||
await this.getRaceResultsDetailUseCase.execute({ raceId });
|
||||
return this.raceResultsDetailPresenter;
|
||||
}
|
||||
|
||||
async getRaceWithSOF(raceId: string): Promise<RaceWithSOFPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching race with SOF:', { raceId });
|
||||
|
||||
const result = await this.getRaceWithSOFUseCase.execute({ raceId });
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get race with SOF');
|
||||
}
|
||||
|
||||
const presenter = new RaceWithSOFPresenter();
|
||||
presenter.present(result.value as RaceWithSOFOutputPort);
|
||||
return presenter;
|
||||
await this.getRaceWithSOFUseCase.execute({ raceId });
|
||||
return this.raceWithSOFPresenter;
|
||||
}
|
||||
|
||||
async getRaceProtests(raceId: string): Promise<RaceProtestsPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching race protests:', { raceId });
|
||||
|
||||
const result = await this.getRaceProtestsUseCase.execute({ raceId });
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get race protests');
|
||||
}
|
||||
|
||||
const presenter = new RaceProtestsPresenter();
|
||||
presenter.present(result.value as RaceProtestsOutputPort);
|
||||
return presenter;
|
||||
await this.getRaceProtestsUseCase.execute({ raceId });
|
||||
return this.raceProtestsPresenter;
|
||||
}
|
||||
|
||||
async getRacePenalties(raceId: string): Promise<RacePenaltiesPresenter> {
|
||||
this.logger.debug('[RaceService] Fetching race penalties:', { raceId });
|
||||
|
||||
const result = await this.getRacePenaltiesUseCase.execute({ raceId });
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error('Failed to get race penalties');
|
||||
}
|
||||
|
||||
const presenter = new RacePenaltiesPresenter();
|
||||
presenter.present(result.value as RacePenaltiesOutputPort);
|
||||
return presenter;
|
||||
await this.getRacePenaltiesUseCase.execute({ raceId });
|
||||
return this.racePenaltiesPresenter;
|
||||
}
|
||||
|
||||
async registerForRace(params: RegisterForRaceParamsDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Registering for race:', params);
|
||||
|
||||
const result = await this.registerForRaceUseCase.execute(params);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REGISTER_FOR_RACE', 'Failed to register for race');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.registerForRaceUseCase.execute(params);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async withdrawFromRace(params: WithdrawFromRaceParamsDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Withdrawing from race:', params);
|
||||
|
||||
const result = await this.withdrawFromRaceUseCase.execute(params);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_WITHDRAW_FROM_RACE', 'Failed to withdraw from race');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.withdrawFromRaceUseCase.execute(params);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async cancelRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
||||
async cancelRace(params: RaceActionParamsDTO, cancelledById: string): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Cancelling race:', params);
|
||||
|
||||
const result = await this.cancelRaceUseCase.execute({ raceId: params.raceId });
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_CANCEL_RACE', 'Failed to cancel race');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.cancelRaceUseCase.execute({ raceId: params.raceId, cancelledById });
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async completeRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Completing race:', params);
|
||||
|
||||
const result = await this.completeRaceUseCase.execute({ raceId: params.raceId });
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_COMPLETE_RACE', 'Failed to complete race');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.completeRaceUseCase.execute({ raceId: params.raceId });
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async reopenRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
||||
async reopenRace(params: RaceActionParamsDTO, reopenedById: string): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Re-opening race:', params);
|
||||
|
||||
const result = await this.reopenRaceUseCase.execute({ raceId: params.raceId });
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const errorCode = result.unwrapErr().code;
|
||||
|
||||
if (errorCode === 'RACE_ALREADY_SCHEDULED') {
|
||||
this.logger.debug('[RaceService] Race is already scheduled, treating reopen as success.');
|
||||
presenter.presentSuccess('Race already scheduled');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentFailure(errorCode ?? 'UNEXPECTED_ERROR', 'Unexpected error while reopening race');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.reopenRaceUseCase.execute({ raceId: params.raceId, reopenedById });
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async fileProtest(command: FileProtestCommandDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Filing protest:', command);
|
||||
|
||||
const result = await this.fileProtestUseCase.execute(command);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_FILE_PROTEST', 'Failed to file protest');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.fileProtestUseCase.execute(command);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async applyQuickPenalty(command: QuickPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Applying quick penalty:', command);
|
||||
|
||||
const result = await this.quickPenaltyUseCase.execute(command);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_APPLY_QUICK_PENALTY', 'Failed to apply quick penalty');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.quickPenaltyUseCase.execute(command);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async applyPenalty(command: ApplyPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Applying penalty:', command);
|
||||
|
||||
const result = await this.applyPenaltyUseCase.execute(command);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_APPLY_PENALTY', 'Failed to apply penalty');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.applyPenaltyUseCase.execute(command);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async requestProtestDefense(command: RequestProtestDefenseCommandDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Requesting protest defense:', command);
|
||||
|
||||
const result = await this.requestProtestDefenseUseCase.execute(command);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REQUEST_PROTEST_DEFENSE', 'Failed to request protest defense');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.requestProtestDefenseUseCase.execute(command);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
|
||||
async reviewProtest(command: ReviewProtestCommandDTO): Promise<CommandResultPresenter> {
|
||||
this.logger.debug('[RaceService] Reviewing protest:', command);
|
||||
|
||||
const result = await this.reviewProtestUseCase.execute(command);
|
||||
|
||||
const presenter = new CommandResultPresenter();
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REVIEW_PROTEST', 'Failed to review protest');
|
||||
return presenter;
|
||||
}
|
||||
|
||||
presenter.presentSuccess();
|
||||
return presenter;
|
||||
await this.reviewProtestUseCase.execute(command);
|
||||
return this.commandResultPresenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,28 @@ export class AllRacesListItemDTO {
|
||||
strengthOfField!: number | null;
|
||||
}
|
||||
|
||||
export class AllRacesFilterOptionsDTO {
|
||||
@ApiProperty({ type: [{ value: String, label: String }] })
|
||||
statuses!: { value: AllRacesStatus; label: string }[];
|
||||
export class AllRacesStatusFilterDTO {
|
||||
@ApiProperty()
|
||||
value!: AllRacesStatus;
|
||||
|
||||
@ApiProperty({ type: [{ id: String, name: String }] })
|
||||
leagues!: { id: string; name: string }[];
|
||||
@ApiProperty()
|
||||
label!: string;
|
||||
}
|
||||
|
||||
export class AllRacesLeagueFilterDTO {
|
||||
@ApiProperty()
|
||||
id!: string;
|
||||
|
||||
@ApiProperty()
|
||||
name!: string;
|
||||
}
|
||||
|
||||
export class AllRacesFilterOptionsDTO {
|
||||
@ApiProperty({ type: [AllRacesStatusFilterDTO] })
|
||||
statuses!: AllRacesStatusFilterDTO[];
|
||||
|
||||
@ApiProperty({ type: [AllRacesLeagueFilterDTO] })
|
||||
leagues!: AllRacesLeagueFilterDTO[];
|
||||
}
|
||||
|
||||
export class AllRacesPageDTO {
|
||||
|
||||
@@ -7,19 +7,19 @@ export interface CommandResultDTO {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export type CommandApplicationError<E extends string = string> = ApplicationErrorCode<
|
||||
E,
|
||||
export type CommandApplicationError = ApplicationErrorCode<
|
||||
string,
|
||||
{ message: string }
|
||||
>;
|
||||
|
||||
export class CommandResultPresenter<E extends string = string> {
|
||||
export class CommandResultPresenter {
|
||||
private model: CommandResultDTO | null = null;
|
||||
|
||||
reset(): void {
|
||||
this.model = null;
|
||||
}
|
||||
|
||||
present(result: Result<unknown, CommandApplicationError<E>>): void {
|
||||
present(result: Result<unknown, CommandApplicationError>): void {
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
this.model = {
|
||||
@@ -36,7 +36,7 @@ export class CommandResultPresenter<E extends string = string> {
|
||||
presentSuccess(message?: string): void {
|
||||
this.model = {
|
||||
success: true,
|
||||
message,
|
||||
...(message !== undefined && { message }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export class CommandResultPresenter<E extends string = string> {
|
||||
this.model = {
|
||||
success: false,
|
||||
errorCode,
|
||||
message,
|
||||
...(message !== undefined && { message }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,4 +59,8 @@ export class CommandResultPresenter<E extends string = string> {
|
||||
|
||||
return this.model;
|
||||
}
|
||||
|
||||
get viewModel(): CommandResultDTO {
|
||||
return this.responseModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ export class RaceDetailPresenter implements UseCaseOutputPort<GetRaceDetailResul
|
||||
track: output.race.track,
|
||||
car: output.race.car,
|
||||
scheduledAt: output.race.scheduledAt.toISOString(),
|
||||
sessionType: output.race.sessionType,
|
||||
status: output.race.status,
|
||||
sessionType: output.race.sessionType.toString(),
|
||||
status: output.race.status.toString(),
|
||||
strengthOfField: output.race.strengthOfField ?? null,
|
||||
registeredCount: output.race.registeredCount ?? undefined,
|
||||
maxParticipants: output.race.maxParticipants ?? undefined,
|
||||
...(output.race.registeredCount !== undefined && { registeredCount: output.race.registeredCount }),
|
||||
...(output.race.maxParticipants !== undefined && { maxParticipants: output.race.maxParticipants }),
|
||||
}
|
||||
: null;
|
||||
|
||||
@@ -54,22 +54,22 @@ export class RaceDetailPresenter implements UseCaseOutputPort<GetRaceDetailResul
|
||||
name: output.league.name.toString(),
|
||||
description: output.league.description.toString(),
|
||||
settings: {
|
||||
maxDrivers: output.league.settings.maxDrivers ?? undefined,
|
||||
qualifyingFormat: output.league.settings.qualifyingFormat ?? undefined,
|
||||
...(output.league.settings.maxDrivers !== undefined && { maxDrivers: output.league.settings.maxDrivers }),
|
||||
...(output.league.settings.qualifyingFormat !== undefined && { qualifyingFormat: output.league.settings.qualifyingFormat.toString() }),
|
||||
},
|
||||
}
|
||||
: null;
|
||||
|
||||
const entryListDTO: RaceDetailEntryDTO[] = await Promise.all(
|
||||
output.drivers.map(async driver => {
|
||||
const ratingResult = await this.driverRatingProvider.getDriverRating({ driverId: driver.id });
|
||||
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
|
||||
const rating = this.driverRatingProvider.getRating(driver.id);
|
||||
const avatarUrl = this.imageService.getDriverAvatar(driver.id);
|
||||
return {
|
||||
id: driver.id,
|
||||
name: driver.name.toString(),
|
||||
country: driver.country.toString(),
|
||||
avatarUrl: avatarResult.avatarUrl,
|
||||
rating: ratingResult.rating,
|
||||
avatarUrl,
|
||||
rating,
|
||||
isCurrentUser: driver.id === params.driverId,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -34,10 +34,10 @@ export class RaceProtestsPresenter {
|
||||
protestingDriverId: protest.protestingDriverId,
|
||||
accusedDriverId: protest.accusedDriverId,
|
||||
incident: {
|
||||
lap: protest.incident.lap,
|
||||
description: protest.incident.description,
|
||||
lap: protest.incident.lap.toNumber(),
|
||||
description: protest.incident.description.toString(),
|
||||
},
|
||||
status: protest.status,
|
||||
status: protest.status.toString(),
|
||||
filedAt: protest.filedAt.toISOString(),
|
||||
} as RaceProtestDTO));
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@ export class RaceResultsDetailPresenter {
|
||||
throw new Error(`Driver not found for result: ${singleResult.driverId}`);
|
||||
}
|
||||
|
||||
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
|
||||
const avatarUrl = this.imageService.getDriverAvatar(driver.id);
|
||||
|
||||
return {
|
||||
driverId: singleResult.driverId.toString(),
|
||||
driverName: driver.name.toString(),
|
||||
avatarUrl: avatarResult.avatarUrl,
|
||||
avatarUrl,
|
||||
position: singleResult.position.toNumber(),
|
||||
startPosition: singleResult.startPosition.toNumber(),
|
||||
incidents: singleResult.incidents.toNumber(),
|
||||
|
||||
Reference in New Issue
Block a user