import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../ts/plugins.js'; import * as paths from '../ts/paths.js'; import * as fs from 'fs/promises'; import { execSync } from 'child_process'; // Test process that will crash const CRASH_SCRIPT = ` setInterval(() => { console.log('[test] Process is running...'); }, 1000); setTimeout(() => { console.error('[test] About to crash with non-zero exit code!'); process.exit(42); }, 3000); `; tap.test('manual crash log test via CLI', async (tools) => { const crashScriptPath = plugins.path.join(paths.tspmDir, 'test-crash-script.js'); const crashLogsDir = plugins.path.join(paths.tspmDir, 'crashlogs'); // Clean up any existing crash logs try { await fs.rm(crashLogsDir, { recursive: true, force: true }); } catch {} // Write the crash script await fs.writeFile(crashScriptPath, CRASH_SCRIPT); // Stop any existing daemon try { execSync('tsx ts/cli.ts daemon stop', { stdio: 'pipe' }); } catch {} await tools.delayFor(1000); // Start the daemon console.log('Starting daemon...'); try { execSync('tsx ts/cli.ts daemon start', { stdio: 'pipe' }); } catch {} await tools.delayFor(2000); // Add a process that will crash console.log('Adding crash test process...'); let addOutput: string; try { addOutput = execSync(`tsx ts/cli.ts add "node ${crashScriptPath}" --name crash-test`, { encoding: 'utf-8', stdio: 'pipe' }); } catch (e: any) { addOutput = e.stdout || ''; } console.log(addOutput); // Extract process ID from output const idMatch = addOutput.match(/Assigned ID: (\d+)/i) || addOutput.match(/id[:\s]+(\d+)/i); if (!idMatch) { console.log('Could not extract process ID, skipping rest of test'); // Clean up try { execSync('tsx ts/cli.ts daemon stop', { stdio: 'pipe' }); } catch {} await fs.unlink(crashScriptPath).catch(() => {}); return; } const processId = parseInt(idMatch[1]); console.log(`Process ID: ${processId}`); // Start the process console.log('Starting process that will crash...'); try { execSync(`tsx ts/cli.ts start ${processId}`, { stdio: 'pipe' }); } catch {} // Wait for the process to crash (it crashes after 3 seconds) console.log('Waiting for process to crash...'); await tools.delayFor(5000); // Check if crash log was created console.log('Checking for crash log...'); const crashLogFiles = await fs.readdir(crashLogsDir).catch(() => []); console.log(`Found ${crashLogFiles.length} crash log files:`); crashLogFiles.forEach(file => console.log(` - ${file}`)); expect(crashLogFiles.length).toBeGreaterThan(0); // Find and verify crash log const testCrashLog = crashLogFiles.find(file => file.includes('crash-test')); if (testCrashLog) { const crashLogPath = plugins.path.join(crashLogsDir, testCrashLog); const crashLogContent = await fs.readFile(crashLogPath, 'utf-8'); console.log('\nCrash log content:'); console.log(crashLogContent); expect(crashLogContent).toInclude('CRASH REPORT'); expect(crashLogContent).toInclude('Exit Code'); } // Clean up console.log('Cleaning up...'); try { execSync(`tsx ts/cli.ts delete ${processId}`, { stdio: 'pipe' }); } catch {} try { execSync('tsx ts/cli.ts daemon stop', { stdio: 'pipe' }); } catch {} await fs.unlink(crashScriptPath).catch(() => {}); }); export default tap.start();