213 lines
6.4 KiB
TypeScript
213 lines
6.4 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import {
|
|
GetDriversLeaderboardUseCase,
|
|
type GetDriversLeaderboardInput,
|
|
} 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 { GetDriversLeaderboardResult } from './GetDriversLeaderboardUseCase';
|
|
|
|
describe('GetDriversLeaderboardUseCase', () => {
|
|
const mockDriverFindAll = vi.fn();
|
|
const mockDriverRepo: IDriverRepository = {
|
|
findById: vi.fn(),
|
|
findByIRacingId: vi.fn(),
|
|
existsByIRacingId: vi.fn(),
|
|
findAll: mockDriverFindAll,
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
delete: vi.fn(),
|
|
exists: vi.fn(),
|
|
};
|
|
|
|
const mockRankingGetAllDriverRankings = vi.fn();
|
|
const mockRankingService: IRankingService = {
|
|
getAllDriverRankings: mockRankingGetAllDriverRankings,
|
|
};
|
|
|
|
const mockDriverStatsGetDriverStats = vi.fn();
|
|
const mockDriverStatsService: IDriverStatsService = {
|
|
getDriverStats: mockDriverStatsGetDriverStats,
|
|
};
|
|
|
|
const mockGetDriverAvatar = vi.fn();
|
|
const mockLogger: Logger = {
|
|
debug: vi.fn(),
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
};
|
|
|
|
const mockOutput: UseCaseOutputPort<GetDriversLeaderboardResult> = {
|
|
present: vi.fn(),
|
|
};
|
|
|
|
it('should return drivers leaderboard data', async () => {
|
|
const useCase = new GetDriversLeaderboardUseCase(
|
|
mockDriverRepo,
|
|
mockRankingService,
|
|
mockDriverStatsService,
|
|
mockGetDriverAvatar,
|
|
mockLogger,
|
|
mockOutput,
|
|
);
|
|
|
|
const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } };
|
|
const driver2 = { id: 'driver2', name: { value: 'Driver Two' }, country: { value: 'US' } };
|
|
const rankings = [
|
|
{ driverId: 'driver1', rating: 2500, overallRank: 1 },
|
|
{ driverId: 'driver2', rating: 2400, overallRank: 2 },
|
|
];
|
|
const stats1 = { totalRaces: 10, wins: 5, podiums: 7 };
|
|
const stats2 = { totalRaces: 8, wins: 3, podiums: 4 };
|
|
|
|
mockDriverFindAll.mockResolvedValue([driver1, driver2]);
|
|
mockRankingGetAllDriverRankings.mockReturnValue(rankings);
|
|
mockDriverStatsGetDriverStats.mockImplementation((id: string) => {
|
|
if (id === 'driver1') return stats1;
|
|
if (id === 'driver2') return stats2;
|
|
return null;
|
|
});
|
|
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 input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
|
|
|
|
const result = await useCase.execute(input);
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
|
|
expect(mockOutput.present).toHaveBeenCalledWith({
|
|
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(
|
|
mockDriverRepo,
|
|
mockRankingService,
|
|
mockDriverStatsService,
|
|
mockGetDriverAvatar,
|
|
mockLogger,
|
|
mockOutput,
|
|
);
|
|
|
|
mockDriverFindAll.mockResolvedValue([]);
|
|
mockRankingGetAllDriverRankings.mockReturnValue([]);
|
|
|
|
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
|
|
|
|
const result = await useCase.execute(input);
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
|
|
expect(mockOutput.present).toHaveBeenCalledWith({
|
|
items: [],
|
|
totalRaces: 0,
|
|
totalWins: 0,
|
|
activeCount: 0,
|
|
});
|
|
});
|
|
|
|
it('should handle drivers without stats', async () => {
|
|
const useCase = new GetDriversLeaderboardUseCase(
|
|
mockDriverRepo,
|
|
mockRankingService,
|
|
mockDriverStatsService,
|
|
mockGetDriverAvatar,
|
|
mockLogger,
|
|
mockOutput,
|
|
);
|
|
|
|
const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } };
|
|
const rankings = [{ driverId: 'driver1', rating: 2500, overallRank: 1 }];
|
|
|
|
mockDriverFindAll.mockResolvedValue([driver1]);
|
|
mockRankingGetAllDriverRankings.mockReturnValue(rankings);
|
|
mockDriverStatsGetDriverStats.mockReturnValue(null);
|
|
mockGetDriverAvatar.mockResolvedValue('avatar-driver1');
|
|
|
|
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
|
|
|
|
const result = await useCase.execute(input);
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
|
|
expect(mockOutput.present).toHaveBeenCalledWith({
|
|
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(
|
|
mockDriverRepo,
|
|
mockRankingService,
|
|
mockDriverStatsService,
|
|
mockGetDriverAvatar,
|
|
mockLogger,
|
|
mockOutput,
|
|
);
|
|
|
|
const error = new Error('Repository error');
|
|
mockDriverFindAll.mockRejectedValue(error);
|
|
|
|
const input: GetDriversLeaderboardInput = { leagueId: 'league-1' };
|
|
|
|
const result = await useCase.execute(input);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('REPOSITORY_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('Repository error');
|
|
}
|
|
});
|
|
});
|