From 15435c93fc5054435565aa09e38e6b7298c95b88 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sat, 27 Dec 2025 01:53:36 +0100 Subject: [PATCH] fix seeds --- .../bootstrap/racing/RacingDriverFactory.ts | 15 +- .../bootstrap/racing/RacingLeagueFactory.ts | 140 ++++++------------ .../bootstrap/racing/RacingRaceFactory.ts | 21 +-- adapters/bootstrap/racing/RacingSeed.ts | 16 +- .../bootstrap/racing/RacingTeamFactory.ts | 116 ++++++--------- .../bootstrap/racing/RacingTrackFactory.ts | 92 ++++++++++++ .../CompleteDriverOnboardingUseCase.test.ts | 5 +- .../GetDriversLeaderboardUseCase.test.ts | 19 ++- .../use-cases/GetTotalDriversUseCase.test.ts | 10 +- .../IsDriverRegisteredForRaceUseCase.test.ts | 11 +- package-lock.json | 11 +- package.json | 3 +- 12 files changed, 259 insertions(+), 200 deletions(-) create mode 100644 adapters/bootstrap/racing/RacingTrackFactory.ts diff --git a/adapters/bootstrap/racing/RacingDriverFactory.ts b/adapters/bootstrap/racing/RacingDriverFactory.ts index 5ea5ebaf5..db42df0f4 100644 --- a/adapters/bootstrap/racing/RacingDriverFactory.ts +++ b/adapters/bootstrap/racing/RacingDriverFactory.ts @@ -1,4 +1,5 @@ import { Driver } from '@core/racing/domain/entities/Driver'; +import { faker } from '@faker-js/faker'; export class RacingDriverFactory { constructor( @@ -7,7 +8,7 @@ export class RacingDriverFactory { ) {} create(): Driver[] { - const countries = ['DE', 'NL', 'FR', 'GB', 'US', 'CA', 'SE', 'NO', 'IT', 'ES'] as const; + const countries = ['DE', 'NL', 'FR', 'GB', 'US', 'CA', 'SE', 'NO', 'IT', 'ES', 'AU', 'BR', 'JP', 'KR', 'RU', 'PL', 'CZ', 'HU', 'AT', 'CH'] as const; return Array.from({ length: this.driverCount }, (_, idx) => { const i = idx + 1; @@ -15,15 +16,11 @@ export class RacingDriverFactory { return Driver.create({ id: `driver-${i}`, iracingId: String(100000 + i), - name: `Driver ${i}`, - country: countries[idx % countries.length]!, - bio: `Demo driver #${i} seeded for in-memory mode.`, - joinedAt: this.addDays(this.baseDate, -90 + i), + name: faker.person.fullName(), + country: faker.helpers.arrayElement(countries), + bio: faker.lorem.sentences(2), + joinedAt: faker.date.past({ years: 2, refDate: this.baseDate }), }); }); } - - private addDays(date: Date, days: number): Date { - return new Date(date.getTime() + days * 24 * 60 * 60 * 1000); - } } \ No newline at end of file diff --git a/adapters/bootstrap/racing/RacingLeagueFactory.ts b/adapters/bootstrap/racing/RacingLeagueFactory.ts index 4c8f5b403..0cbbfb2a2 100644 --- a/adapters/bootstrap/racing/RacingLeagueFactory.ts +++ b/adapters/bootstrap/racing/RacingLeagueFactory.ts @@ -1,100 +1,58 @@ import { League } from '@core/racing/domain/entities/League'; +import { Driver } from '@core/racing/domain/entities/Driver'; +import { faker } from '@faker-js/faker'; export class RacingLeagueFactory { - constructor(private readonly baseDate: Date) {} + constructor( + private readonly baseDate: Date, + private readonly drivers: Driver[], + ) {} create(): League[] { - const createdAtBase = this.baseDate; + const leagueCount = 20; + const pointsSystems = ['f1-2024', 'indycar', 'custom'] as const; + const qualifyingFormats = ['open', 'single-lap'] as const; - return [ - League.create({ - id: 'league-1', - name: 'GridPilot Sprint Series', - description: 'Weekly sprint races with stable grids.', - ownerId: 'driver-1', - settings: { - pointsSystem: 'f1-2024', - maxDrivers: 24, - sessionDuration: 60, - qualifyingFormat: 'open', - }, - createdAt: this.addDays(createdAtBase, -200), - socialLinks: { - discordUrl: 'https://discord.gg/gridpilot-demo', - youtubeUrl: 'https://youtube.com/@gridpilot-demo', - websiteUrl: 'https://gridpilot-demo.example.com', - }, - }), - League.create({ - id: 'league-2', - name: 'GridPilot Endurance Cup', - description: 'Longer races with strategy and consistency.', - ownerId: 'driver-2', - settings: { - pointsSystem: 'indycar', - maxDrivers: 32, - sessionDuration: 120, - qualifyingFormat: 'open', - }, - createdAt: this.addDays(createdAtBase, -180), - socialLinks: { discordUrl: 'https://discord.gg/gridpilot-endurance' }, - }), - League.create({ - id: 'league-3', - name: 'GridPilot Club Ladder', - description: 'Casual ladder with fast onboarding.', - ownerId: 'driver-3', - settings: { - pointsSystem: 'f1-2024', - maxDrivers: 48, - sessionDuration: 45, - qualifyingFormat: 'single-lap', - }, - createdAt: this.addDays(createdAtBase, -160), - }), - League.create({ - id: 'league-4', - name: 'Nordic Night Series', - description: 'Evening races with tight fields.', - ownerId: 'driver-4', - settings: { - pointsSystem: 'f1-2024', - maxDrivers: 32, - sessionDuration: 60, - qualifyingFormat: 'open', - }, - createdAt: this.addDays(createdAtBase, -150), - }), - League.create({ - id: 'league-5', - name: 'Demo League (Admin)', - description: 'Primary demo league owned by driver-1.', - ownerId: 'driver-1', - settings: { - pointsSystem: 'f1-2024', - maxDrivers: 24, - sessionDuration: 60, - qualifyingFormat: 'open', - }, - createdAt: this.addDays(createdAtBase, -140), - }), - League.create({ - id: 'league-6', - name: 'Sim Racing Alliance', - description: 'Mixed-format season with community events.', - ownerId: 'driver-5', - settings: { - pointsSystem: 'indycar', - maxDrivers: 40, - sessionDuration: 90, - qualifyingFormat: 'open', - }, - createdAt: this.addDays(createdAtBase, -130), - }), - ]; - } + return Array.from({ length: leagueCount }, (_, idx) => { + const i = idx + 1; + const owner = faker.helpers.arrayElement(this.drivers); + const socialLinks: { discordUrl?: string; youtubeUrl?: string; websiteUrl?: string } = {}; + if (faker.datatype.boolean()) socialLinks.discordUrl = faker.internet.url(); + if (faker.datatype.boolean()) socialLinks.youtubeUrl = faker.internet.url(); + if (faker.datatype.boolean()) socialLinks.websiteUrl = faker.internet.url(); - private addDays(date: Date, days: number): Date { - return new Date(date.getTime() + days * 24 * 60 * 60 * 1000); + const leagueData: { + id: string; + name: string; + description: string; + ownerId: string; + settings: { + pointsSystem: 'f1-2024' | 'indycar' | 'custom'; + maxDrivers: number; + sessionDuration: number; + qualifyingFormat: 'open' | 'single-lap'; + }; + createdAt: Date; + socialLinks?: { discordUrl?: string; youtubeUrl?: string; websiteUrl?: string }; + } = { + id: `league-${i}`, + name: faker.company.name() + ' Racing League', + description: faker.lorem.sentences(2), + ownerId: owner.id.toString(), + settings: { + pointsSystem: faker.helpers.arrayElement(pointsSystems), + maxDrivers: faker.number.int({ min: 20, max: 50 }), + sessionDuration: faker.number.int({ min: 30, max: 180 }), + qualifyingFormat: faker.helpers.arrayElement(qualifyingFormats), + }, + createdAt: faker.date.past({ years: 2, refDate: this.baseDate }), + }; + + if (Object.keys(socialLinks).length > 0) { + leagueData.socialLinks = socialLinks; + } + + return League.create(leagueData); + }); } } \ No newline at end of file diff --git a/adapters/bootstrap/racing/RacingRaceFactory.ts b/adapters/bootstrap/racing/RacingRaceFactory.ts index 43aa3a4d5..e3137dff5 100644 --- a/adapters/bootstrap/racing/RacingRaceFactory.ts +++ b/adapters/bootstrap/racing/RacingRaceFactory.ts @@ -1,36 +1,31 @@ import { League } from '@core/racing/domain/entities/League'; import { Race } from '@core/racing/domain/entities/Race'; +import { Track } from '@core/racing/domain/entities/Track'; export class RacingRaceFactory { constructor(private readonly baseDate: Date) {} - create(leagues: League[]): Race[] { - const tracks = [ - 'Monza GP', - 'Spa-Francorchamps', - 'Suzuka', - 'Mount Panorama', - 'Silverstone GP', - 'Interlagos', - 'Imola', - 'Laguna Seca', - ]; + create(leagues: League[], tracks: Track[]): Race[] { const cars = ['GT3 – Porsche 911', 'GT3 – BMW M4', 'LMP3 Prototype', 'GT4 – Alpine', 'Touring – Civic']; const leagueIds = leagues.map((l) => l.id.toString()); + const trackIds = tracks.map((t) => t.id); const demoLeagueId = 'league-5'; const races: Race[] = []; - for (let i = 1; i <= 25; i++) { + for (let i = 1; i <= 50; i++) { const leagueId = leagueIds[(i - 1) % leagueIds.length] ?? demoLeagueId; + const trackId = trackIds[(i - 1) % trackIds.length]!; + const track = tracks.find(t => t.id === trackId)!; const scheduledAt = this.addDays(this.baseDate, i <= 10 ? -35 + i : 1 + (i - 10) * 2); const base = { id: `race-${i}`, leagueId, scheduledAt, - track: tracks[(i - 1) % tracks.length]!, + track: track.name.toString(), + trackId: track.id, car: cars[(i - 1) % cars.length]!, }; diff --git a/adapters/bootstrap/racing/RacingSeed.ts b/adapters/bootstrap/racing/RacingSeed.ts index 4b53bfae6..559ff0d08 100644 --- a/adapters/bootstrap/racing/RacingSeed.ts +++ b/adapters/bootstrap/racing/RacingSeed.ts @@ -6,6 +6,7 @@ import { RaceRegistration } from '@core/racing/domain/entities/RaceRegistration' import { Result as RaceResult } from '@core/racing/domain/entities/result/Result'; import { Standing } from '@core/racing/domain/entities/Standing'; import { Team } from '@core/racing/domain/entities/Team'; +import { Track } from '@core/racing/domain/entities/Track'; import type { TeamMembership } from '@core/racing/domain/types/TeamMembership'; import type { FeedItem } from '@core/social/domain/types/FeedItem'; import { RacingDriverFactory } from './RacingDriverFactory'; @@ -17,6 +18,7 @@ import { RacingRaceFactory } from './RacingRaceFactory'; import { RacingResultFactory } from './RacingResultFactory'; import { RacingStandingFactory } from './RacingStandingFactory'; import { RacingTeamFactory } from './RacingTeamFactory'; +import { RacingTrackFactory } from './RacingTrackFactory'; export type Friendship = { driverId: string; @@ -33,6 +35,7 @@ export type RacingSeed = { raceRegistrations: RaceRegistration[]; teams: Team[]; teamMemberships: TeamMembership[]; + tracks: Track[]; friendships: Friendship[]; feedEvents: FeedItem[]; }; @@ -45,7 +48,7 @@ export type RacingSeedOptions = { export const racingSeedDefaults: Readonly< Required > = { - driverCount: 32, + driverCount: 100, baseDate: new Date('2025-01-15T12:00:00.000Z'), }; @@ -60,23 +63,25 @@ class RacingSeedFactory { create(): RacingSeed { const driverFactory = new RacingDriverFactory(this.driverCount, this.baseDate); - const leagueFactory = new RacingLeagueFactory(this.baseDate); + const trackFactory = new RacingTrackFactory(); const raceFactory = new RacingRaceFactory(this.baseDate); const resultFactory = new RacingResultFactory(); const standingFactory = new RacingStandingFactory(); const membershipFactory = new RacingMembershipFactory(this.baseDate); - const teamFactory = new RacingTeamFactory(this.baseDate); const friendshipFactory = new RacingFriendshipFactory(); const feedFactory = new RacingFeedFactory(this.baseDate); const drivers = driverFactory.create(); + const tracks = trackFactory.create(); + const leagueFactory = new RacingLeagueFactory(this.baseDate, drivers); const leagues = leagueFactory.create(); - const races = raceFactory.create(leagues); + const teamFactory = new RacingTeamFactory(this.baseDate, drivers, leagues); + const teams = teamFactory.createTeams(); + const races = raceFactory.create(leagues, tracks); const results = resultFactory.create(drivers, races); const standings = standingFactory.create(leagues, races, results); const leagueMemberships = membershipFactory.createLeagueMemberships(drivers, leagues); const raceRegistrations = membershipFactory.createRaceRegistrations(races); - const teams = teamFactory.createTeams(); const teamMemberships = teamFactory.createTeamMemberships(drivers, teams); const friendships = friendshipFactory.create(drivers); const feedEvents = feedFactory.create(drivers, friendships, races, leagues); @@ -91,6 +96,7 @@ class RacingSeedFactory { raceRegistrations, teams, teamMemberships, + tracks, friendships, feedEvents, }; diff --git a/adapters/bootstrap/racing/RacingTeamFactory.ts b/adapters/bootstrap/racing/RacingTeamFactory.ts index da0d8de87..0e28c8699 100644 --- a/adapters/bootstrap/racing/RacingTeamFactory.ts +++ b/adapters/bootstrap/racing/RacingTeamFactory.ts @@ -1,92 +1,72 @@ import { Driver } from '@core/racing/domain/entities/Driver'; +import { League } from '@core/racing/domain/entities/League'; import { Team } from '@core/racing/domain/entities/Team'; import type { TeamMembership } from '@core/racing/domain/types/TeamMembership'; +import { faker } from '@faker-js/faker'; export class RacingTeamFactory { - constructor(private readonly baseDate: Date) {} + constructor( + private readonly baseDate: Date, + private readonly drivers: Driver[], + private readonly leagues: League[], + ) {} createTeams(): Team[] { - return [ - Team.create({ - id: 'team-1', - name: 'Apex Racing', - tag: 'APEX', - description: 'Demo team focused on clean racing.', - ownerId: 'driver-1', - leagues: ['league-5'], - createdAt: this.addDays(this.baseDate, -100), - }), - Team.create({ - id: 'team-2', - name: 'Night Owls', - tag: 'NITE', - description: 'Late-night grinders and endurance lovers.', - ownerId: 'driver-2', - leagues: ['league-4'], - createdAt: this.addDays(this.baseDate, -90), - }), - Team.create({ - id: 'team-3', - name: 'Club Legends', - tag: 'CLUB', - description: 'A casual team for ladder climbing.', - ownerId: 'driver-3', - leagues: ['league-3'], - createdAt: this.addDays(this.baseDate, -80), - }), - ]; + const teamCount = 15; + + return Array.from({ length: teamCount }, (_, idx) => { + const i = idx + 1; + const owner = faker.helpers.arrayElement(this.drivers); + const teamLeagues = faker.helpers.arrayElements( + this.leagues.map(l => l.id.toString()), + { min: 0, max: 3 } + ); + + return Team.create({ + id: `team-${i}`, + name: faker.company.name() + ' Racing', + tag: faker.string.alpha({ length: 4, casing: 'upper' }), + description: faker.lorem.sentences(2), + ownerId: owner.id, + leagues: teamLeagues, + createdAt: faker.date.past({ years: 2, refDate: this.baseDate }), + }); + }); } createTeamMemberships(drivers: Driver[], teams: Team[]): TeamMembership[] { const memberships: TeamMembership[] = []; + const usedDrivers = new Set(); - const team1 = teams.find((t) => t.id === 'team-1'); - const team2 = teams.find((t) => t.id === 'team-2'); - const team3 = teams.find((t) => t.id === 'team-3'); + teams.forEach((team) => { + const availableDrivers = drivers.filter(d => !usedDrivers.has(d.id.toString()) && d.id.toString() !== team.ownerId.toString()); + const memberCount = faker.number.int({ min: 1, max: 8 }); + const members = faker.helpers.arrayElements(availableDrivers, memberCount); - if (team1) { - const members = drivers.slice(0, 6); - members.forEach((d, idx) => { - memberships.push({ - teamId: team1.id, - driverId: d.id, - role: d.id === team1.ownerId.toString() ? 'owner' : idx === 1 ? 'manager' : 'driver', - status: 'active', - joinedAt: this.addDays(this.baseDate, -50), - }); + // Add owner + memberships.push({ + teamId: team.id.toString(), + driverId: team.ownerId.toString(), + role: 'owner', + status: 'active', + joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }), }); - } + usedDrivers.add(team.ownerId.toString()); - if (team2) { - const members = drivers.slice(6, 12); - members.forEach((d) => { + // Add members + members.forEach((driver) => { memberships.push({ - teamId: team2.id, - driverId: d.id, - role: d.id === team2.ownerId.toString() ? 'owner' : 'driver', + teamId: team.id.toString(), + driverId: driver.id.toString(), + role: faker.helpers.arrayElement(['driver', 'manager']), status: 'active', - joinedAt: this.addDays(this.baseDate, -45), + joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }), }); + usedDrivers.add(driver.id.toString()); }); - } - - if (team3) { - const members = drivers.slice(12, 18); - members.forEach((d) => { - memberships.push({ - teamId: team3.id, - driverId: d.id, - role: d.id === team3.ownerId.toString() ? 'owner' : 'driver', - status: 'active', - joinedAt: this.addDays(this.baseDate, -40), - }); - }); - } + }); return memberships; } - private addDays(date: Date, days: number): Date { - return new Date(date.getTime() + days * 24 * 60 * 60 * 1000); - } } \ No newline at end of file diff --git a/adapters/bootstrap/racing/RacingTrackFactory.ts b/adapters/bootstrap/racing/RacingTrackFactory.ts new file mode 100644 index 000000000..66f6ed120 --- /dev/null +++ b/adapters/bootstrap/racing/RacingTrackFactory.ts @@ -0,0 +1,92 @@ +import { Track } from '@core/racing/domain/entities/Track'; + +export class RacingTrackFactory { + create(): Track[] { + return [ + Track.create({ + id: 'track-spa', + name: 'Spa-Francorchamps', + shortName: 'SPA', + country: 'Belgium', + category: 'road', + difficulty: 'advanced', + lengthKm: 7.004, + turns: 19, + imageUrl: '/images/tracks/spa.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-monza', + name: 'Autodromo Nazionale Monza', + shortName: 'MON', + country: 'Italy', + category: 'road', + difficulty: 'intermediate', + lengthKm: 5.793, + turns: 11, + imageUrl: '/images/tracks/monza.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-nurburgring', + name: 'Nürburgring Grand Prix', + shortName: 'NUR', + country: 'Germany', + category: 'road', + difficulty: 'advanced', + lengthKm: 5.148, + turns: 15, + imageUrl: '/images/tracks/nurburgring.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-silverstone', + name: 'Silverstone Circuit', + shortName: 'SIL', + country: 'United Kingdom', + category: 'road', + difficulty: 'intermediate', + lengthKm: 5.891, + turns: 18, + imageUrl: '/images/tracks/silverstone.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-suzuka', + name: 'Suzuka International Racing Course', + shortName: 'SUZ', + country: 'Japan', + category: 'road', + difficulty: 'expert', + lengthKm: 5.807, + turns: 18, + imageUrl: '/images/tracks/suzuka.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-daytona', + name: 'Daytona International Speedway', + shortName: 'DAY', + country: 'United States', + category: 'oval', + difficulty: 'intermediate', + lengthKm: 4.023, + turns: 4, + imageUrl: '/images/tracks/daytona.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-laguna', + name: 'WeatherTech Raceway Laguna Seca', + shortName: 'LAG', + country: 'United States', + category: 'road', + difficulty: 'advanced', + lengthKm: 3.602, + turns: 11, + imageUrl: '/images/tracks/laguna.jpg', + gameId: 'iracing', + }), + ]; + } +} \ No newline at end of file diff --git a/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.test.ts b/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.test.ts index f6586e79a..02a9d9476 100644 --- a/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.test.ts +++ b/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.test.ts @@ -35,6 +35,7 @@ describe('CompleteDriverOnboardingUseCase', () => { useCase = new CompleteDriverOnboardingUseCase( driverRepository as unknown as IDriverRepository, logger, + output, ); }); @@ -65,7 +66,7 @@ describe('CompleteDriverOnboardingUseCase', () => { const result = await useCase.execute(command); expect(result.isOk()).toBe(true); - expect(result.unwrap()).toEqual({ driver: createdDriver }); + expect(output.present).toHaveBeenCalledWith({ driver: createdDriver }); expect(driverRepository.findById).toHaveBeenCalledWith('user-1'); expect(driverRepository.create).toHaveBeenCalledWith( expect.objectContaining({ @@ -143,7 +144,7 @@ describe('CompleteDriverOnboardingUseCase', () => { const result = await useCase.execute(command); expect(result.isOk()).toBe(true); - expect(result.unwrap()).toEqual({ driver: createdDriver }); + expect(output.present).toHaveBeenCalledWith({ driver: createdDriver }); expect(driverRepository.create).toHaveBeenCalledWith( expect.objectContaining({ id: 'user-1', diff --git a/core/racing/application/use-cases/GetDriversLeaderboardUseCase.test.ts b/core/racing/application/use-cases/GetDriversLeaderboardUseCase.test.ts index 8fd743620..a6f17a03a 100644 --- a/core/racing/application/use-cases/GetDriversLeaderboardUseCase.test.ts +++ b/core/racing/application/use-cases/GetDriversLeaderboardUseCase.test.ts @@ -7,6 +7,8 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit 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(); @@ -39,6 +41,10 @@ describe('GetDriversLeaderboardUseCase', () => { error: vi.fn(), }; + const mockOutput: UseCaseOutputPort = { + present: vi.fn(), + }; + it('should return drivers leaderboard data', async () => { const useCase = new GetDriversLeaderboardUseCase( mockDriverRepo, @@ -46,6 +52,7 @@ describe('GetDriversLeaderboardUseCase', () => { mockDriverStatsService, mockGetDriverAvatar, mockLogger, + mockOutput, ); const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } }; @@ -75,9 +82,8 @@ describe('GetDriversLeaderboardUseCase', () => { const result = await useCase.execute(input); expect(result.isOk()).toBe(true); - const presented = result.unwrap(); - expect(presented).toEqual({ + expect(mockOutput.present).toHaveBeenCalledWith({ items: [ expect.objectContaining({ driver: driver1, @@ -115,6 +121,7 @@ describe('GetDriversLeaderboardUseCase', () => { mockDriverStatsService, mockGetDriverAvatar, mockLogger, + mockOutput, ); mockDriverFindAll.mockResolvedValue([]); @@ -125,9 +132,8 @@ describe('GetDriversLeaderboardUseCase', () => { const result = await useCase.execute(input); expect(result.isOk()).toBe(true); - const presented = result.unwrap(); - expect(presented).toEqual({ + expect(mockOutput.present).toHaveBeenCalledWith({ items: [], totalRaces: 0, totalWins: 0, @@ -142,6 +148,7 @@ describe('GetDriversLeaderboardUseCase', () => { mockDriverStatsService, mockGetDriverAvatar, mockLogger, + mockOutput, ); const driver1 = { id: 'driver1', name: { value: 'Driver One' }, country: { value: 'US' } }; @@ -157,9 +164,8 @@ describe('GetDriversLeaderboardUseCase', () => { const result = await useCase.execute(input); expect(result.isOk()).toBe(true); - const presented = result.unwrap(); - expect(presented).toEqual({ + expect(mockOutput.present).toHaveBeenCalledWith({ items: [ expect.objectContaining({ driver: driver1, @@ -186,6 +192,7 @@ describe('GetDriversLeaderboardUseCase', () => { mockDriverStatsService, mockGetDriverAvatar, mockLogger, + mockOutput, ); const error = new Error('Repository error'); diff --git a/core/racing/application/use-cases/GetTotalDriversUseCase.test.ts b/core/racing/application/use-cases/GetTotalDriversUseCase.test.ts index e34b7c106..979baa84b 100644 --- a/core/racing/application/use-cases/GetTotalDriversUseCase.test.ts +++ b/core/racing/application/use-cases/GetTotalDriversUseCase.test.ts @@ -3,21 +3,27 @@ import { GetTotalDriversUseCase, GetTotalDriversInput, GetTotalDriversErrorCode, + GetTotalDriversResult, } from './GetTotalDriversUseCase'; import { IDriverRepository } from '../../domain/repositories/IDriverRepository'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; +import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; describe('GetTotalDriversUseCase', () => { let useCase: GetTotalDriversUseCase; let driverRepository: { findAll: Mock; }; + let output: UseCaseOutputPort; beforeEach(() => { driverRepository = { findAll: vi.fn(), }; + output = { + present: vi.fn(), + }; - useCase = new GetTotalDriversUseCase(driverRepository as unknown as IDriverRepository); + useCase = new GetTotalDriversUseCase(driverRepository as unknown as IDriverRepository, output); }); it('should return total number of drivers', async () => { @@ -30,7 +36,7 @@ describe('GetTotalDriversUseCase', () => { const result = await useCase.execute(input); expect(result.isOk()).toBe(true); - expect(result.unwrap()).toEqual({ totalDrivers: 2 }); + expect(output.present).toHaveBeenCalledWith({ totalDrivers: 2 }); }); it('should return error on repository failure', async () => { diff --git a/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.test.ts b/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.test.ts index dec778ada..845b09692 100644 --- a/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.test.ts +++ b/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.test.ts @@ -3,10 +3,12 @@ import { IsDriverRegisteredForRaceUseCase, type IsDriverRegisteredForRaceInput, type IsDriverRegisteredForRaceErrorCode, + type IsDriverRegisteredForRaceResult, } from './IsDriverRegisteredForRaceUseCase'; import { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository'; import type { Logger } from '@core/shared/application'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; +import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; describe('IsDriverRegisteredForRaceUseCase', () => { let useCase: IsDriverRegisteredForRaceUseCase; @@ -19,6 +21,7 @@ describe('IsDriverRegisteredForRaceUseCase', () => { warn: Mock; error: Mock; }; + let output: UseCaseOutputPort; beforeEach(() => { registrationRepository = { isRegistered: vi.fn(), @@ -29,9 +32,13 @@ describe('IsDriverRegisteredForRaceUseCase', () => { warn: vi.fn(), error: vi.fn(), }; + output = { + present: vi.fn(), + }; useCase = new IsDriverRegisteredForRaceUseCase( registrationRepository as unknown as IRaceRegistrationRepository, logger as unknown as Logger, + output, ); }); @@ -43,7 +50,7 @@ describe('IsDriverRegisteredForRaceUseCase', () => { const result = await useCase.execute(params); expect(result.isOk()).toBe(true); - expect(result.unwrap()).toEqual({ + expect(output.present).toHaveBeenCalledWith({ raceId: params.raceId, driverId: params.driverId, isRegistered: true, @@ -58,7 +65,7 @@ describe('IsDriverRegisteredForRaceUseCase', () => { const result = await useCase.execute(params); expect(result.isOk()).toBe(true); - expect(result.unwrap()).toEqual({ + expect(output.present).toHaveBeenCalledWith({ raceId: params.raceId, driverId: params.driverId, isRegistered: false, diff --git a/package-lock.json b/package-lock.json index 702873671..970e7e48b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "eslint-import-resolver-typescript": "2.7.1", "eslint-plugin-boundaries": "^5.3.1", "eslint-plugin-import": "^2.32.0", + "faker": "^6.6.6", "glob": "^13.0.0", "husky": "^9.1.7", "jsdom": "^22.1.0", @@ -87,7 +88,8 @@ }, "devDependencies": { "@nestjs/testing": "^10.4.20", - "ts-node-dev": "^2.0.0" + "ts-node-dev": "^2.0.0", + "tsconfig-paths": "^3.15.0" } }, "apps/api/node_modules/reflect-metadata": { @@ -8126,6 +8128,13 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/faker": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/faker/-/faker-6.6.6.tgz", + "integrity": "sha512-9tCqYEDHI5RYFQigXFwF1hnCwcWCOJl/hmll0lr5D2Ljjb0o4wphb69wikeJDz5qCEzXCoPvG6ss5SDP6IfOdg==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", diff --git a/package.json b/package.json index f2b9056bf..1a2eecd5f 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,10 @@ "commander": "^11.0.0", "electron": "^39.2.7", "eslint": "^8.0.0", + "eslint-import-resolver-typescript": "2.7.1", "eslint-plugin-boundaries": "^5.3.1", "eslint-plugin-import": "^2.32.0", - "eslint-import-resolver-typescript": "2.7.1", + "faker": "^6.6.6", "glob": "^13.0.0", "husky": "^9.1.7", "jsdom": "^22.1.0",