Files
gridpilot.gg/core/racing/application/use-cases/GetTeamsLeaderboardUseCase.test.ts
2025-12-23 20:09:02 +01:00

150 lines
5.0 KiB
TypeScript

import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import {
GetTeamsLeaderboardUseCase,
type GetTeamsLeaderboardResult,
type GetTeamsLeaderboardInput,
type GetTeamsLeaderboardErrorCode,
} from './GetTeamsLeaderboardUseCase';
import { ITeamRepository } from '../../domain/repositories/ITeamRepository';
import { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
import { Team } from '../../domain/entities/Team';
import type { Logger } from '@core/shared/application';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('GetTeamsLeaderboardUseCase', () => {
let useCase: GetTeamsLeaderboardUseCase;
let teamRepository: {
findAll: Mock;
};
let teamMembershipRepository: {
getTeamMembers: Mock;
};
let getDriverStats: Mock;
let logger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
let output: UseCaseOutputPort<GetTeamsLeaderboardResult> & { present: Mock };
beforeEach(() => {
teamRepository = {
findAll: vi.fn(),
};
teamMembershipRepository = {
getTeamMembers: vi.fn(),
};
getDriverStats = vi.fn();
logger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
output = {
present: vi.fn(),
} as unknown as UseCaseOutputPort<GetTeamsLeaderboardResult> & { present: Mock };
useCase = new GetTeamsLeaderboardUseCase(
teamRepository as unknown as ITeamRepository,
teamMembershipRepository as unknown as ITeamMembershipRepository,
getDriverStats as unknown as (driverId: string) => { rating: number | null; wins: number; totalRaces: number } | null,
logger as unknown as Logger,
output,
);
});
it('should return teams leaderboard with calculated stats', async () => {
const team1 = Team.create({
id: 'team-1',
name: 'Team Alpha',
tag: 'TA',
description: 'Description 1',
ownerId: 'owner-1',
leagues: [],
});
const team2 = Team.create({
id: 'team-2',
name: 'Team Beta',
tag: 'TB',
description: 'Description 2',
ownerId: 'owner-2',
leagues: [],
});
const memberships1 = [
{ teamId: 'team-1', driverId: 'driver-1', role: 'driver' as const, status: 'active' as const, joinedAt: new Date() },
{ teamId: 'team-1', driverId: 'driver-2', role: 'driver' as const, status: 'active' as const, joinedAt: new Date() },
];
const memberships2 = [
{ teamId: 'team-2', driverId: 'driver-3', role: 'driver' as const, status: 'active' as const, joinedAt: new Date() },
];
teamRepository.findAll.mockResolvedValue([team1, team2]);
teamMembershipRepository.getTeamMembers.mockImplementation((teamId: string) => {
if (teamId === 'team-1') return Promise.resolve(memberships1);
if (teamId === 'team-2') return Promise.resolve(memberships2);
return Promise.resolve([]);
});
getDriverStats.mockImplementation((driverId: string) => {
if (driverId === 'driver-1') return { rating: 1500, wins: 5, totalRaces: 10 };
if (driverId === 'driver-2') return { rating: 1600, wins: 3, totalRaces: 8 };
if (driverId === 'driver-3') return { rating: null, wins: 2, totalRaces: 5 };
return null;
});
const input: GetTeamsLeaderboardInput = { leagueId: 'league-1' };
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(output.present).toHaveBeenCalledTimes(1);
const presentedRaw = (output.present as unknown as Mock).mock.calls[0]?.[0];
expect(presentedRaw).toBeDefined();
const presented = presentedRaw as GetTeamsLeaderboardResult;
expect(presented.recruitingCount).toBe(2); // both teams are recruiting
expect(presented.items).toHaveLength(2);
expect(presented.items[0]).toMatchObject({
team: team1,
memberCount: 2,
rating: 1550, // (1500 + 1600) / 2
totalWins: 8,
totalRaces: 18,
performanceLevel: expect.any(String),
isRecruiting: true,
});
expect(presented.items[1]).toMatchObject({
team: team2,
memberCount: 1,
rating: null,
totalWins: 2,
totalRaces: 5,
performanceLevel: expect.any(String),
isRecruiting: true,
});
});
it('should return error on repository failure', async () => {
const error = new Error('Repository error');
teamRepository.findAll.mockRejectedValue(error);
const input: GetTeamsLeaderboardInput = { leagueId: 'league-1' };
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
GetTeamsLeaderboardErrorCode,
{ message: string }
>;
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('Repository error');
expect(output.present).not.toHaveBeenCalled();
});
});