mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 13:11:12 -05:00
411 lines
14 KiB
TypeScript
411 lines
14 KiB
TypeScript
/**
|
|
* Entity Submission & Validation Integration Tests
|
|
*
|
|
* Tests for submission validation, schema validation, and entity creation.
|
|
*/
|
|
|
|
import { supabase } from '@/lib/supabaseClient';
|
|
import type { TestSuite, TestResult } from '../testRunner';
|
|
import { TestDataTracker } from '../TestDataTracker';
|
|
|
|
export const submissionTestSuite: TestSuite = {
|
|
id: 'submission',
|
|
name: 'Entity Submission & Validation',
|
|
description: 'Tests for entity submission workflows and validation schemas',
|
|
tests: [
|
|
{
|
|
id: 'submission-001',
|
|
name: 'Park Creation Validation',
|
|
description: 'Validates park submission and creation',
|
|
run: async (): Promise<TestResult> => {
|
|
const startTime = Date.now();
|
|
const tracker = new TestDataTracker();
|
|
let parkId: string | null = null;
|
|
|
|
try {
|
|
const parkSlug = `test-park-submit-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
// Create park with valid data
|
|
const { data: park, error: createError } = await supabase
|
|
.from('parks')
|
|
.insert({
|
|
name: 'Test Park Submission',
|
|
slug: parkSlug,
|
|
park_type: 'theme_park',
|
|
status: 'operating',
|
|
description: 'Test park for submission validation'
|
|
})
|
|
.select('id, name, slug, park_type, status')
|
|
.single();
|
|
|
|
if (createError) throw new Error(`Park creation failed: ${createError.message}`);
|
|
if (!park) throw new Error('Park not returned after creation');
|
|
|
|
parkId = park.id;
|
|
|
|
// Validate created park has correct data
|
|
if (park.name !== 'Test Park Submission') {
|
|
throw new Error(`Expected name "Test Park Submission", got "${park.name}"`);
|
|
}
|
|
if (park.slug !== parkSlug) {
|
|
throw new Error(`Expected slug "${parkSlug}", got "${park.slug}"`);
|
|
}
|
|
if (park.park_type !== 'theme_park') {
|
|
throw new Error(`Expected park_type "theme_park", got "${park.park_type}"`);
|
|
}
|
|
|
|
// Test slug uniqueness constraint
|
|
const { error: duplicateError } = await supabase
|
|
.from('parks')
|
|
.insert({
|
|
name: 'Duplicate Slug Park',
|
|
slug: parkSlug, // Same slug
|
|
park_type: 'theme_park',
|
|
status: 'operating'
|
|
});
|
|
|
|
if (!duplicateError) {
|
|
throw new Error('Duplicate slug was allowed (uniqueness constraint failed)');
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
|
|
return {
|
|
id: 'submission-001',
|
|
name: 'Park Creation Validation',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'pass',
|
|
duration,
|
|
timestamp: new Date().toISOString(),
|
|
details: {
|
|
parkId,
|
|
parkSlug,
|
|
validationsPassed: ['name', 'slug', 'park_type', 'uniqueness_constraint']
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
id: 'submission-001',
|
|
name: 'Park Creation Validation',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'fail',
|
|
duration: Date.now() - startTime,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
} finally {
|
|
await tracker.cleanup();
|
|
const remaining = await tracker.verifyCleanup();
|
|
if (remaining.length > 0) {
|
|
console.warn('submission-001 cleanup incomplete:', remaining);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
id: 'submission-002',
|
|
name: 'Ride Creation with Dependencies',
|
|
description: 'Validates ride submission requires valid park_id',
|
|
run: async (): Promise<TestResult> => {
|
|
const startTime = Date.now();
|
|
const tracker = new TestDataTracker();
|
|
let parkId: string | null = null;
|
|
let rideId: string | null = null;
|
|
|
|
try {
|
|
// First create a park
|
|
const parkSlug = `test-park-ride-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const { data: park, error: parkError } = await supabase
|
|
.from('parks')
|
|
.insert({
|
|
name: 'Test Park for Ride',
|
|
slug: parkSlug,
|
|
park_type: 'theme_park',
|
|
status: 'operating',
|
|
is_test_data: true
|
|
})
|
|
.select('id')
|
|
.single();
|
|
|
|
if (parkError) throw new Error(`Park creation failed: ${parkError.message}`);
|
|
parkId = park.id;
|
|
|
|
// Try to create ride with invalid park_id (should fail)
|
|
const invalidParkId = '00000000-0000-0000-0000-000000000000';
|
|
const { error: invalidError } = await supabase
|
|
.from('rides')
|
|
.insert({
|
|
name: 'Test Ride Invalid Park',
|
|
slug: `test-ride-invalid-${Date.now()}`,
|
|
park_id: invalidParkId,
|
|
category: 'roller_coaster',
|
|
status: 'operating'
|
|
});
|
|
|
|
if (!invalidError) {
|
|
throw new Error('Ride with invalid park_id was allowed (foreign key constraint failed)');
|
|
}
|
|
|
|
// Create ride with valid park_id (should succeed)
|
|
const rideSlug = `test-ride-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const { data: ride, error: rideError } = await supabase
|
|
.from('rides')
|
|
.insert({
|
|
name: 'Test Ride Valid Park',
|
|
slug: rideSlug,
|
|
park_id: parkId,
|
|
category: 'roller_coaster',
|
|
status: 'operating'
|
|
})
|
|
.select('id, name, park_id')
|
|
.single();
|
|
|
|
if (rideError) throw new Error(`Ride creation failed: ${rideError.message}`);
|
|
if (!ride) throw new Error('Ride not returned after creation');
|
|
|
|
rideId = ride.id;
|
|
|
|
if (ride.park_id !== parkId) {
|
|
throw new Error(`Expected park_id "${parkId}", got "${ride.park_id}"`);
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
|
|
return {
|
|
id: 'submission-002',
|
|
name: 'Ride Creation with Dependencies',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'pass',
|
|
duration,
|
|
timestamp: new Date().toISOString(),
|
|
details: {
|
|
parkId,
|
|
rideId,
|
|
validationsPassed: ['foreign_key_constraint', 'valid_dependency']
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
id: 'submission-002',
|
|
name: 'Ride Creation with Dependencies',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'fail',
|
|
duration: Date.now() - startTime,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
} finally {
|
|
await tracker.cleanup();
|
|
const remaining = await tracker.verifyCleanup();
|
|
if (remaining.length > 0) {
|
|
console.warn('submission-002 cleanup incomplete:', remaining);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
id: 'submission-003',
|
|
name: 'Company Creation All Types',
|
|
description: 'Validates company creation for all company types',
|
|
run: async (): Promise<TestResult> => {
|
|
const startTime = Date.now();
|
|
const tracker = new TestDataTracker();
|
|
const companyIds: string[] = [];
|
|
|
|
try {
|
|
const companyTypes = ['manufacturer', 'operator', 'designer', 'property_owner'] as const;
|
|
|
|
for (const companyType of companyTypes) {
|
|
const slug = `test-company-${companyType}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
const { data: company, error: createError } = await supabase
|
|
.from('companies')
|
|
.insert({
|
|
name: `Test ${companyType} Company`,
|
|
slug,
|
|
company_type: companyType,
|
|
description: `Test company of type ${companyType}`
|
|
})
|
|
.select('id, company_type')
|
|
.single();
|
|
|
|
if (createError) {
|
|
throw new Error(`${companyType} creation failed: ${createError.message}`);
|
|
}
|
|
if (!company) {
|
|
throw new Error(`${companyType} not returned after creation`);
|
|
}
|
|
|
|
companyIds.push(company.id);
|
|
tracker.track('companies', company.id);
|
|
|
|
if (company.company_type !== companyType) {
|
|
throw new Error(`Expected company_type "${companyType}", got "${company.company_type}"`);
|
|
}
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
|
|
return {
|
|
id: 'submission-003',
|
|
name: 'Company Creation All Types',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'pass',
|
|
duration,
|
|
timestamp: new Date().toISOString(),
|
|
details: {
|
|
companiesCreated: companyIds.length,
|
|
companyTypes: companyTypes,
|
|
companyIds
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
id: 'submission-003',
|
|
name: 'Company Creation All Types',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'fail',
|
|
duration: Date.now() - startTime,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
} finally {
|
|
await tracker.cleanup();
|
|
const remaining = await tracker.verifyCleanup();
|
|
if (remaining.length > 0) {
|
|
console.warn('submission-003 cleanup incomplete:', remaining);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
id: 'submission-004',
|
|
name: 'Ride Model with Images',
|
|
description: 'Validates ride model creation with image fields',
|
|
run: async (): Promise<TestResult> => {
|
|
const startTime = Date.now();
|
|
let manufacturerId: string | null = null;
|
|
let modelId: string | null = null;
|
|
|
|
try {
|
|
// Create manufacturer first
|
|
const mfgSlug = `test-mfg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const { data: manufacturer, error: mfgError } = await supabase
|
|
.from('companies')
|
|
.insert({
|
|
name: 'Test Manufacturer',
|
|
slug: mfgSlug,
|
|
company_type: 'manufacturer'
|
|
})
|
|
.select('id')
|
|
.single();
|
|
|
|
if (mfgError) throw new Error(`Manufacturer creation failed: ${mfgError.message}`);
|
|
manufacturerId = manufacturer.id;
|
|
|
|
// Create ride model with images
|
|
const modelSlug = `test-model-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const testImageUrl = 'https://imagedelivery.net/test-account/test-image-id/public';
|
|
const testImageId = 'test-image-id';
|
|
|
|
const { data: model, error: modelError } = await supabase
|
|
.from('ride_models')
|
|
.insert({
|
|
name: 'Test Ride Model',
|
|
slug: modelSlug,
|
|
manufacturer_id: manufacturerId,
|
|
category: 'roller_coaster',
|
|
ride_type: 'steel_coaster',
|
|
banner_image_url: testImageUrl,
|
|
banner_image_id: testImageId,
|
|
card_image_url: testImageUrl,
|
|
card_image_id: testImageId
|
|
})
|
|
.select('id, banner_image_url, banner_image_id, card_image_url, card_image_id')
|
|
.single();
|
|
|
|
if (modelError) throw new Error(`Ride model creation failed: ${modelError.message}`);
|
|
if (!model) throw new Error('Ride model not returned after creation');
|
|
|
|
modelId = model.id;
|
|
|
|
// Validate image fields
|
|
if (model.banner_image_url !== testImageUrl) {
|
|
throw new Error(`banner_image_url mismatch: expected "${testImageUrl}", got "${model.banner_image_url}"`);
|
|
}
|
|
if (model.banner_image_id !== testImageId) {
|
|
throw new Error(`banner_image_id mismatch: expected "${testImageId}", got "${model.banner_image_id}"`);
|
|
}
|
|
if (model.card_image_url !== testImageUrl) {
|
|
throw new Error(`card_image_url mismatch`);
|
|
}
|
|
if (model.card_image_id !== testImageId) {
|
|
throw new Error(`card_image_id mismatch`);
|
|
}
|
|
|
|
// Verify version was created with images
|
|
let version: any = null;
|
|
const pollStart = Date.now();
|
|
while (!version && Date.now() - pollStart < 5000) {
|
|
const { data } = await supabase
|
|
.from('ride_model_versions')
|
|
.select('banner_image_url, banner_image_id, card_image_url, card_image_id')
|
|
.eq('ride_model_id', modelId)
|
|
.eq('version_number', 1)
|
|
.single();
|
|
|
|
if (data) {
|
|
version = data;
|
|
break;
|
|
}
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
|
|
if (!version) throw new Error('Version not created after 5s timeout');
|
|
if (version.banner_image_url !== testImageUrl) {
|
|
throw new Error('Version missing banner_image_url');
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
|
|
return {
|
|
id: 'submission-004',
|
|
name: 'Ride Model with Images',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'pass',
|
|
duration,
|
|
timestamp: new Date().toISOString(),
|
|
details: {
|
|
modelId,
|
|
manufacturerId,
|
|
imageFieldsValidated: ['banner_image_url', 'banner_image_id', 'card_image_url', 'card_image_id'],
|
|
versionCreated: true
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
id: 'submission-004',
|
|
name: 'Ride Model with Images',
|
|
suite: 'Entity Submission & Validation',
|
|
status: 'fail',
|
|
duration: Date.now() - startTime,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
} finally {
|
|
if (modelId) {
|
|
await supabase.from('ride_models').delete().eq('id', modelId);
|
|
}
|
|
if (manufacturerId) {
|
|
await supabase.from('companies').delete().eq('id', manufacturerId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|