refactor use cases
This commit is contained in:
@@ -6,13 +6,12 @@ import {
|
||||
type GetCurrentUserSocialResult,
|
||||
} from './GetCurrentUserSocialUseCase';
|
||||
import type { ISocialGraphRepository } from '../../domain/repositories/ISocialGraphRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Driver } from '@core/racing/domain/entities/Driver';
|
||||
|
||||
describe('GetCurrentUserSocialUseCase', () => {
|
||||
let socialGraphRepository: ISocialGraphRepository & { getFriends: Mock };
|
||||
let logger: Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock };
|
||||
let output: UseCaseOutputPort<GetCurrentUserSocialResult> & { present: Mock };
|
||||
let useCase: GetCurrentUserSocialUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -29,14 +28,10 @@ describe('GetCurrentUserSocialUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock };
|
||||
|
||||
output = {
|
||||
present: vi.fn(),
|
||||
} as unknown as UseCaseOutputPort<GetCurrentUserSocialResult> & { present: Mock };
|
||||
|
||||
useCase = new GetCurrentUserSocialUseCase(socialGraphRepository, logger, output);
|
||||
useCase = new GetCurrentUserSocialUseCase(socialGraphRepository, logger);
|
||||
});
|
||||
|
||||
it('presents current user social with mapped friends', async () => {
|
||||
it('returns current user social with mapped friends', async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date('2025-01-01T00:00:00.000Z'));
|
||||
|
||||
@@ -55,20 +50,17 @@ describe('GetCurrentUserSocialUseCase', () => {
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
const socialResult = result.unwrap();
|
||||
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const presented = (output.present as Mock).mock.calls[0]![0] as GetCurrentUserSocialResult;
|
||||
|
||||
expect(presented.currentUser).toEqual({
|
||||
expect(socialResult.currentUser).toEqual({
|
||||
driverId: 'driver-1',
|
||||
displayName: '',
|
||||
avatarUrl: '',
|
||||
countryCode: '',
|
||||
});
|
||||
|
||||
expect(presented.friends).toHaveLength(1);
|
||||
expect(presented.friends[0]).toEqual({
|
||||
expect(socialResult.friends).toHaveLength(1);
|
||||
expect(socialResult.friends[0]).toEqual({
|
||||
driverId: 'friend-1',
|
||||
displayName: 'Friend One',
|
||||
avatarUrl: '',
|
||||
@@ -82,17 +74,17 @@ describe('GetCurrentUserSocialUseCase', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('warns and presents empty friends list when no friends exist', async () => {
|
||||
it('returns empty friends list when no friends exist', async () => {
|
||||
socialGraphRepository.getFriends.mockResolvedValue([]);
|
||||
|
||||
const input: GetCurrentUserSocialInput = { driverId: 'driver-1' };
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const socialResult = result.unwrap();
|
||||
|
||||
const presented = (output.present as Mock).mock.calls[0]![0] as GetCurrentUserSocialResult;
|
||||
expect(presented.friends).toEqual([]);
|
||||
expect(socialResult.friends).toEqual([]);
|
||||
expect(socialResult.currentUser.driverId).toBe('driver-1');
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledTimes(1);
|
||||
expect((logger.warn as Mock).mock.calls[0]![0]).toBe(
|
||||
@@ -112,7 +104,6 @@ describe('GetCurrentUserSocialUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { ISocialGraphRepository } from '../../domain/repositories/ISocialGraphRepository';
|
||||
@@ -26,18 +26,18 @@ export type GetCurrentUserSocialApplicationError = ApplicationErrorCode<
|
||||
* Application-level use case to retrieve the current user's social context.
|
||||
*
|
||||
* Keeps orchestration in the social bounded context while delegating
|
||||
* data access to domain repositories and presenting via an output port.
|
||||
* data access to domain repositories.
|
||||
* Returns Result directly without calling presenter.
|
||||
*/
|
||||
export class GetCurrentUserSocialUseCase {
|
||||
constructor(
|
||||
private readonly socialGraphRepository: ISocialGraphRepository,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<GetCurrentUserSocialResult>,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: GetCurrentUserSocialInput,
|
||||
): Promise<Result<void, GetCurrentUserSocialApplicationError>> {
|
||||
): Promise<Result<GetCurrentUserSocialResult, GetCurrentUserSocialApplicationError>> {
|
||||
this.logger.debug('GetCurrentUserSocialUseCase.execute: Starting execution', { input });
|
||||
|
||||
try {
|
||||
@@ -81,12 +81,11 @@ export class GetCurrentUserSocialUseCase {
|
||||
friends,
|
||||
};
|
||||
|
||||
this.output.present(result);
|
||||
this.logger.info(
|
||||
'GetCurrentUserSocialUseCase.execute: Successfully presented current user social data',
|
||||
'GetCurrentUserSocialUseCase.execute: Successfully retrieved current user social data',
|
||||
);
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok(result);
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
|
||||
@@ -7,12 +7,11 @@ import {
|
||||
} from './GetUserFeedUseCase';
|
||||
import type { IFeedRepository } from '../../domain/repositories/IFeedRepository';
|
||||
import type { FeedItem } from '../../domain/types/FeedItem';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
|
||||
describe('GetUserFeedUseCase', () => {
|
||||
let feedRepository: IFeedRepository & { getFeedForDriver: Mock };
|
||||
let logger: Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock };
|
||||
let output: UseCaseOutputPort<GetUserFeedResult> & { present: Mock };
|
||||
let useCase: GetUserFeedUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -28,14 +27,10 @@ describe('GetUserFeedUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock };
|
||||
|
||||
output = {
|
||||
present: vi.fn(),
|
||||
} as unknown as UseCaseOutputPort<GetUserFeedResult> & { present: Mock };
|
||||
|
||||
useCase = new GetUserFeedUseCase(feedRepository, logger, output);
|
||||
useCase = new GetUserFeedUseCase(feedRepository, logger);
|
||||
});
|
||||
|
||||
it('presents feed items when repository returns items', async () => {
|
||||
it('returns feed items when repository returns items', async () => {
|
||||
const items: FeedItem[] = [
|
||||
{
|
||||
id: 'item-1',
|
||||
@@ -65,29 +60,26 @@ describe('GetUserFeedUseCase', () => {
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
const feedResult = result.unwrap();
|
||||
|
||||
expect(feedRepository.getFeedForDriver).toHaveBeenCalledTimes(1);
|
||||
expect(feedRepository.getFeedForDriver).toHaveBeenCalledWith('driver-1', 10);
|
||||
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const presented = (output.present as Mock).mock.calls[0]![0] as GetUserFeedResult;
|
||||
expect(presented.items).toEqual(items);
|
||||
expect(feedResult.items).toEqual(items);
|
||||
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('warns and presents empty list when no items exist', async () => {
|
||||
it('returns empty list when no items exist', async () => {
|
||||
feedRepository.getFeedForDriver.mockResolvedValue([]);
|
||||
|
||||
const input: GetUserFeedInput = { driverId: 'driver-1' };
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(output.present).toHaveBeenCalledTimes(1);
|
||||
const feedResult = result.unwrap();
|
||||
|
||||
const presented = (output.present as Mock).mock.calls[0]![0] as GetUserFeedResult;
|
||||
expect(presented.items).toEqual([]);
|
||||
expect(feedResult.items).toEqual([]);
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledTimes(1);
|
||||
expect((logger.warn as Mock).mock.calls[0]![0]).toBe(
|
||||
@@ -107,7 +99,6 @@ describe('GetUserFeedUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Logger , UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { IFeedRepository } from '../../domain/repositories/IFeedRepository';
|
||||
@@ -26,12 +26,11 @@ export class GetUserFeedUseCase {
|
||||
constructor(
|
||||
private readonly feedRepository: IFeedRepository,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<GetUserFeedResult>,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: GetUserFeedInput,
|
||||
): Promise<Result<void, GetUserFeedApplicationError>> {
|
||||
): Promise<Result<GetUserFeedResult, GetUserFeedApplicationError>> {
|
||||
const { driverId, limit } = input;
|
||||
this.logger.debug('GetUserFeedUseCase.execute started', { driverId, limit });
|
||||
|
||||
@@ -51,9 +50,7 @@ export class GetUserFeedUseCase {
|
||||
items,
|
||||
};
|
||||
|
||||
this.output.present(result);
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok(result);
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user