mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:51:14 -05:00
feat: Update test data generators
This commit is contained in:
@@ -1,6 +1,23 @@
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
|
import {
|
||||||
|
randomInt,
|
||||||
|
randomFloat,
|
||||||
|
randomItem,
|
||||||
|
randomDate,
|
||||||
|
randomDatePrecision,
|
||||||
|
shouldPopulateField,
|
||||||
|
generateWaterRideFields,
|
||||||
|
generateDarkRideFields,
|
||||||
|
generateFlatRideFields,
|
||||||
|
generateKiddieRideFields,
|
||||||
|
generateTransportationRideFields,
|
||||||
|
generateRideMaterials,
|
||||||
|
generateSourceUrl,
|
||||||
|
generateSubmissionNotes,
|
||||||
|
type FieldDensity
|
||||||
|
} from './testDataGeneratorHelpers';
|
||||||
|
|
||||||
// Preset configurations
|
// Preset configurations
|
||||||
export const PRESETS = {
|
export const PRESETS = {
|
||||||
@@ -21,26 +38,6 @@ const COMPANY_SUFFIXES = ['Industries', 'Manufacturing', 'Enterprises', 'Solutio
|
|||||||
const CITIES = ['New York', 'Los Angeles', 'Chicago', 'London', 'Paris', 'Tokyo', 'Sydney', 'Toronto', 'Berlin', 'Madrid'];
|
const CITIES = ['New York', 'Los Angeles', 'Chicago', 'London', 'Paris', 'Tokyo', 'Sydney', 'Toronto', 'Berlin', 'Madrid'];
|
||||||
const COUNTRIES = ['USA', 'UK', 'France', 'Japan', 'Australia', 'Canada', 'Germany', 'Spain', 'Italy', 'Netherlands'];
|
const COUNTRIES = ['USA', 'UK', 'France', 'Japan', 'Australia', 'Canada', 'Germany', 'Spain', 'Italy', 'Netherlands'];
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function randomItem<T>(array: T[]): T {
|
|
||||||
return array[Math.floor(Math.random() * array.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomInt(min: number, max: number): number {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomFloat(min: number, max: number, decimals = 2): number {
|
|
||||||
return parseFloat((Math.random() * (max - min) + min).toFixed(decimals));
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomDate(startYear: number, endYear: number): string {
|
|
||||||
const start = new Date(startYear, 0, 1);
|
|
||||||
const end = new Date(endYear, 11, 31);
|
|
||||||
const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
|
|
||||||
return date.toISOString().split('T')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateSlug(name: string, counter?: number): string {
|
function generateSlug(name: string, counter?: number): string {
|
||||||
let slug = name
|
let slug = name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
@@ -107,18 +104,20 @@ export class TestDataContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Random data generators
|
// Random data generators
|
||||||
export function generateRandomPark(counter: number): ParkSubmissionData {
|
export function generateRandomPark(counter: number, density: FieldDensity = 'mixed'): ParkSubmissionData {
|
||||||
const name = `${randomItem(PARK_ADJECTIVES)} ${randomItem(PARK_NOUNS)}`;
|
const name = `${randomItem(PARK_ADJECTIVES)} ${randomItem(PARK_NOUNS)}`;
|
||||||
const slug = generateSlug(name, counter);
|
const slug = generateSlug(name, counter);
|
||||||
|
const openingDate = randomDate(1950, 2024);
|
||||||
|
const status = randomItem(['operating', 'operating', 'operating', 'seasonal']); // More likely to be operating
|
||||||
|
|
||||||
return {
|
const parkData: ParkSubmissionData = {
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
description: `A thrilling amusement park featuring world-class attractions and entertainment.`,
|
description: `A thrilling amusement park featuring world-class attractions and entertainment.`,
|
||||||
park_type: randomItem(['theme_park', 'amusement_park', 'water_park', 'adventure_park']),
|
park_type: randomItem(['theme_park', 'amusement_park', 'water_park', 'adventure_park']),
|
||||||
status: randomItem(['operating', 'operating', 'operating', 'seasonal']), // More likely to be operating
|
status,
|
||||||
opening_date: randomDate(1950, 2024),
|
opening_date: openingDate,
|
||||||
closing_date: Math.random() > 0.9 ? randomDate(2000, 2024) : null,
|
opening_date_precision: randomDatePrecision(),
|
||||||
website_url: `https://${slug}.example.com`,
|
website_url: `https://${slug}.example.com`,
|
||||||
phone: `+1-555-${randomInt(100, 999)}-${randomInt(1000, 9999)}`,
|
phone: `+1-555-${randomInt(100, 999)}-${randomInt(1000, 9999)}`,
|
||||||
email: `info@${slug}.example.com`,
|
email: `info@${slug}.example.com`,
|
||||||
@@ -131,25 +130,48 @@ export function generateRandomPark(counter: number): ParkSubmissionData {
|
|||||||
card_image_id: null,
|
card_image_id: null,
|
||||||
is_test_data: true
|
is_test_data: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add closing date for closed/seasonal parks
|
||||||
|
if ((status === 'closed' || status === 'seasonal') && shouldPopulateField(density, counter, 'high')) {
|
||||||
|
const openYear = parseInt(openingDate.split('-')[0]);
|
||||||
|
parkData.closing_date = randomDate(openYear + 5, 2024);
|
||||||
|
parkData.closing_date_precision = randomDatePrecision();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add source URL
|
||||||
|
if (shouldPopulateField(density, counter, 'low')) {
|
||||||
|
parkData.source_url = generateSourceUrl('park', counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add submission notes
|
||||||
|
const notes = generateSubmissionNotes(density, counter, 'park');
|
||||||
|
if (notes) {
|
||||||
|
parkData.submission_notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parkData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateRandomRide(parkId: string, counter: number): RideSubmissionData {
|
export function generateRandomRide(parkId: string, counter: number, density: FieldDensity = 'mixed'): RideSubmissionData {
|
||||||
const name = `${randomItem(RIDE_PREFIXES)} ${randomItem(RIDE_TYPES)}`;
|
const name = `${randomItem(RIDE_PREFIXES)} ${randomItem(RIDE_TYPES)}`;
|
||||||
const slug = generateSlug(name, counter);
|
const slug = generateSlug(name, counter);
|
||||||
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride', 'family_ride']);
|
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride', 'family_ride', 'transport_ride', 'kiddie_ride']);
|
||||||
|
const openingDate = randomDate(1980, 2024);
|
||||||
|
const status = randomItem(['operating', 'operating', 'operating', 'seasonal']);
|
||||||
|
|
||||||
return {
|
const rideData: RideSubmissionData = {
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
description: `An exciting ${category.replace('_', ' ')} experience for all ages.`,
|
description: `An exciting ${category.replace('_', ' ')} experience for all ages.`,
|
||||||
category,
|
category,
|
||||||
ride_sub_type: null,
|
ride_sub_type: null,
|
||||||
status: randomItem(['operating', 'operating', 'operating', 'seasonal']),
|
status,
|
||||||
park_id: parkId,
|
park_id: parkId,
|
||||||
ride_model_id: null,
|
ride_model_id: null,
|
||||||
manufacturer_id: null,
|
manufacturer_id: null,
|
||||||
designer_id: null,
|
designer_id: null,
|
||||||
opening_date: randomDate(1980, 2024),
|
opening_date: openingDate,
|
||||||
|
opening_date_precision: randomDatePrecision(),
|
||||||
closing_date: Math.random() > 0.95 ? randomDate(2010, 2024) : null,
|
closing_date: Math.random() > 0.95 ? randomDate(2010, 2024) : null,
|
||||||
height_requirement: randomInt(90, 140),
|
height_requirement: randomInt(90, 140),
|
||||||
age_requirement: null,
|
age_requirement: null,
|
||||||
@@ -171,19 +193,57 @@ export function generateRandomRide(parkId: string, counter: number): RideSubmiss
|
|||||||
image_url: null,
|
image_url: null,
|
||||||
is_test_data: true
|
is_test_data: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add closing date precision if closing date exists
|
||||||
|
if (rideData.closing_date && shouldPopulateField(density, counter, 'medium')) {
|
||||||
|
rideData.closing_date_precision = randomDatePrecision();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add material arrays for appropriate rides
|
||||||
|
if (category === 'roller_coaster' && shouldPopulateField(density, counter, 'medium')) {
|
||||||
|
const materials = generateRideMaterials(category);
|
||||||
|
Object.assign(rideData, materials);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add category-specific fields
|
||||||
|
if (category === 'water_ride') {
|
||||||
|
Object.assign(rideData, generateWaterRideFields(density, counter));
|
||||||
|
} else if (category === 'dark_ride') {
|
||||||
|
Object.assign(rideData, generateDarkRideFields(density, counter));
|
||||||
|
} else if (category === 'flat_ride') {
|
||||||
|
Object.assign(rideData, generateFlatRideFields(density, counter));
|
||||||
|
} else if (category === 'kiddie_ride') {
|
||||||
|
Object.assign(rideData, generateKiddieRideFields(density, counter));
|
||||||
|
} else if (category === 'transport_ride') {
|
||||||
|
Object.assign(rideData, generateTransportationRideFields(density, counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add source URL
|
||||||
|
if (shouldPopulateField(density, counter, 'low')) {
|
||||||
|
rideData.source_url = generateSourceUrl('ride', counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add submission notes
|
||||||
|
const notes = generateSubmissionNotes(density, counter, 'ride');
|
||||||
|
if (notes) {
|
||||||
|
rideData.submission_notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rideData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateRandomCompany(type: 'manufacturer' | 'operator' | 'designer' | 'property_owner', counter: number): CompanySubmissionData {
|
export function generateRandomCompany(type: 'manufacturer' | 'operator' | 'designer' | 'property_owner', counter: number, density: FieldDensity = 'mixed'): CompanySubmissionData {
|
||||||
const name = `${randomItem(COMPANY_PREFIXES)} ${randomItem(COMPANY_SUFFIXES)}`;
|
const name = `${randomItem(COMPANY_PREFIXES)} ${randomItem(COMPANY_SUFFIXES)}`;
|
||||||
const slug = generateSlug(name, counter);
|
const slug = generateSlug(name, counter);
|
||||||
|
const foundedYear = randomInt(1950, 2020);
|
||||||
|
|
||||||
return {
|
const companyData: CompanySubmissionData = {
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
company_type: type,
|
company_type: type,
|
||||||
description: `A leading ${type.replace('_', ' ')} in the amusement industry.`,
|
description: `A leading ${type.replace('_', ' ')} in the amusement industry.`,
|
||||||
person_type: Math.random() > 0.9 ? 'individual' : 'company',
|
person_type: Math.random() > 0.9 ? 'individual' : 'company',
|
||||||
founded_year: randomInt(1950, 2020),
|
founded_year: foundedYear,
|
||||||
headquarters_location: `${randomItem(CITIES)}, ${randomItem(COUNTRIES)}`,
|
headquarters_location: `${randomItem(CITIES)}, ${randomItem(COUNTRIES)}`,
|
||||||
website_url: `https://${slug}.example.com`,
|
website_url: `https://${slug}.example.com`,
|
||||||
logo_url: null,
|
logo_url: null,
|
||||||
@@ -193,14 +253,40 @@ export function generateRandomCompany(type: 'manufacturer' | 'operator' | 'desig
|
|||||||
card_image_id: null,
|
card_image_id: null,
|
||||||
is_test_data: true
|
is_test_data: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add full founded date with precision
|
||||||
|
if (shouldPopulateField(density, counter, 'medium')) {
|
||||||
|
companyData.founded_date = `${foundedYear}-01-01`;
|
||||||
|
companyData.founded_date_precision = randomItem(['year', 'month', 'day']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add defunct date for some companies
|
||||||
|
if (shouldPopulateField(density, counter, 'low') && Math.random() > 0.85) {
|
||||||
|
const defunctYear = randomInt(foundedYear + 10, 2024);
|
||||||
|
companyData.defunct_date = `${defunctYear}-12-31`;
|
||||||
|
companyData.defunct_date_precision = randomItem(['year', 'month', 'day']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add source URL
|
||||||
|
if (shouldPopulateField(density, counter, 'low')) {
|
||||||
|
companyData.source_url = generateSourceUrl('company', counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add submission notes
|
||||||
|
const notes = generateSubmissionNotes(density, counter, 'company');
|
||||||
|
if (notes) {
|
||||||
|
companyData.submission_notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return companyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateRandomRideModel(manufacturerId: string, counter: number): RideModelSubmissionData {
|
export function generateRandomRideModel(manufacturerId: string, counter: number, density: FieldDensity = 'mixed'): RideModelSubmissionData {
|
||||||
const name = `${randomItem(RIDE_PREFIXES)} Model ${randomInt(100, 999)}`;
|
const name = `${randomItem(RIDE_PREFIXES)} Model ${randomInt(100, 999)}`;
|
||||||
const slug = generateSlug(name, counter);
|
const slug = generateSlug(name, counter);
|
||||||
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']);
|
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']);
|
||||||
|
|
||||||
return {
|
const modelData: RideModelSubmissionData = {
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
manufacturer_id: manufacturerId,
|
manufacturer_id: manufacturerId,
|
||||||
@@ -213,6 +299,19 @@ export function generateRandomRideModel(manufacturerId: string, counter: number)
|
|||||||
card_image_id: null,
|
card_image_id: null,
|
||||||
is_test_data: true
|
is_test_data: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add source URL
|
||||||
|
if (shouldPopulateField(density, counter, 'low')) {
|
||||||
|
modelData.source_url = generateSourceUrl('ride-model', counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add submission notes
|
||||||
|
const notes = generateSubmissionNotes(density, counter, 'ride model');
|
||||||
|
if (notes) {
|
||||||
|
modelData.submission_notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup utilities
|
// Cleanup utilities
|
||||||
|
|||||||
191
src/lib/testDataGeneratorHelpers.ts
Normal file
191
src/lib/testDataGeneratorHelpers.ts
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* Test Data Generator Helpers
|
||||||
|
*
|
||||||
|
* Reusable utilities for generating category-specific fields and managing field density.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Field Density Logic
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export type FieldDensity = 'minimal' | 'standard' | 'maximum' | 'mixed';
|
||||||
|
export type FieldImportance = 'low' | 'medium' | 'high';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a field should be populated based on density and importance
|
||||||
|
*/
|
||||||
|
export function shouldPopulateField(
|
||||||
|
density: FieldDensity,
|
||||||
|
index: number,
|
||||||
|
fieldImportance: FieldImportance
|
||||||
|
): boolean {
|
||||||
|
const rand = Math.random();
|
||||||
|
|
||||||
|
if (density === 'minimal') {
|
||||||
|
return fieldImportance === 'high' ? rand < 0.5 : rand < 0.1;
|
||||||
|
} else if (density === 'standard') {
|
||||||
|
return fieldImportance === 'high' ? rand < 0.8 : rand < 0.5;
|
||||||
|
} else if (density === 'maximum') {
|
||||||
|
return rand < 0.95; // Almost everything
|
||||||
|
} else if (density === 'mixed') {
|
||||||
|
// Vary by entity index
|
||||||
|
if (index % 3 === 0) return shouldPopulateField('minimal', index, fieldImportance);
|
||||||
|
if (index % 3 === 1) return shouldPopulateField('standard', index, fieldImportance);
|
||||||
|
return shouldPopulateField('maximum', index, fieldImportance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Random Data Generators
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export function randomInt(min: number, max: number): number {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomFloat(min: number, max: number, decimals = 2): number {
|
||||||
|
const value = Math.random() * (max - min) + min;
|
||||||
|
return parseFloat(value.toFixed(decimals));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomItem<T>(array: T[]): T {
|
||||||
|
return array[randomInt(0, array.length - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomDate(startYear: number, endYear: number): string {
|
||||||
|
const year = randomInt(startYear, endYear);
|
||||||
|
const month = randomInt(1, 12);
|
||||||
|
const day = randomInt(1, 28);
|
||||||
|
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomDatePrecision(): 'day' | 'month' | 'year' {
|
||||||
|
return randomItem(['day', 'month', 'year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Category-Specific Field Generators
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate water ride specific fields
|
||||||
|
*/
|
||||||
|
export function generateWaterRideFields(density: FieldDensity, index: number) {
|
||||||
|
if (!shouldPopulateField(density, index, 'medium')) return {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
water_depth_cm: randomInt(30, 300),
|
||||||
|
splash_height_meters: randomFloat(1, 20, 1),
|
||||||
|
wetness_level: randomItem(['dry', 'light', 'moderate', 'soaked']),
|
||||||
|
flume_type: randomItem(['log', 'tube', 'raft', 'boat']),
|
||||||
|
boat_capacity: randomInt(2, 20)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate dark ride specific fields
|
||||||
|
*/
|
||||||
|
export function generateDarkRideFields(density: FieldDensity, index: number) {
|
||||||
|
if (!shouldPopulateField(density, index, 'medium')) return {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
theme_name: randomItem(['Space Adventure', 'Haunted Mansion', 'Underwater Journey', 'Fantasy Quest']),
|
||||||
|
story_description: 'An immersive journey through a themed environment with exciting scenes.',
|
||||||
|
show_duration_seconds: randomInt(180, 600),
|
||||||
|
animatronics_count: randomInt(5, 50),
|
||||||
|
projection_type: randomItem(['2d', '3d', 'holographic', 'mixed']),
|
||||||
|
ride_system: randomItem(['omnimover', 'tracked', 'trackless', 'boat']),
|
||||||
|
scenes_count: randomInt(5, 20)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate flat ride specific fields
|
||||||
|
*/
|
||||||
|
export function generateFlatRideFields(density: FieldDensity, index: number) {
|
||||||
|
if (!shouldPopulateField(density, index, 'medium')) return {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
rotation_type: randomItem(['horizontal', 'vertical', 'both', 'none']),
|
||||||
|
motion_pattern: randomItem(['circular', 'pendulum', 'spinning', 'wave', 'random']),
|
||||||
|
platform_count: randomInt(1, 8),
|
||||||
|
swing_angle_degrees: randomInt(45, 180),
|
||||||
|
rotation_speed_rpm: randomInt(5, 30),
|
||||||
|
arm_length_meters: randomFloat(5, 25, 1),
|
||||||
|
max_height_reached_meters: randomFloat(10, 80, 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate kiddie ride specific fields
|
||||||
|
*/
|
||||||
|
export function generateKiddieRideFields(density: FieldDensity, index: number) {
|
||||||
|
if (!shouldPopulateField(density, index, 'medium')) return {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
min_age: randomInt(2, 5),
|
||||||
|
max_age: randomInt(8, 12),
|
||||||
|
educational_theme: randomItem(['counting', 'colors', 'animals', 'shapes', 'letters']),
|
||||||
|
character_theme: randomItem(['dinosaurs', 'princesses', 'superheroes', 'animals', 'vehicles'])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate transportation ride specific fields
|
||||||
|
*/
|
||||||
|
export function generateTransportationRideFields(density: FieldDensity, index: number) {
|
||||||
|
if (!shouldPopulateField(density, index, 'medium')) return {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
transport_type: randomItem(['monorail', 'train', 'skyway', 'gondola', 'ferry']),
|
||||||
|
route_length_meters: randomInt(500, 5000),
|
||||||
|
stations_count: randomInt(2, 8),
|
||||||
|
vehicle_capacity: randomInt(20, 200),
|
||||||
|
vehicles_count: randomInt(2, 12),
|
||||||
|
round_trip_duration_seconds: randomInt(300, 1800)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate material arrays for rides
|
||||||
|
*/
|
||||||
|
export function generateRideMaterials(category: string) {
|
||||||
|
if (category === 'roller_coaster') {
|
||||||
|
return {
|
||||||
|
track_material: [randomItem(['steel', 'wood', 'hybrid'])],
|
||||||
|
support_material: [randomItem(['steel', 'wood', 'concrete'])],
|
||||||
|
propulsion_method: [randomItem(['chain_lift', 'cable_lift', 'launch', 'gravity', 'tire_drive'])]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate source URL for testing
|
||||||
|
*/
|
||||||
|
export function generateSourceUrl(entityType: string, index: number): string {
|
||||||
|
return `https://example.com/source/${entityType}/${index + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate submission notes based on field density
|
||||||
|
*/
|
||||||
|
export function generateSubmissionNotes(
|
||||||
|
density: FieldDensity,
|
||||||
|
index: number,
|
||||||
|
entityType: string
|
||||||
|
): string | undefined {
|
||||||
|
if (!shouldPopulateField(density, index, 'low')) return undefined;
|
||||||
|
|
||||||
|
const notes = [
|
||||||
|
`Updated ${entityType} information from official source`,
|
||||||
|
`Added comprehensive data for testing purposes`,
|
||||||
|
`Verified information through multiple sources`,
|
||||||
|
`Historical data added for completeness`,
|
||||||
|
`Technical specifications verified with manufacturer`
|
||||||
|
];
|
||||||
|
|
||||||
|
return randomItem(notes);
|
||||||
|
}
|
||||||
@@ -113,6 +113,8 @@ export interface CompanySubmissionData {
|
|||||||
founded_year?: number | null; // Legacy field
|
founded_year?: number | null; // Legacy field
|
||||||
founded_date?: string | null;
|
founded_date?: string | null;
|
||||||
founded_date_precision?: string | null;
|
founded_date_precision?: string | null;
|
||||||
|
defunct_date?: string | null;
|
||||||
|
defunct_date_precision?: string | null;
|
||||||
headquarters_location?: string | null;
|
headquarters_location?: string | null;
|
||||||
website_url?: string | null;
|
website_url?: string | null;
|
||||||
logo_url?: string | null;
|
logo_url?: string | null;
|
||||||
|
|||||||
181
tests/fixtures/test-data.ts
vendored
181
tests/fixtures/test-data.ts
vendored
@@ -17,6 +17,11 @@ export interface ParkTestData {
|
|||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
opened_date: string;
|
opened_date: string;
|
||||||
|
opening_date_precision?: string;
|
||||||
|
closing_date?: string;
|
||||||
|
closing_date_precision?: string;
|
||||||
|
source_url?: string;
|
||||||
|
submission_notes?: string;
|
||||||
is_test_data: boolean;
|
is_test_data: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +33,45 @@ export interface RideTestData {
|
|||||||
status: string;
|
status: string;
|
||||||
park_id: string;
|
park_id: string;
|
||||||
opened_date: string;
|
opened_date: string;
|
||||||
|
opening_date_precision?: string;
|
||||||
|
closing_date?: string;
|
||||||
|
closing_date_precision?: string;
|
||||||
|
track_material?: string[];
|
||||||
|
support_material?: string[];
|
||||||
|
propulsion_method?: string[];
|
||||||
|
source_url?: string;
|
||||||
|
submission_notes?: string;
|
||||||
is_test_data: boolean;
|
is_test_data: boolean;
|
||||||
|
// Category-specific fields (optional)
|
||||||
|
water_depth_cm?: number;
|
||||||
|
splash_height_meters?: number;
|
||||||
|
wetness_level?: string;
|
||||||
|
flume_type?: string;
|
||||||
|
boat_capacity?: number;
|
||||||
|
theme_name?: string;
|
||||||
|
story_description?: string;
|
||||||
|
show_duration_seconds?: number;
|
||||||
|
animatronics_count?: number;
|
||||||
|
projection_type?: string;
|
||||||
|
ride_system?: string;
|
||||||
|
scenes_count?: number;
|
||||||
|
rotation_type?: string;
|
||||||
|
motion_pattern?: string;
|
||||||
|
platform_count?: number;
|
||||||
|
swing_angle_degrees?: number;
|
||||||
|
rotation_speed_rpm?: number;
|
||||||
|
arm_length_meters?: number;
|
||||||
|
max_height_reached_meters?: number;
|
||||||
|
min_age?: number;
|
||||||
|
max_age?: number;
|
||||||
|
educational_theme?: string;
|
||||||
|
character_theme?: string;
|
||||||
|
transport_type?: string;
|
||||||
|
route_length_meters?: number;
|
||||||
|
stations_count?: number;
|
||||||
|
vehicle_capacity?: number;
|
||||||
|
vehicles_count?: number;
|
||||||
|
round_trip_duration_seconds?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompanyTestData {
|
export interface CompanyTestData {
|
||||||
@@ -38,6 +81,11 @@ export interface CompanyTestData {
|
|||||||
company_type: string;
|
company_type: string;
|
||||||
person_type: string;
|
person_type: string;
|
||||||
founded_date: string;
|
founded_date: string;
|
||||||
|
founded_date_precision?: string;
|
||||||
|
defunct_date?: string;
|
||||||
|
defunct_date_precision?: string;
|
||||||
|
source_url?: string;
|
||||||
|
submission_notes?: string;
|
||||||
is_test_data: boolean;
|
is_test_data: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +95,8 @@ export interface RideModelTestData {
|
|||||||
description: string;
|
description: string;
|
||||||
category: string;
|
category: string;
|
||||||
manufacturer_id: string;
|
manufacturer_id: string;
|
||||||
|
source_url?: string;
|
||||||
|
submission_notes?: string;
|
||||||
is_test_data: boolean;
|
is_test_data: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,21 +105,40 @@ export interface RideModelTestData {
|
|||||||
*/
|
*/
|
||||||
export function generateParkData(overrides?: Partial<ParkTestData>): ParkTestData {
|
export function generateParkData(overrides?: Partial<ParkTestData>): ParkTestData {
|
||||||
const name = faker.company.name() + ' Park';
|
const name = faker.company.name() + ' Park';
|
||||||
|
const openedDate = faker.date.past({ years: 50 }).toISOString().split('T')[0];
|
||||||
|
const status = faker.helpers.arrayElement(['operating', 'closed', 'under_construction']);
|
||||||
|
|
||||||
return {
|
const data: ParkTestData = {
|
||||||
name,
|
name,
|
||||||
slug: faker.helpers.slugify(name).toLowerCase(),
|
slug: faker.helpers.slugify(name).toLowerCase(),
|
||||||
description: faker.lorem.paragraphs(2),
|
description: faker.lorem.paragraphs(2),
|
||||||
park_type: faker.helpers.arrayElement(['theme_park', 'amusement_park', 'water_park']),
|
park_type: faker.helpers.arrayElement(['theme_park', 'amusement_park', 'water_park']),
|
||||||
status: faker.helpers.arrayElement(['operating', 'closed', 'under_construction']),
|
status,
|
||||||
location_country: faker.location.countryCode(),
|
location_country: faker.location.countryCode(),
|
||||||
location_city: faker.location.city(),
|
location_city: faker.location.city(),
|
||||||
latitude: parseFloat(faker.location.latitude()),
|
latitude: parseFloat(faker.location.latitude()),
|
||||||
longitude: parseFloat(faker.location.longitude()),
|
longitude: parseFloat(faker.location.longitude()),
|
||||||
opened_date: faker.date.past({ years: 50 }).toISOString().split('T')[0],
|
opened_date: openedDate,
|
||||||
|
opening_date_precision: faker.helpers.arrayElement(['day', 'month', 'year']),
|
||||||
is_test_data: true,
|
is_test_data: true,
|
||||||
...overrides,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add closing date for closed parks
|
||||||
|
if (status === 'closed') {
|
||||||
|
data.closing_date = faker.date.between({ from: openedDate, to: new Date() }).toISOString().split('T')[0];
|
||||||
|
data.closing_date_precision = faker.helpers.arrayElement(['day', 'month', 'year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add optional fields
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.source_url = faker.internet.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.submission_notes = faker.lorem.sentence();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...data, ...overrides };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,18 +146,74 @@ export function generateParkData(overrides?: Partial<ParkTestData>): ParkTestDat
|
|||||||
*/
|
*/
|
||||||
export function generateRideData(parkId: string, overrides?: Partial<RideTestData>): RideTestData {
|
export function generateRideData(parkId: string, overrides?: Partial<RideTestData>): RideTestData {
|
||||||
const name = faker.word.adjective() + ' ' + faker.word.noun();
|
const name = faker.word.adjective() + ' ' + faker.word.noun();
|
||||||
|
const openedDate = faker.date.past({ years: 30 }).toISOString().split('T')[0];
|
||||||
|
const status = faker.helpers.arrayElement(['operating', 'closed', 'sbno']);
|
||||||
|
const category = faker.helpers.arrayElement(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']);
|
||||||
|
|
||||||
return {
|
const data: RideTestData = {
|
||||||
name,
|
name,
|
||||||
slug: faker.helpers.slugify(name).toLowerCase(),
|
slug: faker.helpers.slugify(name).toLowerCase(),
|
||||||
description: faker.lorem.paragraphs(2),
|
description: faker.lorem.paragraphs(2),
|
||||||
category: faker.helpers.arrayElement(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']),
|
category,
|
||||||
status: faker.helpers.arrayElement(['operating', 'closed', 'sbno']),
|
status,
|
||||||
park_id: parkId,
|
park_id: parkId,
|
||||||
opened_date: faker.date.past({ years: 30 }).toISOString().split('T')[0],
|
opened_date: openedDate,
|
||||||
|
opening_date_precision: faker.helpers.arrayElement(['day', 'month', 'year']),
|
||||||
is_test_data: true,
|
is_test_data: true,
|
||||||
...overrides,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add closing date for closed rides
|
||||||
|
if (status === 'closed') {
|
||||||
|
data.closing_date = faker.date.between({ from: openedDate, to: new Date() }).toISOString().split('T')[0];
|
||||||
|
data.closing_date_precision = faker.helpers.arrayElement(['day', 'month', 'year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add material arrays for roller coasters
|
||||||
|
if (category === 'roller_coaster' && faker.datatype.boolean()) {
|
||||||
|
data.track_material = [faker.helpers.arrayElement(['steel', 'wood', 'hybrid'])];
|
||||||
|
data.support_material = [faker.helpers.arrayElement(['steel', 'wood', 'concrete'])];
|
||||||
|
data.propulsion_method = [faker.helpers.arrayElement(['chain_lift', 'cable_lift', 'launch', 'gravity'])];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add category-specific fields
|
||||||
|
if (category === 'water_ride' && faker.datatype.boolean()) {
|
||||||
|
data.water_depth_cm = faker.number.int({ min: 30, max: 300 });
|
||||||
|
data.splash_height_meters = faker.number.float({ min: 1, max: 20, fractionDigits: 1 });
|
||||||
|
data.wetness_level = faker.helpers.arrayElement(['dry', 'light', 'moderate', 'soaked']);
|
||||||
|
data.flume_type = faker.helpers.arrayElement(['log', 'tube', 'raft', 'boat']);
|
||||||
|
data.boat_capacity = faker.number.int({ min: 2, max: 20 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category === 'dark_ride' && faker.datatype.boolean()) {
|
||||||
|
data.theme_name = faker.lorem.words(2);
|
||||||
|
data.story_description = faker.lorem.sentence();
|
||||||
|
data.show_duration_seconds = faker.number.int({ min: 180, max: 600 });
|
||||||
|
data.animatronics_count = faker.number.int({ min: 5, max: 50 });
|
||||||
|
data.projection_type = faker.helpers.arrayElement(['2d', '3d', 'holographic', 'mixed']);
|
||||||
|
data.ride_system = faker.helpers.arrayElement(['omnimover', 'tracked', 'trackless', 'boat']);
|
||||||
|
data.scenes_count = faker.number.int({ min: 5, max: 20 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category === 'flat_ride' && faker.datatype.boolean()) {
|
||||||
|
data.rotation_type = faker.helpers.arrayElement(['horizontal', 'vertical', 'both', 'none']);
|
||||||
|
data.motion_pattern = faker.helpers.arrayElement(['circular', 'pendulum', 'spinning', 'wave']);
|
||||||
|
data.platform_count = faker.number.int({ min: 1, max: 8 });
|
||||||
|
data.swing_angle_degrees = faker.number.int({ min: 45, max: 180 });
|
||||||
|
data.rotation_speed_rpm = faker.number.int({ min: 5, max: 30 });
|
||||||
|
data.arm_length_meters = faker.number.float({ min: 5, max: 25, fractionDigits: 1 });
|
||||||
|
data.max_height_reached_meters = faker.number.float({ min: 10, max: 80, fractionDigits: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add optional fields
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.source_url = faker.internet.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.submission_notes = faker.lorem.sentence();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...data, ...overrides };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,17 +224,35 @@ export function generateCompanyData(
|
|||||||
overrides?: Partial<CompanyTestData>
|
overrides?: Partial<CompanyTestData>
|
||||||
): CompanyTestData {
|
): CompanyTestData {
|
||||||
const name = faker.company.name();
|
const name = faker.company.name();
|
||||||
|
const foundedDate = faker.date.past({ years: 100 }).toISOString().split('T')[0];
|
||||||
|
|
||||||
return {
|
const data: CompanyTestData = {
|
||||||
name,
|
name,
|
||||||
slug: faker.helpers.slugify(name).toLowerCase(),
|
slug: faker.helpers.slugify(name).toLowerCase(),
|
||||||
description: faker.lorem.paragraphs(2),
|
description: faker.lorem.paragraphs(2),
|
||||||
company_type: companyType,
|
company_type: companyType,
|
||||||
person_type: faker.helpers.arrayElement(['individual', 'company']),
|
person_type: faker.helpers.arrayElement(['individual', 'company']),
|
||||||
founded_date: faker.date.past({ years: 100 }).toISOString().split('T')[0],
|
founded_date: foundedDate,
|
||||||
|
founded_date_precision: faker.helpers.arrayElement(['day', 'month', 'year']),
|
||||||
is_test_data: true,
|
is_test_data: true,
|
||||||
...overrides,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add defunct date for some companies
|
||||||
|
if (faker.datatype.boolean(0.15)) {
|
||||||
|
data.defunct_date = faker.date.between({ from: foundedDate, to: new Date() }).toISOString().split('T')[0];
|
||||||
|
data.defunct_date_precision = faker.helpers.arrayElement(['day', 'month', 'year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add optional fields
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.source_url = faker.internet.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.submission_notes = faker.lorem.sentence();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...data, ...overrides };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,15 +264,25 @@ export function generateRideModelData(
|
|||||||
): RideModelTestData {
|
): RideModelTestData {
|
||||||
const name = faker.word.adjective() + ' Model';
|
const name = faker.word.adjective() + ' Model';
|
||||||
|
|
||||||
return {
|
const data: RideModelTestData = {
|
||||||
name,
|
name,
|
||||||
slug: faker.helpers.slugify(name).toLowerCase(),
|
slug: faker.helpers.slugify(name).toLowerCase(),
|
||||||
description: faker.lorem.paragraphs(2),
|
description: faker.lorem.paragraphs(2),
|
||||||
category: faker.helpers.arrayElement(['roller_coaster', 'flat_ride', 'water_ride']),
|
category: faker.helpers.arrayElement(['roller_coaster', 'flat_ride', 'water_ride']),
|
||||||
manufacturer_id: manufacturerId,
|
manufacturer_id: manufacturerId,
|
||||||
is_test_data: true,
|
is_test_data: true,
|
||||||
...overrides,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add optional fields
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.source_url = faker.internet.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
data.submission_notes = faker.lorem.sentence();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...data, ...overrides };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user