This commit is contained in:
2025-12-12 23:49:56 +01:00
parent cae81b1088
commit 8f1db21fb1
29 changed files with 879 additions and 399 deletions

View File

@@ -1,6 +1,13 @@
import { describe, it, expect } from 'vitest';
import { GetDashboardOverviewUseCase } from '@gridpilot/racing/application/use-cases/GetDashboardOverviewUseCase';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import { Race } from '@gridpilot/racing/domain/entities/Race';
import { Result } from '@gridpilot/racing/domain/entities/Result';
import { League } from '@gridpilot/racing/domain/entities/League';
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
import { LeagueMembership } from '@gridpilot/racing/domain/entities/LeagueMembership';
import type { FeedItem } from '@gridpilot/social/domain/types/FeedItem';
import type {
IDashboardOverviewPresenter,
DashboardOverviewViewModel,
@@ -25,11 +32,17 @@ class FakeDashboardOverviewPresenter implements IDashboardOverviewPresenter {
interface TestImageService {
getDriverAvatar(driverId: string): string;
getTeamLogo(teamId: string): string;
getLeagueCover(leagueId: string): string;
getLeagueLogo(leagueId: string): string;
}
function createTestImageService(): TestImageService {
return {
getDriverAvatar: (driverId: string) => `avatar-${driverId}`,
getTeamLogo: (teamId: string) => `team-logo-${teamId}`,
getLeagueCover: (leagueId: string) => `league-cover-${leagueId}`,
getLeagueLogo: (leagueId: string) => `league-logo-${leagueId}`,
};
}
@@ -38,143 +51,173 @@ describe('GetDashboardOverviewUseCase', () => {
// Given a driver with memberships in two leagues and future races with mixed registration
const driverId = 'driver-1';
const driver = { id: driverId, name: 'Alice Racer', country: 'US' };
const driver = Driver.create({ id: driverId, iracingId: '12345', name: 'Alice Racer', country: 'US' });
const leagues = [
{ id: 'league-1', name: 'Alpha League' },
{ id: 'league-2', name: 'Beta League' },
League.create({ id: 'league-1', name: 'Alpha League', description: 'First league', ownerId: 'owner-1' }),
League.create({ id: 'league-2', name: 'Beta League', description: 'Second league', ownerId: 'owner-2' }),
];
const now = Date.now();
const races = [
{
Race.create({
id: 'race-1',
leagueId: 'league-1',
track: 'Monza',
car: 'GT3',
scheduledAt: new Date(now + 60 * 60 * 1000),
status: 'scheduled' as const,
},
{
status: 'scheduled',
}),
Race.create({
id: 'race-2',
leagueId: 'league-1',
track: 'Spa',
car: 'GT3',
scheduledAt: new Date(now + 2 * 60 * 60 * 1000),
status: 'scheduled' as const,
},
{
status: 'scheduled',
}),
Race.create({
id: 'race-3',
leagueId: 'league-2',
track: 'Silverstone',
car: 'GT4',
scheduledAt: new Date(now + 3 * 60 * 60 * 1000),
status: 'scheduled' as const,
},
{
status: 'scheduled',
}),
Race.create({
id: 'race-4',
leagueId: 'league-2',
track: 'Imola',
car: 'GT4',
scheduledAt: new Date(now + 4 * 60 * 60 * 1000),
status: 'scheduled' as const,
},
status: 'scheduled',
}),
];
const results: unknown[] = [];
const results: Result[] = [];
const memberships = [
{
LeagueMembership.create({
leagueId: 'league-1',
driverId,
role: 'member',
status: 'active',
},
{
}),
LeagueMembership.create({
leagueId: 'league-2',
driverId,
role: 'member',
status: 'active',
},
}),
];
const registeredRaceIds = new Set<string>(['race-1', 'race-3']);
const feedItems: DashboardFeedItemSummaryViewModel[] = [];
const friends: Array<{ id: string }> = [];
const feedItems: FeedItem[] = [];
const friends: Driver[] = [];
const driverRepository: {
findById: (id: string) => Promise<{ id: string; name: string; country: string } | null>;
} = {
findById: async (id: string) => (id === driver.id ? driver : null),
const driverRepository = {
findById: async (id: string): Promise<Driver | null> => (id === driver.id ? driver : null),
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => [],
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const raceRepository: {
findAll: () => Promise<
Array<{
id: string;
leagueId: string;
track: string;
car: string;
scheduledAt: Date;
status: 'scheduled';
}>
>;
} = {
findAll: async () => races,
const raceRepository = {
findById: async (): Promise<Race | null> => null,
findAll: async (): Promise<Race[]> => races,
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const resultRepository: {
findAll: () => Promise<unknown[]>;
} = {
findAll: async () => results,
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => results,
findByRaceId: async (): Promise<Result[]> => [],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const leagueRepository: {
findAll: () => Promise<Array<{ id: string; name: string }>>;
} = {
findAll: async () => leagues,
const leagueRepository = {
findById: async (): Promise<League | null> => null,
findAll: async (): Promise<League[]> => leagues,
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const standingRepository: {
findByLeagueId: (leagueId: string) => Promise<unknown[]>;
} = {
findByLeagueId: async () => [],
const standingRepository = {
findByLeagueId: async (): Promise<Standing[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Standing | null> => null,
findAll: async (): Promise<Standing[]> => [],
save: async (): Promise<Standing> => { throw new Error('Not implemented'); },
saveMany: async (): Promise<Standing[]> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByLeagueId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
recalculate: async (): Promise<Standing[]> => [],
};
const leagueMembershipRepository: {
getMembership: (
leagueId: string,
driverIdParam: string,
) => Promise<{ leagueId: string; driverId: string; status: string } | null>;
} = {
getMembership: async (leagueId: string, driverIdParam: string) => {
const leagueMembershipRepository = {
getMembership: async (leagueId: string, driverIdParam: string): Promise<LeagueMembership | null> => {
return (
memberships.find(
(m) => m.leagueId === leagueId && m.driverId === driverIdParam,
) ?? null
);
},
getLeagueMembers: async (): Promise<LeagueMembership[]> => [],
getJoinRequests: async (): Promise<any[]> => [],
saveMembership: async (): Promise<LeagueMembership> => { throw new Error('Not implemented'); },
removeMembership: async (): Promise<void> => { throw new Error('Not implemented'); },
saveJoinRequest: async (): Promise<any> => { throw new Error('Not implemented'); },
removeJoinRequest: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const raceRegistrationRepository: {
isRegistered: (raceId: string, driverIdParam: string) => Promise<boolean>;
} = {
isRegistered: async (raceId: string, driverIdParam: string) => {
const raceRegistrationRepository = {
isRegistered: async (raceId: string, driverIdParam: string): Promise<boolean> => {
if (driverIdParam !== driverId) return false;
return registeredRaceIds.has(raceId);
},
getRegisteredDrivers: async (): Promise<string[]> => [],
getRegistrationCount: async (): Promise<number> => 0,
register: async (): Promise<void> => { throw new Error('Not implemented'); },
withdraw: async (): Promise<void> => { throw new Error('Not implemented'); },
getDriverRegistrations: async (): Promise<string[]> => [],
clearRaceRegistrations: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const feedRepository: {
getFeedForDriver: (driverIdParam: string) => Promise<DashboardFeedItemSummaryViewModel[]>;
} = {
getFeedForDriver: async () => feedItems,
const feedRepository = {
getFeedForDriver: async (): Promise<FeedItem[]> => feedItems,
getGlobalFeed: async (): Promise<FeedItem[]> => [],
};
const socialRepository: {
getFriends: (driverIdParam: string) => Promise<Array<{ id: string }>>;
} = {
getFriends: async () => friends,
const socialRepository = {
getFriends: async (): Promise<Driver[]> => friends,
getFriendIds: async (): Promise<string[]> => [],
getSuggestedFriends: async (): Promise<Driver[]> => [],
};
const imageService = createTestImageService();
@@ -230,138 +273,181 @@ describe('GetDashboardOverviewUseCase', () => {
// Given completed races with results and standings
const driverId = 'driver-2';
const driver = { id: driverId, name: 'Result Driver', country: 'DE' };
const driver = Driver.create({ id: driverId, iracingId: '67890', name: 'Result Driver', country: 'DE' });
const leagues = [
{ id: 'league-A', name: 'Results League A' },
{ id: 'league-B', name: 'Results League B' },
League.create({ id: 'league-A', name: 'Results League A', description: 'League A', ownerId: 'owner-A' }),
League.create({ id: 'league-B', name: 'Results League B', description: 'League B', ownerId: 'owner-B' }),
];
const raceOld = {
const raceOld = Race.create({
id: 'race-old',
leagueId: 'league-A',
track: 'Old Circuit',
car: 'GT3',
scheduledAt: new Date('2024-01-01T10:00:00Z'),
status: 'completed' as const,
};
status: 'completed',
});
const raceNew = {
const raceNew = Race.create({
id: 'race-new',
leagueId: 'league-B',
track: 'New Circuit',
car: 'GT4',
scheduledAt: new Date('2024-02-01T10:00:00Z'),
status: 'completed' as const,
};
status: 'completed',
});
const races = [raceOld, raceNew];
const results = [
{
Result.create({
id: 'result-old',
raceId: raceOld.id,
driverId,
position: 5,
fastestLap: 120,
incidents: 3,
},
{
startPosition: 5,
}),
Result.create({
id: 'result-new',
raceId: raceNew.id,
driverId,
position: 2,
fastestLap: 115,
incidents: 1,
},
startPosition: 2,
}),
];
const memberships = [
{
LeagueMembership.create({
leagueId: 'league-A',
driverId,
role: 'member',
status: 'active',
},
{
}),
LeagueMembership.create({
leagueId: 'league-B',
driverId,
role: 'member',
status: 'active',
},
}),
];
const standingsByLeague = new Map<
string,
Array<{ leagueId: string; driverId: string; position: number; points: number }>
Standing[]
>();
standingsByLeague.set('league-A', [
{ leagueId: 'league-A', driverId, position: 3, points: 50 },
{ leagueId: 'league-A', driverId: 'other-1', position: 1, points: 80 },
Standing.create({ leagueId: 'league-A', driverId, position: 3, points: 50 }),
Standing.create({ leagueId: 'league-A', driverId: 'other-1', position: 1, points: 80 }),
]);
standingsByLeague.set('league-B', [
{ leagueId: 'league-B', driverId, position: 1, points: 100 },
{ leagueId: 'league-B', driverId: 'other-2', position: 2, points: 90 },
Standing.create({ leagueId: 'league-B', driverId, position: 1, points: 100 }),
Standing.create({ leagueId: 'league-B', driverId: 'other-2', position: 2, points: 90 }),
]);
const driverRepository: {
findById: (id: string) => Promise<{ id: string; name: string; country: string } | null>;
} = {
findById: async (id: string) => (id === driver.id ? driver : null),
const driverRepository = {
findById: async (id: string): Promise<Driver | null> => (id === driver.id ? driver : null),
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => [],
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const raceRepository: {
findAll: () => Promise<typeof races>;
} = {
findAll: async () => races,
const raceRepository = {
findById: async (): Promise<Race | null> => null,
findAll: async (): Promise<Race[]> => races,
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const resultRepository: {
findAll: () => Promise<typeof results>;
} = {
findAll: async () => results,
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => results,
findByRaceId: async (): Promise<Result[]> => [],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const leagueRepository: {
findAll: () => Promise<typeof leagues>;
} = {
findAll: async () => leagues,
const leagueRepository = {
findById: async (): Promise<League | null> => null,
findAll: async (): Promise<League[]> => leagues,
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const standingRepository: {
findByLeagueId: (leagueId: string) => Promise<Array<{ leagueId: string; driverId: string; position: number; points: number }>>;
} = {
findByLeagueId: async (leagueId: string) =>
const standingRepository = {
findByLeagueId: async (leagueId: string): Promise<Standing[]> =>
standingsByLeague.get(leagueId) ?? [],
findByDriverIdAndLeagueId: async (): Promise<Standing | null> => null,
findAll: async (): Promise<Standing[]> => [],
save: async (): Promise<Standing> => { throw new Error('Not implemented'); },
saveMany: async (): Promise<Standing[]> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByLeagueId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
recalculate: async (): Promise<Standing[]> => [],
};
const leagueMembershipRepository: {
getMembership: (
leagueId: string,
driverIdParam: string,
) => Promise<{ leagueId: string; driverId: string; status: string } | null>;
} = {
getMembership: async (leagueId: string, driverIdParam: string) => {
const leagueMembershipRepository = {
getMembership: async (leagueId: string, driverIdParam: string): Promise<LeagueMembership | null> => {
return (
memberships.find(
(m) => m.leagueId === leagueId && m.driverId === driverIdParam,
) ?? null
);
},
getLeagueMembers: async (): Promise<LeagueMembership[]> => [],
getJoinRequests: async (): Promise<any[]> => [],
saveMembership: async (): Promise<LeagueMembership> => { throw new Error('Not implemented'); },
removeMembership: async (): Promise<void> => { throw new Error('Not implemented'); },
saveJoinRequest: async (): Promise<any> => { throw new Error('Not implemented'); },
removeJoinRequest: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const raceRegistrationRepository: {
isRegistered: (raceId: string, driverIdParam: string) => Promise<boolean>;
} = {
isRegistered: async () => false,
const raceRegistrationRepository = {
isRegistered: async (): Promise<boolean> => false,
getRegisteredDrivers: async (): Promise<string[]> => [],
getRegistrationCount: async (): Promise<number> => 0,
register: async (): Promise<void> => { throw new Error('Not implemented'); },
withdraw: async (): Promise<void> => { throw new Error('Not implemented'); },
getDriverRegistrations: async (): Promise<string[]> => [],
clearRaceRegistrations: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const feedRepository: {
getFeedForDriver: (driverIdParam: string) => Promise<DashboardFeedItemSummaryViewModel[]>;
} = {
getFeedForDriver: async () => [],
const feedRepository = {
getFeedForDriver: async (): Promise<FeedItem[]> => [],
getGlobalFeed: async (): Promise<FeedItem[]> => [],
};
const socialRepository: {
getFriends: (driverIdParam: string) => Promise<Array<{ id: string }>>;
} = {
getFriends: async () => [],
const socialRepository = {
getFriends: async (): Promise<Driver[]> => [],
getFriendIds: async (): Promise<string[]> => [],
getSuggestedFriends: async (): Promise<Driver[]> => [],
};
const imageService = createTestImageService();
@@ -430,54 +516,100 @@ describe('GetDashboardOverviewUseCase', () => {
// Given a driver with no related data
const driverId = 'driver-empty';
const driver = { id: driverId, name: 'New Racer', country: 'FR' };
const driver = Driver.create({ id: driverId, iracingId: '11111', name: 'New Racer', country: 'FR' });
const driverRepository: {
findById: (id: string) => Promise<{ id: string; name: string; country: string } | null>;
} = {
findById: async (id: string) => (id === driver.id ? driver : null),
const driverRepository = {
findById: async (id: string): Promise<Driver | null> => (id === driver.id ? driver : null),
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => [],
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const raceRepository: { findAll: () => Promise<never[]> } = {
findAll: async () => [],
const raceRepository = {
findById: async (): Promise<Race | null> => null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const resultRepository: { findAll: () => Promise<never[]> } = {
findAll: async () => [],
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (): Promise<Result[]> => [],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const leagueRepository: { findAll: () => Promise<never[]> } = {
findAll: async () => [],
const leagueRepository = {
findById: async (): Promise<League | null> => null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const standingRepository: {
findByLeagueId: (leagueId: string) => Promise<never[]>;
} = {
findByLeagueId: async () => [],
const standingRepository = {
findByLeagueId: async (): Promise<Standing[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Standing | null> => null,
findAll: async (): Promise<Standing[]> => [],
save: async (): Promise<Standing> => { throw new Error('Not implemented'); },
saveMany: async (): Promise<Standing[]> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByLeagueId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
recalculate: async (): Promise<Standing[]> => [],
};
const leagueMembershipRepository: {
getMembership: (leagueId: string, driverIdParam: string) => Promise<null>;
} = {
getMembership: async () => null,
const leagueMembershipRepository = {
getMembership: async (): Promise<LeagueMembership | null> => null,
getLeagueMembers: async (): Promise<LeagueMembership[]> => [],
getJoinRequests: async (): Promise<any[]> => [],
saveMembership: async (): Promise<LeagueMembership> => { throw new Error('Not implemented'); },
removeMembership: async (): Promise<void> => { throw new Error('Not implemented'); },
saveJoinRequest: async (): Promise<any> => { throw new Error('Not implemented'); },
removeJoinRequest: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const raceRegistrationRepository: {
isRegistered: (raceId: string, driverIdParam: string) => Promise<boolean>;
} = {
isRegistered: async () => false,
const raceRegistrationRepository = {
isRegistered: async (): Promise<boolean> => false,
getRegisteredDrivers: async (): Promise<string[]> => [],
getRegistrationCount: async (): Promise<number> => 0,
register: async (): Promise<void> => { throw new Error('Not implemented'); },
withdraw: async (): Promise<void> => { throw new Error('Not implemented'); },
getDriverRegistrations: async (): Promise<string[]> => [],
clearRaceRegistrations: async (): Promise<void> => { throw new Error('Not implemented'); },
};
const feedRepository: {
getFeedForDriver: (driverIdParam: string) => Promise<DashboardFeedItemSummaryViewModel[]>;
} = {
getFeedForDriver: async () => [],
const feedRepository = {
getFeedForDriver: async (): Promise<FeedItem[]> => [],
getGlobalFeed: async (): Promise<FeedItem[]> => [],
};
const socialRepository: {
getFriends: (driverIdParam: string) => Promise<Array<{ id: string }>>;
} = {
getFriends: async () => [],
const socialRepository = {
getFriends: async (): Promise<Driver[]> => [],
getFriendIds: async (): Promise<string[]> => [],
getSuggestedFriends: async (): Promise<Driver[]> => [],
};
const imageService = createTestImageService();

View File

@@ -2,10 +2,10 @@ import { describe, it, expect, beforeEach } from 'vitest';
import { JoinLeagueUseCase } from '@gridpilot/racing/application/use-cases/JoinLeagueUseCase';
import type { ILeagueMembershipRepository } from '@gridpilot/racing/domain/repositories/ILeagueMembershipRepository';
import type {
import {
LeagueMembership,
MembershipRole,
MembershipStatus,
type MembershipRole,
type MembershipStatus,
} from '@gridpilot/racing/domain/entities/LeagueMembership';
class InMemoryLeagueMembershipRepository implements ILeagueMembershipRepository {
@@ -109,13 +109,13 @@ describe('Membership use-cases', () => {
const leagueId = 'league-1';
const driverId = 'driver-1';
repository.seedMembership({
repository.seedMembership(LeagueMembership.create({
leagueId,
driverId,
role: 'member',
status: 'active',
joinedAt: new Date('2024-01-01'),
});
}));
await expect(
useCase.execute({ leagueId, driverId }),

View File

@@ -4,6 +4,8 @@ import { Race } from '@gridpilot/racing/domain/entities/Race';
import { League } from '@gridpilot/racing/domain/entities/League';
import { Result } from '@gridpilot/racing/domain/entities/Result';
import { Penalty } from '@gridpilot/racing/domain/entities/Penalty';
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import { GetRaceResultsDetailUseCase } from '@gridpilot/racing/application/use-cases/GetRaceResultsDetailUseCase';
import { ImportRaceResultsUseCase } from '@gridpilot/racing/application/use-cases/ImportRaceResultsUseCase';
@@ -77,37 +79,64 @@ describe('ImportRaceResultsUseCase', () => {
let existsByRaceIdCalled = false;
const recalcCalls: string[] = [];
const raceRepository: {
findById: (id: string) => Promise<Race | null>;
} = {
findById: async (id: string) => races.get(id) ?? null,
const raceRepository = {
findById: async (id: string): Promise<Race | null> => races.get(id) ?? null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const leagueRepository: {
findById: (id: string) => Promise<League | null>;
} = {
findById: async (id: string) => leagues.get(id) ?? null,
const leagueRepository = {
findById: async (id: string): Promise<League | null> => leagues.get(id) ?? null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const resultRepository: {
existsByRaceId: (raceId: string) => Promise<boolean>;
createMany: (results: Result[]) => Promise<Result[]>;
} = {
existsByRaceId: async (raceId: string) => {
existsByRaceIdCalled = true;
return storedResults.some((r) => r.raceId === raceId);
},
createMany: async (results: Result[]) => {
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (): Promise<Result[]> => [],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (results: Result[]): Promise<Result[]> => {
storedResults.push(...results);
return results;
},
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (raceId: string): Promise<boolean> => {
existsByRaceIdCalled = true;
return storedResults.some((r) => r.raceId === raceId);
},
};
const standingRepository: {
recalculate: (leagueId: string) => Promise<void>;
} = {
recalculate: async (leagueId: string) => {
const standingRepository = {
findByLeagueId: async (): Promise<Standing[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Standing | null> => null,
findAll: async (): Promise<Standing[]> => [],
save: async (): Promise<Standing> => { throw new Error('Not implemented'); },
saveMany: async (): Promise<Standing[]> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByLeagueId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
recalculate: async (leagueId: string): Promise<Standing[]> => {
recalcCalls.push(leagueId);
return [];
},
};
@@ -196,34 +225,60 @@ describe('ImportRaceResultsUseCase', () => {
}),
];
const raceRepository: {
findById: (id: string) => Promise<Race | null>;
} = {
findById: async (id: string) => races.get(id) ?? null,
const raceRepository = {
findById: async (id: string): Promise<Race | null> => races.get(id) ?? null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const leagueRepository: {
findById: (id: string) => Promise<League | null>;
} = {
findById: async (id: string) => leagues.get(id) ?? null,
const leagueRepository = {
findById: async (id: string): Promise<League | null> => leagues.get(id) ?? null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const resultRepository: {
existsByRaceId: (raceId: string) => Promise<boolean>;
createMany: (results: Result[]) => Promise<Result[]>;
} = {
existsByRaceId: async (raceId: string) => {
return storedResults.some((r) => r.raceId === raceId);
},
createMany: async (_results: Result[]) => {
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (): Promise<Result[]> => [],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (_results: Result[]): Promise<Result[]> => {
throw new Error('Should not be called when results already exist');
},
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (raceId: string): Promise<boolean> => {
return storedResults.some((r) => r.raceId === raceId);
},
};
const standingRepository: {
recalculate: (leagueId: string) => Promise<void>;
} = {
recalculate: async (_leagueId: string) => {
const standingRepository = {
findByLeagueId: async (): Promise<Standing[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Standing | null> => null,
findAll: async (): Promise<Standing[]> => [],
save: async (): Promise<Standing> => { throw new Error('Not implemented'); },
saveMany: async (): Promise<Standing[]> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByLeagueId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
recalculate: async (_leagueId: string): Promise<Standing[]> => {
throw new Error('Should not be called when results already exist');
},
};
@@ -315,35 +370,68 @@ describe('GetRaceResultsDetailUseCase', () => {
const results = [result1, result2];
const drivers = [driver1, driver2];
const raceRepository: {
findById: (id: string) => Promise<Race | null>;
} = {
findById: async (id: string) => races.get(id) ?? null,
const raceRepository = {
findById: async (id: string): Promise<Race | null> => races.get(id) ?? null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const leagueRepository: {
findById: (id: string) => Promise<League | null>;
} = {
findById: async (id: string) => leagues.get(id) ?? null,
const leagueRepository = {
findById: async (id: string): Promise<League | null> => leagues.get(id) ?? null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const resultRepository: {
findByRaceId: (raceId: string) => Promise<Result[]>;
} = {
findByRaceId: async (raceId: string) =>
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (raceId: string): Promise<Result[]> =>
results.filter((r) => r.raceId === raceId),
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const driverRepository: {
findAll: () => Promise<Array<{ id: string; name: string; country: string }>>;
} = {
findAll: async () => drivers,
const driverRepository = {
findById: async (): Promise<Driver | null> => null,
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => drivers.map(d => Driver.create({ id: d.id, iracingId: '123', name: d.name, country: d.country })),
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const penaltyRepository: {
findByRaceId: (raceId: string) => Promise<Penalty[]>;
} = {
findByRaceId: async () => [] as Penalty[],
const penaltyRepository = {
findById: async (): Promise<Penalty | null> => null,
findByRaceId: async (): Promise<Penalty[]> => [] as Penalty[],
findByDriverId: async (): Promise<Penalty[]> => [],
findByProtestId: async (): Promise<Penalty[]> => [],
findPending: async (): Promise<Penalty[]> => [],
findIssuedBy: async (): Promise<Penalty[]> => [],
create: async (): Promise<void> => { throw new Error('Not implemented'); },
update: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const presenter = new FakeRaceResultsDetailPresenter();
@@ -354,7 +442,6 @@ describe('GetRaceResultsDetailUseCase', () => {
resultRepository,
driverRepository,
penaltyRepository,
presenter,
);
// When executing the query
@@ -364,8 +451,8 @@ describe('GetRaceResultsDetailUseCase', () => {
expect(viewModel).not.toBeNull();
// Then points system matches the default F1-style configuration
expect(viewModel!.pointsSystem[1]).toBe(25);
expect(viewModel!.pointsSystem[2]).toBe(18);
expect(viewModel!.pointsSystem?.[1]).toBe(25);
expect(viewModel!.pointsSystem?.[2]).toBe(18);
// And fastest lap is identified correctly
expect(viewModel!.fastestLapTime).toBeCloseTo(88.456, 3);
@@ -408,6 +495,7 @@ describe('GetRaceResultsDetailUseCase', () => {
const penalty = Penalty.create({
id: 'pen-1',
leagueId: league.id,
raceId: race.id,
driverId: driver.id,
type: 'points_deduction',
@@ -424,36 +512,69 @@ describe('GetRaceResultsDetailUseCase', () => {
const drivers = [driver];
const penalties = [penalty];
const raceRepository: {
findById: (id: string) => Promise<Race | null>;
} = {
findById: async (id: string) => races.get(id) ?? null,
const raceRepository = {
findById: async (id: string): Promise<Race | null> => races.get(id) ?? null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const leagueRepository: {
findById: (id: string) => Promise<League | null>;
} = {
findById: async (id: string) => leagues.get(id) ?? null,
const leagueRepository = {
findById: async (id: string): Promise<League | null> => leagues.get(id) ?? null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const resultRepository: {
findByRaceId: (raceId: string) => Promise<Result[]>;
} = {
findByRaceId: async (raceId: string) =>
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (raceId: string): Promise<Result[]> =>
results.filter((r) => r.raceId === raceId),
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const driverRepository: {
findAll: () => Promise<Array<{ id: string; name: string; country: string }>>;
} = {
findAll: async () => drivers,
const driverRepository = {
findById: async (): Promise<Driver | null> => null,
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => drivers.map(d => Driver.create({ id: d.id, iracingId: '123', name: d.name, country: d.country })),
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const penaltyRepository: {
findByRaceId: (raceId: string) => Promise<Penalty[]>;
} = {
findByRaceId: async (raceId: string) =>
const penaltyRepository = {
findById: async (): Promise<Penalty | null> => null,
findByRaceId: async (raceId: string): Promise<Penalty[]> =>
penalties.filter((p) => p.raceId === raceId),
findByDriverId: async (): Promise<Penalty[]> => [],
findByProtestId: async (): Promise<Penalty[]> => [],
findPending: async (): Promise<Penalty[]> => [],
findIssuedBy: async (): Promise<Penalty[]> => [],
create: async (): Promise<void> => { throw new Error('Not implemented'); },
update: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const presenter = new FakeRaceResultsDetailPresenter();
@@ -464,7 +585,6 @@ describe('GetRaceResultsDetailUseCase', () => {
resultRepository,
driverRepository,
penaltyRepository,
presenter,
);
// When
@@ -491,34 +611,67 @@ describe('GetRaceResultsDetailUseCase', () => {
it('presents an error when race does not exist', async () => {
// Given repositories without the requested race
const raceRepository: {
findById: (id: string) => Promise<Race | null>;
} = {
findById: async () => null,
const raceRepository = {
findById: async (): Promise<Race | null> => null,
findAll: async (): Promise<Race[]> => [],
findByLeagueId: async (): Promise<Race[]> => [],
findUpcomingByLeagueId: async (): Promise<Race[]> => [],
findCompletedByLeagueId: async (): Promise<Race[]> => [],
findByStatus: async (): Promise<Race[]> => [],
findByDateRange: async (): Promise<Race[]> => [],
create: async (): Promise<Race> => { throw new Error('Not implemented'); },
update: async (): Promise<Race> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const leagueRepository: {
findById: (id: string) => Promise<League | null>;
} = {
findById: async () => null,
const leagueRepository = {
findById: async (): Promise<League | null> => null,
findAll: async (): Promise<League[]> => [],
findByOwnerId: async (): Promise<League[]> => [],
create: async (): Promise<League> => { throw new Error('Not implemented'); },
update: async (): Promise<League> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
searchByName: async (): Promise<League[]> => [],
};
const resultRepository: {
findByRaceId: (raceId: string) => Promise<Result[]>;
} = {
findByRaceId: async () => [] as Result[],
const resultRepository = {
findById: async (): Promise<Result | null> => null,
findAll: async (): Promise<Result[]> => [],
findByRaceId: async (): Promise<Result[]> => [] as Result[],
findByDriverId: async (): Promise<Result[]> => [],
findByDriverIdAndLeagueId: async (): Promise<Result[]> => [],
create: async (): Promise<Result> => { throw new Error('Not implemented'); },
createMany: async (): Promise<Result[]> => { throw new Error('Not implemented'); },
update: async (): Promise<Result> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
deleteByRaceId: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByRaceId: async (): Promise<boolean> => false,
};
const driverRepository: {
findAll: () => Promise<Array<{ id: string; name: string; country: string }>>;
} = {
findAll: async () => [],
const driverRepository = {
findById: async (): Promise<Driver | null> => null,
findByIRacingId: async (): Promise<Driver | null> => null,
findAll: async (): Promise<Driver[]> => [],
create: async (): Promise<Driver> => { throw new Error('Not implemented'); },
update: async (): Promise<Driver> => { throw new Error('Not implemented'); },
delete: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
existsByIRacingId: async (): Promise<boolean> => false,
};
const penaltyRepository: {
findByRaceId: (raceId: string) => Promise<Penalty[]>;
} = {
findByRaceId: async () => [] as Penalty[],
const penaltyRepository = {
findById: async (): Promise<Penalty | null> => null,
findByRaceId: async (): Promise<Penalty[]> => [] as Penalty[],
findByDriverId: async (): Promise<Penalty[]> => [],
findByProtestId: async (): Promise<Penalty[]> => [],
findPending: async (): Promise<Penalty[]> => [],
findIssuedBy: async (): Promise<Penalty[]> => [],
create: async (): Promise<void> => { throw new Error('Not implemented'); },
update: async (): Promise<void> => { throw new Error('Not implemented'); },
exists: async (): Promise<boolean> => false,
};
const presenter = new FakeRaceResultsDetailPresenter();
@@ -529,7 +682,6 @@ describe('GetRaceResultsDetailUseCase', () => {
resultRepository,
driverRepository,
penaltyRepository,
presenter,
);
// When

View File

@@ -9,13 +9,14 @@ import {
LeagueMembership,
type MembershipStatus,
} from '@gridpilot/racing/domain/entities/LeagueMembership';
import { Team } from '@gridpilot/racing/domain/entities/Team';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import type {
Team,
TeamMembership,
TeamMembershipStatus,
TeamRole,
TeamJoinRequest,
} from '@gridpilot/racing/domain/entities/Team';
} from '@gridpilot/racing/domain/types/TeamMembership';
import { RegisterForRaceUseCase } from '@gridpilot/racing/application/use-cases/RegisterForRaceUseCase';
import { WithdrawFromRaceUseCase } from '@gridpilot/racing/application/use-cases/WithdrawFromRaceUseCase';
@@ -169,10 +170,23 @@ class TestDriverRegistrationStatusPresenter implements IDriverRegistrationStatus
raceId: string | null = null;
driverId: string | null = null;
present(isRegistered: boolean, raceId: string, driverId: string): void {
present(isRegistered: boolean, raceId: string, driverId: string) {
this.isRegistered = isRegistered;
this.raceId = raceId;
this.driverId = driverId;
return {
isRegistered,
raceId,
driverId,
};
}
getViewModel() {
return {
isRegistered: this.isRegistered!,
raceId: this.raceId!,
driverId: this.driverId!,
};
}
}
@@ -185,9 +199,20 @@ class TestRaceRegistrationsPresenter implements IRaceRegistrationsPresenter {
this.driverIds = [];
}
present(input: RaceRegistrationsResultDTO): void {
present(input: RaceRegistrationsResultDTO) {
this.driverIds = input.registeredDriverIds;
this.raceId = null;
return {
registeredDriverIds: input.registeredDriverIds,
count: input.registeredDriverIds.length,
};
}
getViewModel() {
return {
registeredDriverIds: this.driverIds,
count: this.driverIds.length,
};
}
}
@@ -411,8 +436,43 @@ describe('Racing application use-cases - teams', () => {
let getDriverTeamUseCase: GetDriverTeamUseCase;
class FakeDriverRepository {
async findById(driverId: string): Promise<{ id: string; name: string } | null> {
return { id: driverId, name: `Driver ${driverId}` };
async findById(driverId: string): Promise<Driver | null> {
return Driver.create({ id: driverId, iracingId: '123', name: `Driver ${driverId}`, country: 'US' });
}
async findByIRacingId(id: string): Promise<Driver | null> {
return null;
}
async findAll(): Promise<Driver[]> {
return [];
}
async create(driver: Driver): Promise<Driver> {
return driver;
}
async update(driver: Driver): Promise<Driver> {
return driver;
}
async delete(id: string): Promise<void> {
}
async exists(id: string): Promise<boolean> {
return false;
}
async existsByIRacingId(iracingId: string): Promise<boolean> {
return false;
}
async findByLeagueId(leagueId: string): Promise<Driver[]> {
return [];
}
async findByTeamId(teamId: string): Promise<Driver[]> {
return [];
}
}
@@ -420,6 +480,18 @@ describe('Racing application use-cases - teams', () => {
getDriverAvatar(driverId: string): string {
return `https://example.com/avatar/${driverId}.png`;
}
getTeamLogo(teamId: string): string {
return `https://example.com/logo/${teamId}.png`;
}
getLeagueCover(leagueId: string): string {
return `https://example.com/cover/${leagueId}.png`;
}
getLeagueLogo(leagueId: string): string {
return `https://example.com/logo/${leagueId}.png`;
}
}
class TestAllTeamsPresenter implements IAllTeamsPresenter {
@@ -438,9 +510,9 @@ describe('Racing application use-cases - teams', () => {
description: team.description,
memberCount: team.memberCount,
leagues: team.leagues,
specialization: team.specialization,
region: team.region,
languages: team.languages,
specialization: (team as any).specialization,
region: (team as any).region,
languages: (team as any).languages,
})),
totalCount: input.teams.length,
};
@@ -487,7 +559,7 @@ describe('Racing application use-cases - teams', () => {
return {
driverId,
driverName,
role: membership.role,
role: ((membership.role as any) === 'owner' ? 'owner' : (membership.role as any) === 'member' ? 'member' : (membership.role as any) === 'manager' ? 'manager' : (membership.role as any) === 'driver' ? 'member' : 'member') as "owner" | "member" | "manager",
joinedAt: membership.joinedAt.toISOString(),
isActive: membership.status === 'active',
avatarUrl,
@@ -496,7 +568,7 @@ describe('Racing application use-cases - teams', () => {
const ownerCount = members.filter((m) => m.role === 'owner').length;
const managerCount = members.filter((m) => m.role === 'manager').length;
const memberCount = members.filter((m) => m.role === 'member').length;
const memberCount = members.filter((m) => (m.role as any) === 'member').length;
this.viewModel = {
members,
@@ -534,7 +606,7 @@ describe('Racing application use-cases - teams', () => {
driverId,
driverName,
teamId: request.teamId,
status: 'pending',
status: 'pending' as const,
requestedAt: request.requestedAt.toISOString(),
avatarUrl,
};
@@ -559,7 +631,7 @@ describe('Racing application use-cases - teams', () => {
}
class TestDriverTeamPresenter implements IDriverTeamPresenter {
private viewModel: DriverTeamViewModel | null = null;
viewModel: DriverTeamViewModel | null = null;
reset(): void {
this.viewModel = null;
@@ -579,12 +651,9 @@ describe('Racing application use-cases - teams', () => {
description: team.description,
ownerId: team.ownerId,
leagues: team.leagues,
specialization: team.specialization,
region: team.region,
languages: team.languages,
},
membership: {
role: membership.role,
role: (membership.role === 'owner' || membership.role === 'manager') ? membership.role : 'member' as "owner" | "member" | "manager",
joinedAt: membership.joinedAt.toISOString(),
isActive: membership.status === 'active',
},
@@ -619,19 +688,17 @@ describe('Racing application use-cases - teams', () => {
getAllTeamsUseCase = new GetAllTeamsUseCase(
teamRepo,
membershipRepo,
allTeamsPresenter,
);
teamDetailsPresenter = new TestTeamDetailsPresenter();
getTeamDetailsUseCase = new GetTeamDetailsUseCase(
teamRepo,
membershipRepo,
teamDetailsPresenter,
);
const driverRepository = new FakeDriverRepository();
const imageService = new FakeImageService();
teamMembersPresenter = new TestTeamMembersPresenter();
getTeamMembersUseCase = new GetTeamMembersUseCase(
membershipRepo,