From 58d9a1c762e836f04ce80487d17a34f0e95a4301 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sat, 27 Dec 2025 02:00:17 +0100 Subject: [PATCH] more seeds --- .../bootstrap/racing/RacingLeagueFactory.ts | 45 +++++-- .../bootstrap/racing/RacingRaceFactory.ts | 69 ++++++++-- .../bootstrap/racing/RacingTeamFactory.ts | 56 +++++++- .../bootstrap/racing/RacingTrackFactory.ts | 126 +++++++++++++++++- 4 files changed, 258 insertions(+), 38 deletions(-) diff --git a/adapters/bootstrap/racing/RacingLeagueFactory.ts b/adapters/bootstrap/racing/RacingLeagueFactory.ts index 0cbbfb2a2..2a8703842 100644 --- a/adapters/bootstrap/racing/RacingLeagueFactory.ts +++ b/adapters/bootstrap/racing/RacingLeagueFactory.ts @@ -10,16 +10,28 @@ export class RacingLeagueFactory { create(): League[] { const leagueCount = 20; - const pointsSystems = ['f1-2024', 'indycar', 'custom'] as const; - const qualifyingFormats = ['open', 'single-lap'] as const; + + // Create diverse league configurations + const leagueConfigs = [ + // Small sprint leagues + { maxDrivers: 16, sessionDuration: 30, pointsSystem: 'f1-2024' as const, qualifyingFormat: 'single-lap' as const }, + { maxDrivers: 20, sessionDuration: 45, pointsSystem: 'f1-2024' as const, qualifyingFormat: 'open' as const }, + // Medium endurance leagues + { maxDrivers: 24, sessionDuration: 60, pointsSystem: 'indycar' as const, qualifyingFormat: 'open' as const }, + { maxDrivers: 28, sessionDuration: 90, pointsSystem: 'custom' as const, qualifyingFormat: 'open' as const }, + // Large mixed leagues + { maxDrivers: 32, sessionDuration: 120, pointsSystem: 'f1-2024' as const, qualifyingFormat: 'open' as const }, + { maxDrivers: 36, sessionDuration: 75, pointsSystem: 'indycar' as const, qualifyingFormat: 'single-lap' as const }, + { maxDrivers: 40, sessionDuration: 100, pointsSystem: 'custom' as const, qualifyingFormat: 'open' as const }, + { maxDrivers: 44, sessionDuration: 85, pointsSystem: 'f1-2024' as const, qualifyingFormat: 'open' as const }, + { maxDrivers: 48, sessionDuration: 110, pointsSystem: 'indycar' as const, qualifyingFormat: 'single-lap' as const }, + { maxDrivers: 50, sessionDuration: 95, pointsSystem: 'custom' as const, qualifyingFormat: 'open' as const }, + ]; 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(); + const config = leagueConfigs[idx % leagueConfigs.length]!; const leagueData: { id: string; @@ -39,15 +51,24 @@ export class RacingLeagueFactory { 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), - }, + settings: config, createdAt: faker.date.past({ years: 2, refDate: this.baseDate }), }; + // Add social links with varying completeness + const socialLinks: { discordUrl?: string; youtubeUrl?: string; websiteUrl?: string } = {}; + const socialLinkTypes = ['discord', 'youtube', 'website'] as const; + + // Ensure some leagues have no social links, some have partial, some have all + const numSocialLinks = idx % 4; // 0, 1, 2, or 3 + const selectedTypes = faker.helpers.arrayElements(socialLinkTypes, numSocialLinks); + + selectedTypes.forEach(type => { + if (type === 'discord') socialLinks.discordUrl = faker.internet.url(); + if (type === 'youtube') socialLinks.youtubeUrl = faker.internet.url(); + if (type === 'website') socialLinks.websiteUrl = faker.internet.url(); + }); + if (Object.keys(socialLinks).length > 0) { leagueData.socialLinks = socialLinks; } diff --git a/adapters/bootstrap/racing/RacingRaceFactory.ts b/adapters/bootstrap/racing/RacingRaceFactory.ts index e3137dff5..cc04f2d68 100644 --- a/adapters/bootstrap/racing/RacingRaceFactory.ts +++ b/adapters/bootstrap/racing/RacingRaceFactory.ts @@ -14,11 +14,41 @@ export class RacingRaceFactory { const races: Race[] = []; + // Create races with systematic coverage of different statuses and scenarios + const statuses: Array<'scheduled' | 'running' | 'completed' | 'cancelled'> = ['scheduled', 'running', 'completed', 'cancelled']; + 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); + + // Determine status systematically to ensure coverage + let status: 'scheduled' | 'running' | 'completed' | 'cancelled'; + let scheduledAt: Date; + + if (i <= 4) { + // First 4 races: one of each status + status = statuses[i - 1]!; + scheduledAt = this.addDays(this.baseDate, i <= 2 ? -35 + i : 1 + (i - 2) * 2); + } else if (i <= 10) { + // Next 6: completed races + status = 'completed'; + scheduledAt = this.addDays(this.baseDate, -35 + i); + } else if (i <= 15) { + // Next 5: scheduled future races + status = 'scheduled'; + scheduledAt = this.addDays(this.baseDate, 1 + (i - 10) * 3); + } else if (i <= 20) { + // Next 5: cancelled races + status = 'cancelled'; + scheduledAt = this.addDays(this.baseDate, -20 + (i - 15)); + } else { + // Rest: mix of scheduled and completed + status = i % 3 === 0 ? 'completed' : 'scheduled'; + scheduledAt = status === 'completed' + ? this.addDays(this.baseDate, -10 + (i - 20)) + : this.addDays(this.baseDate, 5 + (i - 20) * 2); + } const base = { id: `race-${i}`, @@ -29,34 +59,53 @@ export class RacingRaceFactory { car: cars[(i - 1) % cars.length]!, }; - if (i === 1) { + // Special case for running race + if (status === 'running') { races.push( Race.create({ ...base, - leagueId: demoLeagueId, - scheduledAt: this.addMinutes(this.baseDate, -30), status: 'running', - strengthOfField: 1530, - registeredCount: 16, + strengthOfField: 1400 + (i * 10), // Varying SOF + registeredCount: 12 + (i % 5), // Varying registration counts }), ); continue; } - if (scheduledAt < this.baseDate) { + // Add varying SOF and registration counts for completed races + if (status === 'completed') { races.push( Race.create({ ...base, status: 'completed', + strengthOfField: 1200 + (i * 15), + registeredCount: 8 + (i % 8), }), ); continue; } + // Scheduled races with some having registration data + if (status === 'scheduled') { + const hasRegistrations = i % 4 !== 0; // 75% have registrations + races.push( + Race.create({ + ...base, + status: 'scheduled', + ...(hasRegistrations && { + strengthOfField: 1300 + (i * 8), + registeredCount: 5 + (i % 10), + }), + }), + ); + continue; + } + + // Cancelled races races.push( Race.create({ ...base, - status: 'scheduled', + status: 'cancelled', }), ); } @@ -67,8 +116,4 @@ export class RacingRaceFactory { private addDays(date: Date, days: number): Date { return new Date(date.getTime() + days * 24 * 60 * 60 * 1000); } - - private addMinutes(date: Date, minutes: number): Date { - return new Date(date.getTime() + minutes * 60 * 1000); - } } \ No newline at end of file diff --git a/adapters/bootstrap/racing/RacingTeamFactory.ts b/adapters/bootstrap/racing/RacingTeamFactory.ts index 0e28c8699..34031e29b 100644 --- a/adapters/bootstrap/racing/RacingTeamFactory.ts +++ b/adapters/bootstrap/racing/RacingTeamFactory.ts @@ -38,9 +38,35 @@ export class RacingTeamFactory { const memberships: TeamMembership[] = []; const usedDrivers = new Set(); - teams.forEach((team) => { + teams.forEach((team, teamIndex) => { 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 }); + + // Create varied team compositions + let memberCount: number; + let hasManager: boolean; + + if (teamIndex % 5 === 0) { + // Solo teams (just owner) + memberCount = 0; + hasManager = false; + } else if (teamIndex % 5 === 1) { + // Small teams (2-3 members) + memberCount = faker.number.int({ min: 1, max: 2 }); + hasManager = faker.datatype.boolean(); + } else if (teamIndex % 5 === 2) { + // Medium teams (3-5 members) + memberCount = faker.number.int({ min: 2, max: 4 }); + hasManager = true; + } else if (teamIndex % 5 === 3) { + // Large teams (5-7 members) + memberCount = faker.number.int({ min: 4, max: 6 }); + hasManager = true; + } else { + // Mixed - sometimes with manager, sometimes without + memberCount = faker.number.int({ min: 1, max: 5 }); + hasManager = faker.datatype.boolean(); + } + const members = faker.helpers.arrayElements(availableDrivers, memberCount); // Add owner @@ -53,16 +79,32 @@ export class RacingTeamFactory { }); usedDrivers.add(team.ownerId.toString()); - // Add members - members.forEach((driver) => { + // Add manager if needed + if (hasManager && members.length > 0) { + const managerIndex = faker.number.int({ min: 0, max: members.length - 1 }); + const manager = members[managerIndex]!; memberships.push({ teamId: team.id.toString(), - driverId: driver.id.toString(), - role: faker.helpers.arrayElement(['driver', 'manager']), + driverId: manager.id.toString(), + role: 'manager', status: 'active', joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }), }); - usedDrivers.add(driver.id.toString()); + usedDrivers.add(manager.id.toString()); + } + + // Add remaining members as drivers + members.forEach((driver) => { + if (!usedDrivers.has(driver.id.toString())) { + memberships.push({ + teamId: team.id.toString(), + driverId: driver.id.toString(), + role: 'driver', + status: 'active', + joinedAt: faker.date.past({ years: 1, refDate: team.createdAt.toDate() }), + }); + usedDrivers.add(driver.id.toString()); + } }); }); diff --git a/adapters/bootstrap/racing/RacingTrackFactory.ts b/adapters/bootstrap/racing/RacingTrackFactory.ts index 66f6ed120..3c0061c96 100644 --- a/adapters/bootstrap/racing/RacingTrackFactory.ts +++ b/adapters/bootstrap/racing/RacingTrackFactory.ts @@ -3,6 +3,7 @@ import { Track } from '@core/racing/domain/entities/Track'; export class RacingTrackFactory { create(): Track[] { return [ + // Road tracks - various difficulties Track.create({ id: 'track-spa', name: 'Spa-Francorchamps', @@ -63,6 +64,67 @@ export class RacingTrackFactory { imageUrl: '/images/tracks/suzuka.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', + }), + Track.create({ + id: 'track-zandvoort', + name: 'Circuit Zandvoort', + shortName: 'ZAN', + country: 'Netherlands', + category: 'road', + difficulty: 'intermediate', + lengthKm: 4.259, + turns: 14, + imageUrl: '/images/tracks/zandvoort.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-imola', + name: 'Autodromo Enzo e Dino Ferrari', + shortName: 'IMO', + country: 'Italy', + category: 'road', + difficulty: 'advanced', + lengthKm: 4.909, + turns: 19, + imageUrl: '/images/tracks/imola.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-le-mans', + name: 'Circuit de la Sarthe', + shortName: 'LEM', + country: 'France', + category: 'road', + difficulty: 'expert', + lengthKm: 13.626, + turns: 38, + imageUrl: '/images/tracks/le-mans.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-hockenheim', + name: 'Hockenheimring', + shortName: 'HOC', + country: 'Germany', + category: 'road', + difficulty: 'intermediate', + lengthKm: 4.574, + turns: 17, + imageUrl: '/images/tracks/hockenheim.jpg', + gameId: 'iracing', + }), + // Oval tracks Track.create({ id: 'track-daytona', name: 'Daytona International Speedway', @@ -76,15 +138,65 @@ export class RacingTrackFactory { gameId: 'iracing', }), Track.create({ - id: 'track-laguna', - name: 'WeatherTech Raceway Laguna Seca', - shortName: 'LAG', + id: 'track-indianapolis', + name: 'Indianapolis Motor Speedway', + shortName: 'IMS', country: 'United States', - category: 'road', + category: 'oval', difficulty: 'advanced', - lengthKm: 3.602, - turns: 11, - imageUrl: '/images/tracks/laguna.jpg', + lengthKm: 4.192, + turns: 4, + imageUrl: '/images/tracks/indianapolis.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-talladega', + name: 'Talladega Superspeedway', + shortName: 'TAL', + country: 'United States', + category: 'oval', + difficulty: 'beginner', + lengthKm: 4.280, + turns: 4, + imageUrl: '/images/tracks/talladega.jpg', + gameId: 'iracing', + }), + // Street tracks + Track.create({ + id: 'track-miami', + name: 'Miami Street Circuit', + shortName: 'MIA', + country: 'United States', + category: 'street', + difficulty: 'intermediate', + lengthKm: 5.410, + turns: 19, + imageUrl: '/images/tracks/miami.jpg', + gameId: 'iracing', + }), + Track.create({ + id: 'track-las-vegas', + name: 'Las Vegas Street Circuit', + shortName: 'VEG', + country: 'United States', + category: 'street', + difficulty: 'advanced', + lengthKm: 6.201, + turns: 17, + imageUrl: '/images/tracks/las-vegas.jpg', + gameId: 'iracing', + }), + // Dirt tracks + Track.create({ + id: 'track-eldo', + name: 'Eldora Speedway', + shortName: 'ELD', + country: 'United States', + category: 'dirt', + difficulty: 'beginner', + lengthKm: 0.805, + turns: 4, + imageUrl: '/images/tracks/eldora.jpg', gameId: 'iracing', }), ];