refactor use cases
This commit is contained in:
@@ -3,7 +3,6 @@ import { DashboardModule } from './DashboardModule';
|
||||
import { DashboardController } from './DashboardController';
|
||||
import { DashboardService } from './DashboardService';
|
||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||
import { DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN } from './DashboardProviders';
|
||||
|
||||
describe('DashboardModule', () => {
|
||||
let module: TestingModule;
|
||||
@@ -30,8 +29,8 @@ describe('DashboardModule', () => {
|
||||
expect(service).toBeInstanceOf(DashboardService);
|
||||
});
|
||||
|
||||
it('should bind DashboardOverviewPresenter as the output port for the use case', () => {
|
||||
const presenter = module.get<DashboardOverviewPresenter>(DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN);
|
||||
it('should provide DashboardOverviewPresenter', () => {
|
||||
const presenter = module.get<DashboardOverviewPresenter>(DashboardOverviewPresenter);
|
||||
expect(presenter).toBeDefined();
|
||||
expect(presenter).toBeInstanceOf(DashboardOverviewPresenter);
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||
import {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
@@ -36,7 +35,6 @@ import {
|
||||
|
||||
// Re-export tokens for convenience (legacy imports)
|
||||
export {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
@@ -60,10 +58,6 @@ export const DashboardProviders: Provider[] = [
|
||||
useFactory: (logger: Logger) => new InMemoryImageServiceAdapter(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DashboardOverviewPresenter,
|
||||
},
|
||||
{
|
||||
provide: DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
useFactory: (
|
||||
@@ -77,7 +71,6 @@ export const DashboardProviders: Provider[] = [
|
||||
feedRepo: IFeedRepository,
|
||||
socialRepo: ISocialGraphRepository,
|
||||
imageService: ImageServicePort,
|
||||
output: DashboardOverviewPresenter,
|
||||
) =>
|
||||
new DashboardOverviewUseCase(
|
||||
driverRepo,
|
||||
@@ -91,7 +84,6 @@ export const DashboardProviders: Provider[] = [
|
||||
socialRepo,
|
||||
async (driverId: string) => imageService.getDriverAvatar(driverId),
|
||||
() => null,
|
||||
output,
|
||||
),
|
||||
inject: [
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
@@ -104,7 +96,6 @@ export const DashboardProviders: Provider[] = [
|
||||
SOCIAL_FEED_REPOSITORY_TOKEN,
|
||||
SOCIAL_GRAPH_REPOSITORY_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -4,8 +4,9 @@ import { DashboardService } from './DashboardService';
|
||||
|
||||
describe('DashboardService', () => {
|
||||
it('getDashboardOverview returns presenter model on success', async () => {
|
||||
const presenter = { getResponseModel: vi.fn(() => ({ feed: [] })) };
|
||||
const useCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
const mockResult = { currentDriver: null, myUpcomingRaces: [], otherUpcomingRaces: [], upcomingRaces: [], activeLeaguesCount: 0, nextRace: null, recentResults: [], leagueStandingsSummaries: [], feedSummary: { notificationCount: 0, items: [] }, friends: [] };
|
||||
const presenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ feed: [] })) };
|
||||
const useCase = { execute: vi.fn(async () => Result.ok(mockResult)) };
|
||||
|
||||
const service = new DashboardService(
|
||||
{ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
|
||||
@@ -15,13 +16,14 @@ describe('DashboardService', () => {
|
||||
|
||||
await expect(service.getDashboardOverview('d1')).resolves.toEqual({ feed: [] });
|
||||
expect(useCase.execute).toHaveBeenCalledWith({ driverId: 'd1' });
|
||||
expect(presenter.present).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
|
||||
it('getDashboardOverview throws with details message on error', async () => {
|
||||
const service = new DashboardService(
|
||||
{ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as any,
|
||||
{ getResponseModel: vi.fn() } as any,
|
||||
{ present: vi.fn(), getResponseModel: vi.fn() } as any,
|
||||
);
|
||||
|
||||
await expect(service.getDashboardOverview('d1')).rejects.toThrow('Failed to get dashboard overview: boom');
|
||||
@@ -31,7 +33,7 @@ describe('DashboardService', () => {
|
||||
const service = new DashboardService(
|
||||
{ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
{ getResponseModel: vi.fn() } as any,
|
||||
{ present: vi.fn(), getResponseModel: vi.fn() } as any,
|
||||
);
|
||||
|
||||
await expect(service.getDashboardOverview('d1')).rejects.toThrow('Failed to get dashboard overview: Unknown error');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { DashboardOverviewDTO } from './dtos/DashboardOverviewDTO';
|
||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { Logger } from '@core/shared/application/Logger';
|
||||
|
||||
// Tokens (standalone to avoid circular imports)
|
||||
import {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
} from './DashboardTokens';
|
||||
@@ -18,7 +17,7 @@ export class DashboardService {
|
||||
constructor(
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
@Inject(DASHBOARD_OVERVIEW_USE_CASE_TOKEN) private readonly dashboardOverviewUseCase: DashboardOverviewUseCase,
|
||||
@Inject(DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN) private readonly presenter: DashboardOverviewPresenter,
|
||||
private readonly presenter: DashboardOverviewPresenter,
|
||||
) {}
|
||||
|
||||
async getDashboardOverview(driverId: string): Promise<DashboardOverviewDTO> {
|
||||
@@ -29,9 +28,11 @@ export class DashboardService {
|
||||
if (result.isErr()) {
|
||||
const error = result.error;
|
||||
const message = error?.details?.message || 'Unknown error';
|
||||
throw new Error(`Failed to get dashboard overview: ${message}`);
|
||||
throw new NotFoundException(`Failed to get dashboard overview: ${message}`);
|
||||
}
|
||||
|
||||
// Present the result
|
||||
this.presenter.present(result.unwrap());
|
||||
return this.presenter.getResponseModel();
|
||||
}
|
||||
}
|
||||
@@ -12,5 +12,4 @@ export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
||||
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
|
||||
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
||||
export const DASHBOARD_OVERVIEW_USE_CASE_TOKEN = 'DashboardOverviewUseCase';
|
||||
export const DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN = 'DashboardOverviewOutputPort';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
||||
import type {
|
||||
DashboardOverviewResult,
|
||||
} from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
||||
@@ -13,7 +12,7 @@ import {
|
||||
DashboardFriendSummaryDTO,
|
||||
} from '../dtos/DashboardOverviewDTO';
|
||||
|
||||
export class DashboardOverviewPresenter implements UseCaseOutputPort<DashboardOverviewResult> {
|
||||
export class DashboardOverviewPresenter {
|
||||
private responseModel: DashboardOverviewDTO | null = null;
|
||||
|
||||
present(data: DashboardOverviewResult): void {
|
||||
|
||||
Reference in New Issue
Block a user