UNPKG

@joystick.js/db-canary

Version:

JoystickDB - A minimalist database server for the Joystick framework

198 lines (162 loc) • 6.92 kB
#!/usr/bin/env node /** * @fileoverview Full debug test runner that mimics the original test runner * but with enhanced exception tracking to identify problematic tests. */ import { spawn } from 'child_process'; // Track uncaught exceptions with detailed context const uncaught_exceptions = []; let current_test_phase = 'startup'; let test_start_time = null; // Enhanced exception handlers that log but don't exit process.on('uncaughtException', (error) => { const exception_info = { type: 'uncaughtException', phase: current_test_phase, message: error.message, stack: error.stack, timestamp: new Date().toISOString(), elapsed_ms: test_start_time ? Date.now() - test_start_time : 0 }; uncaught_exceptions.push(exception_info); console.error(`\nšŸ”„ UNCAUGHT EXCEPTION #${uncaught_exceptions.length}:`); console.error(`šŸ“ Phase: ${current_test_phase}`); console.error(`ā° Elapsed: ${exception_info.elapsed_ms}ms`); console.error(`šŸ’„ Error: ${error.message}`); console.error(`šŸ“š Stack (first 5 lines):`); const stack_lines = error.stack.split('\n').slice(0, 5); stack_lines.forEach(line => console.error(` ${line}`)); console.error(`ā° Time: ${exception_info.timestamp}\n`); }); process.on('unhandledRejection', (reason, promise) => { const exception_info = { type: 'unhandledRejection', phase: current_test_phase, reason: reason?.toString() || 'Unknown reason', stack: reason?.stack || 'No stack available', timestamp: new Date().toISOString(), elapsed_ms: test_start_time ? Date.now() - test_start_time : 0 }; uncaught_exceptions.push(exception_info); console.error(`\nšŸ”„ UNHANDLED REJECTION #${uncaught_exceptions.length}:`); console.error(`šŸ“ Phase: ${current_test_phase}`); console.error(`ā° Elapsed: ${exception_info.elapsed_ms}ms`); console.error(`šŸ’„ Reason: ${reason}`); console.error(`šŸ“š Stack (first 5 lines):`); const stack_lines = (reason?.stack || 'No stack available').split('\n').slice(0, 5); stack_lines.forEach(line => console.error(` ${line}`)); console.error(`ā° Time: ${exception_info.timestamp}\n`); }); /** * Runs the full test suite exactly like the original test runner. * @returns {Promise<number>} Exit code */ const run_full_test_suite_debug = () => { return new Promise((resolve) => { current_test_phase = 'full-test-suite'; test_start_time = Date.now(); console.log(`šŸ” Running FULL TEST SUITE with debug tracking`); console.log(`šŸ“ This mimics the exact command: npm test`); console.log(`šŸ’» Command: ./node_modules/.bin/ava --serial --verbose tests/client/**/*.test.js tests/server/**/*.test.js`); console.log(`šŸ”§ NODE_OPTIONS: --expose-gc --max-old-space-size=4096`); console.log(`ā° Started at: ${new Date().toISOString()}\n`); const command = './node_modules/.bin/ava'; const args = ['--serial', '--verbose', 'tests/client/**/*.test.js', 'tests/server/**/*.test.js']; const child = spawn(command, args, { stdio: 'pipe', // Capture output so we can track progress env: { ...process.env, NODE_ENV: 'test', NODE_OPTIONS: '--expose-gc --max-old-space-size=4096' } }); let output_buffer = ''; let test_count = 0; child.stdout.on('data', (data) => { const text = data.toString(); output_buffer += text; process.stdout.write(text); // Track test progress const test_matches = text.match(/āœ”/g); if (test_matches) { test_count += test_matches.length; current_test_phase = `test-${test_count}`; } }); child.stderr.on('data', (data) => { const text = data.toString(); output_buffer += text; process.stderr.write(text); }); child.on('close', (code) => { const elapsed = Date.now() - test_start_time; console.log(`\nāœ… Full test suite completed with exit code: ${code}`); console.log(`ā° Total elapsed: ${elapsed}ms`); console.log(`šŸ“Š Total tests detected: ${test_count}`); // Exception analysis console.log(`\nšŸ“Š UNCAUGHT EXCEPTION ANALYSIS:`); console.log(`Total exceptions detected: ${uncaught_exceptions.length}`); if (uncaught_exceptions.length > 0) { console.log('\nšŸ”„ Exception Timeline:'); uncaught_exceptions.forEach((exc, index) => { console.log(`\nException #${index + 1}:`); console.log(` Type: ${exc.type}`); console.log(` Phase: ${exc.phase}`); console.log(` Elapsed: ${exc.elapsed_ms}ms`); console.log(` Message: ${exc.message}`); console.log(` Time: ${exc.timestamp}`); if (exc.stack) { console.log(` Key Stack Lines:`); const stack_lines = exc.stack.split('\n') .filter(line => line.includes('db/src/') || line.includes('db/tests/')) .slice(0, 3); stack_lines.forEach(line => console.log(` ${line.trim()}`)); } }); // Try to correlate with test timing console.log('\nšŸ•’ Exception Timing Analysis:'); uncaught_exceptions.forEach((exc, index) => { const test_number_estimate = Math.floor((exc.elapsed_ms / elapsed) * test_count); console.log(` Exception #${index + 1} occurred around test #${test_number_estimate} (${exc.elapsed_ms}ms elapsed)`); }); } resolve(code); }); child.on('error', (error) => { console.error(`\nāŒ Full test suite failed: ${error.message}`); resolve(1); }); }); }; /** * Main execution function. */ const main = async () => { const args = process.argv.slice(2); if (args.includes('--help') || args.includes('-h')) { console.log(` šŸ” Full Debug Test Runner for JoystickDB Usage: node full_debug_test_runner.js This runner executes the complete test suite exactly like 'npm test' but captures uncaught exceptions and unhandled rejections with detailed timing and context information to identify problematic tests. `); process.exit(0); } console.log('šŸŽÆ Selected strategy: Full Test Suite Debug Analysis'); const exit_code = await run_full_test_suite_debug(); if (exit_code === 0 && uncaught_exceptions.length === 0) { console.log(`\nšŸŽ‰ All tests passed with NO uncaught exceptions!`); } else if (exit_code === 0 && uncaught_exceptions.length > 0) { console.log(`\nāš ļø All tests passed but ${uncaught_exceptions.length} uncaught exceptions detected`); } else { console.log(`\nšŸ’„ Tests failed with exit code: ${exit_code}`); } process.exit(exit_code); }; // Run the main function main().catch(error => { console.error(`\nšŸ’„ Debug runner error: ${error.message}`); console.error(error.stack); process.exit(1); });