diff --git a/src/lib/integrationTests/formatTestError.ts b/src/lib/integrationTests/formatTestError.ts new file mode 100644 index 00000000..5b976edd --- /dev/null +++ b/src/lib/integrationTests/formatTestError.ts @@ -0,0 +1,59 @@ +/** + * Test Error Formatting Utility + * + * Provides robust error formatting for test results to avoid "[object Object]" messages + */ + +/** + * Format error for test result display + * Handles Error objects, PostgresError objects, and plain objects + * + * @param error - Any error value thrown in a test + * @returns Formatted, human-readable error string + */ +export function formatTestError(error: unknown): string { + // Standard Error objects + if (error instanceof Error) { + return error.message; + } + + // Object-like errors (PostgresError, custom error objects, etc.) + if (typeof error === 'object' && error !== null) { + const err = error as any; + + // Try common error message properties + if (err.message && typeof err.message === 'string') { + return err.message; + } + + // Some errors nest the actual error in an 'error' property + if (err.error) { + return formatTestError(err.error); + } + + // Some APIs use 'msg' instead of 'message' + if (err.msg && typeof err.msg === 'string') { + return err.msg; + } + + // Supabase errors might have details + if (err.details && typeof err.details === 'string') { + return `${err.message || 'Error'}: ${err.details}`; + } + + // Last resort: stringify the entire object + try { + const stringified = JSON.stringify(error, null, 2); + // If it's too long, truncate it + return stringified.length > 500 + ? stringified.substring(0, 500) + '... (truncated)' + : stringified; + } catch { + // JSON.stringify can fail on circular references + return String(error); + } + } + + // Primitive values (strings, numbers, etc.) + return String(error); +} diff --git a/src/lib/integrationTests/helpers/approvalTestHelpers.ts b/src/lib/integrationTests/helpers/approvalTestHelpers.ts index 23c2352c..36bf6916 100644 --- a/src/lib/integrationTests/helpers/approvalTestHelpers.ts +++ b/src/lib/integrationTests/helpers/approvalTestHelpers.ts @@ -8,7 +8,8 @@ import { supabase } from '@/lib/supabaseClient'; import { TestDataTracker } from '../TestDataTracker'; -import { +import { formatTestError } from '../formatTestError'; +import { submitParkCreation, submitRideCreation, submitManufacturerCreation, @@ -18,6 +19,9 @@ import { submitRideModelCreation } from '@/lib/entitySubmissionHelpers'; +// Re-export formatTestError for use in test suites +export { formatTestError } from '../formatTestError'; + // ============================================ // AUTHENTICATION // ============================================ @@ -416,7 +420,7 @@ export async function approveSubmission( const duration = performance.now() - startTime; return { success: false, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), duration, }; } diff --git a/src/lib/integrationTests/index.ts b/src/lib/integrationTests/index.ts index 738c70a1..33e23a9e 100644 --- a/src/lib/integrationTests/index.ts +++ b/src/lib/integrationTests/index.ts @@ -7,5 +7,6 @@ export { IntegrationTestRunner } from './testRunner'; export { allTestSuites } from './suites'; export { formatResultsAsMarkdown, formatSingleTestAsMarkdown } from './formatters'; +export { formatTestError } from './formatTestError'; export type { TestResult, Test, TestSuite } from './testRunner'; diff --git a/src/lib/integrationTests/suites/approvalPipelineTests.ts b/src/lib/integrationTests/suites/approvalPipelineTests.ts index 2360c766..49c5b695 100644 --- a/src/lib/integrationTests/suites/approvalPipelineTests.ts +++ b/src/lib/integrationTests/suites/approvalPipelineTests.ts @@ -36,6 +36,7 @@ import { verifySubmissionStatus, createParkDirectly, createRideDirectly, + formatTestError, } from '../helpers/approvalTestHelpers'; // ============================================ @@ -132,7 +133,7 @@ const parkCreationTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -220,7 +221,7 @@ const rideCreationTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -298,7 +299,7 @@ const companyCreationTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -387,7 +388,7 @@ const rideModelCreationTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -497,7 +498,7 @@ const rideManufacturerCompositeTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -624,7 +625,7 @@ const partialApprovalTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -714,7 +715,7 @@ const idempotencyTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -809,7 +810,7 @@ const parkUpdateTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -897,7 +898,7 @@ const rideUpdateTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -999,7 +1000,7 @@ const rideManufacturerDesignerCompositeTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1097,7 +1098,7 @@ const parkOperatorOwnerCompositeTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1188,7 +1189,7 @@ const lockConflictTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1280,7 +1281,7 @@ const bannedUserTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1373,7 +1374,7 @@ const multipleEditChainTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1478,7 +1479,7 @@ const versionSnapshotIntegrityTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1557,7 +1558,7 @@ const parkPhotoGalleryTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1639,7 +1640,7 @@ const ridePhotoGalleryTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1753,7 +1754,7 @@ const invalidTempRefTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; @@ -1852,7 +1853,7 @@ const concurrentEditTest: Test = { suite: 'Approval Pipeline', status: 'fail', duration: Date.now() - startTime, - error: error instanceof Error ? error.message : String(error), + error: formatTestError(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }; diff --git a/src/lib/integrationTests/suites/performanceTests.ts b/src/lib/integrationTests/suites/performanceTests.ts index cba021b6..c43dc0aa 100644 --- a/src/lib/integrationTests/suites/performanceTests.ts +++ b/src/lib/integrationTests/suites/performanceTests.ts @@ -126,7 +126,7 @@ export const performanceTestSuite: TestSuite = { .select('id') .single(); - if (parkError) throw parkError; + if (parkError) throw new Error(`Park creation failed: ${parkError.message}`); parkId = park.id; tracker.track('parks', parkId); @@ -214,7 +214,7 @@ export const performanceTestSuite: TestSuite = { const modDuration = Date.now() - modStart; - if (modError) throw modError; + if (modError) throw new Error(`Moderator check failed: ${modError.message}`); // Test is_user_banned function performance const banStart = Date.now(); @@ -225,7 +225,7 @@ export const performanceTestSuite: TestSuite = { const banDuration = Date.now() - banStart; - if (banError) throw banError; + if (banError) throw new Error(`Ban check failed: ${banError.message}`); // Performance threshold: 200ms for simple functions const threshold = 200; diff --git a/src/lib/integrationTests/suites/unitConversionTests.ts b/src/lib/integrationTests/suites/unitConversionTests.ts index ca4a49f1..63fe8ee0 100644 --- a/src/lib/integrationTests/suites/unitConversionTests.ts +++ b/src/lib/integrationTests/suites/unitConversionTests.ts @@ -38,7 +38,7 @@ export const unitConversionTestSuite: TestSuite = { .select('id') .single(); - if (parkError) throw parkError; + if (parkError) throw new Error(`Park creation failed: ${parkError.message}`); parkId = park.id; tracker.track('parks', parkId); @@ -145,7 +145,7 @@ export const unitConversionTestSuite: TestSuite = { .select('id') .single(); - if (parkError) throw parkError; + if (parkError) throw new Error(`Park creation failed: ${parkError.message}`); parkId = park.id; tracker.track('parks', parkId); @@ -167,7 +167,7 @@ export const unitConversionTestSuite: TestSuite = { .select('id') .single(); - if (rideError) throw rideError; + if (rideError) throw new Error(`Ride creation failed: ${rideError.message}`); rideId = ride.id; tracker.track('rides', rideId);