refactor racing use cases

This commit is contained in:
2025-12-21 00:43:42 +01:00
parent e9d6f90bb2
commit c12656d671
308 changed files with 14401 additions and 7419 deletions

View File

@@ -1,9 +1,16 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { GetDriversLeaderboardUseCase } from './GetDriversLeaderboardUseCase';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetDriversLeaderboardUseCase,
type GetDriversLeaderboardResult,
type GetDriversLeaderboardInput,
type GetDriversLeaderboardErrorCode,
} from './GetDriversLeaderboardUseCase';
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import type { IRankingService } from '../../domain/services/IRankingService';
import type { IDriverStatsService } from '../../domain/services/IDriverStatsService';
import type { Logger } from '@core/shared/application';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('GetDriversLeaderboardUseCase', () => {
const mockDriverFindAll = vi.fn();
@@ -34,8 +41,13 @@ describe('GetDriversLeaderboardUseCase', () => {
error: vi.fn(),
};
let output: UseCaseOutputPort<GetDriversLeaderboardResult> & { present: Mock };
beforeEach(() => {
vi.clearAllMocks();
output = {
present: vi.fn(),
} as unknown as UseCaseOutputPort<GetDriversLeaderboardResult> & { present: Mock };
});
it('should return drivers leaderboard data', async () => {
@@ -45,6 +57,7 @@ describe('GetDriversLeaderboardUseCase', () => {
mockDriverStatsService,
mockGetDriverAvatar,
mockLogger,
output,
);
const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } };
@@ -63,49 +76,52 @@ describe('GetDriversLeaderboardUseCase', () => {
if (id === 'driver2') return stats2;
return null;
});
mockGetDriverAvatar.mockImplementation((input) => {
if (input.driverId === 'driver1') return Promise.resolve({ avatarUrl: 'avatar-driver1' });
if (input.driverId === 'driver2') return Promise.resolve({ avatarUrl: 'avatar-driver2' });
return Promise.resolve({ avatarUrl: 'avatar-default' });
mockGetDriverAvatar.mockImplementation((driverId: string) => {
if (driverId === 'driver1') return Promise.resolve('avatar-driver1');
if (driverId === 'driver2') return Promise.resolve('avatar-driver2');
return Promise.resolve('avatar-default');
});
const result = await useCase.execute();
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
expect(result.isOk()).toBe(true);
expect(result.value).toEqual({
drivers: [
{
id: 'driver1',
name: 'Driver One',
rating: 2500,
skillLevel: 'advanced',
nationality: 'US',
racesCompleted: 10,
wins: 5,
podiums: 7,
isActive: true,
rank: 1,
avatarUrl: 'avatar-driver1',
},
{
id: 'driver2',
name: 'Driver Two',
rating: 2400,
skillLevel: 'intermediate',
nationality: 'US',
racesCompleted: 8,
wins: 3,
podiums: 4,
isActive: true,
rank: 2,
avatarUrl: 'avatar-driver2',
},
],
totalRaces: 18,
totalWins: 8,
activeCount: 2,
});
});
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presented = (output.present as any).mock.calls[0][0] as GetDriversLeaderboardResult;
expect(presented).toEqual({
items: [
expect.objectContaining({
driver: driver1,
rating: 2500,
skillLevel: 'advanced',
racesCompleted: 10,
wins: 5,
podiums: 7,
isActive: true,
rank: 1,
avatarUrl: 'avatar-driver1',
}),
expect.objectContaining({
driver: driver2,
rating: 2400,
skillLevel: 'intermediate',
racesCompleted: 8,
wins: 3,
podiums: 4,
isActive: true,
rank: 2,
avatarUrl: 'avatar-driver2',
}),
],
totalRaces: 18,
totalWins: 8,
activeCount: 2,
});
});
it('should return empty result when no drivers', async () => {
const useCase = new GetDriversLeaderboardUseCase(
@@ -114,16 +130,24 @@ describe('GetDriversLeaderboardUseCase', () => {
mockDriverStatsService,
mockGetDriverAvatar,
mockLogger,
output,
);
mockDriverFindAll.mockResolvedValue([]);
mockRankingGetAllDriverRankings.mockReturnValue([]);
const result = await useCase.execute();
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.value).toEqual({
drivers: [],
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presented = (output.present as any).mock.calls[0][0] as GetDriversLeaderboardResult;
expect(presented).toEqual({
items: [],
totalRaces: 0,
totalWins: 0,
activeCount: 0,
@@ -137,6 +161,7 @@ describe('GetDriversLeaderboardUseCase', () => {
mockDriverStatsService,
mockGetDriverAvatar,
mockLogger,
output,
);
const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } };
@@ -145,32 +170,37 @@ describe('GetDriversLeaderboardUseCase', () => {
mockDriverFindAll.mockResolvedValue([driver1]);
mockRankingGetAllDriverRankings.mockReturnValue(rankings);
mockDriverStatsGetDriverStats.mockReturnValue(null);
mockGetDriverAvatar.mockResolvedValue({ avatarUrl: 'avatar-driver1' });
mockGetDriverAvatar.mockResolvedValue('avatar-driver1');
const result = await useCase.execute();
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
expect(result.isOk()).toBe(true);
expect(result.value).toEqual({
drivers: [
{
id: 'driver1',
name: 'Driver One',
rating: 2500,
skillLevel: 'advanced',
nationality: 'US',
racesCompleted: 0,
wins: 0,
podiums: 0,
isActive: false,
rank: 1,
avatarUrl: 'avatar-driver1',
},
],
totalRaces: 0,
totalWins: 0,
activeCount: 0,
});
});
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presented = (output.present as any).mock.calls[0][0] as GetDriversLeaderboardResult;
expect(presented).toEqual({
items: [
expect.objectContaining({
driver: driver1,
rating: 2500,
skillLevel: 'advanced',
racesCompleted: 0,
wins: 0,
podiums: 0,
isActive: false,
rank: 1,
avatarUrl: 'avatar-driver1',
}),
],
totalRaces: 0,
totalWins: 0,
activeCount: 0,
});
});
it('should return error when repository throws', async () => {
const useCase = new GetDriversLeaderboardUseCase(
@@ -179,15 +209,20 @@ describe('GetDriversLeaderboardUseCase', () => {
mockDriverStatsService,
mockGetDriverAvatar,
mockLogger,
output,
);
const error = new Error('Repository error');
mockDriverFindAll.mockRejectedValue(error);
const result = await useCase.execute();
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
expect(result.unwrapErr().details.message).toBe('Repository error');
const err = result.unwrapErr();
expect(err.code).toBe('REPOSITORY_ERROR');
expect((err as any).details?.message).toBe('Repository error');
expect(output.present).not.toHaveBeenCalled();
});
});
});