#!/usr/bin/env node /** * Test script to verify signer reconnection behavior * * Usage: * node test-signer-reconnection.js * * This script will: * 1. Connect to your API * 2. Monitor signer health * 3. Simulate operations during connection issues * 4. Verify reconnection behavior */ const fetch = require('node-fetch'); const API_BASE = process.env.API_BASE || 'http://localhost:8080'; const TEST_DURATION = 60000; // 1 minute test const CHECK_INTERVAL = 2000; // Check every 2 seconds async function checkSignerHealth() { try { const response = await fetch(`${API_BASE}/api/v1/process/health/signer`, { timeout: 5000 }); if (!response.ok) { return { error: `HTTP ${response.status}` }; } const data = await response.json(); return data.data?.signer || { error: 'Invalid response format' }; } catch (error) { return { error: error.message }; } } async function forceReconnect() { try { const response = await fetch(`${API_BASE}/api/v1/process/admin/signer/reconnect`, { method: 'POST', timeout: 5000 }); const data = await response.json(); return data; } catch (error) { return { error: error.message }; } } function formatTimestamp() { return new Date().toISOString().substr(11, 8); // HH:MM:SS } function formatDuration(ms) { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms/1000).toFixed(1)}s`; return `${(ms/60000).toFixed(1)}m`; } async function runTest() { console.log(`๐Ÿงช Starting Signer Reconnection Test`); console.log(`๐Ÿ“ API Base: ${API_BASE}`); console.log(`โฑ๏ธ Test Duration: ${formatDuration(TEST_DURATION)}`); console.log(`๐Ÿ”„ Check Interval: ${formatDuration(CHECK_INTERVAL)}`); console.log(`\n${'='.repeat(80)}\n`); const startTime = Date.now(); let lastState = null; let stateChanges = []; let totalChecks = 0; let healthyChecks = 0; let errors = []; console.log(`Time | State | Attempts | Last Connected | Details`); console.log(`${'-'.repeat(80)}`); const testInterval = setInterval(async () => { const checkTime = Date.now(); const health = await checkSignerHealth(); totalChecks++; const timestamp = formatTimestamp(); if (health.error) { errors.push({ time: checkTime, error: health.error }); console.log(`${timestamp} | โŒ ERROR | - | - | ${health.error}`); } else { healthyChecks++; const state = health.state || 'unknown'; const attempts = health.reconnectAttempts || 0; const lastConnected = health.lastConnected ? formatDuration(checkTime - health.lastConnected) + ' ago' : 'never'; const lastError = health.lastError ? ` (${health.lastError})` : ''; // Track state changes if (lastState !== state) { stateChanges.push({ time: checkTime, from: lastState, to: state, duration: lastState ? checkTime - stateChanges[stateChanges.length - 1]?.time : 0 }); lastState = state; } const stateIcon = { 'connected': 'โœ…', 'connecting': '๐Ÿ”„', 'reconnecting': '๐Ÿ”„', 'disconnected': '๐Ÿ”Œ', 'failed': 'โŒ' }[state] || 'โ“'; console.log(`${timestamp} | ${stateIcon} ${state.padEnd(10)} | ${attempts.toString().padStart(8)} | ${lastConnected.padEnd(14)} | ${lastError}`); } // Check if test duration is over if (Date.now() - startTime >= TEST_DURATION) { clearInterval(testInterval); await printTestSummary(startTime, totalChecks, healthyChecks, errors, stateChanges); } }, CHECK_INTERVAL); // Handle Ctrl+C gracefully process.on('SIGINT', async () => { console.log('\n\nโน๏ธ Test interrupted by user'); clearInterval(testInterval); await printTestSummary(startTime, totalChecks, healthyChecks, errors, stateChanges); process.exit(0); }); // Optional: Test force reconnection after 30 seconds setTimeout(async () => { console.log(`\n${formatTimestamp()} | ๐Ÿ”ง TESTING | - | - | Forcing reconnection...`); const result = await forceReconnect(); if (result.error) { console.log(`${formatTimestamp()} | โŒ ERROR | - | - | Force reconnect failed: ${result.error}`); } else { console.log(`${formatTimestamp()} | ๐Ÿ”ง TESTING | - | - | Force reconnection initiated`); } }, 30000); } async function printTestSummary(startTime, totalChecks, healthyChecks, errors, stateChanges) { const duration = Date.now() - startTime; const successRate = totalChecks > 0 ? (healthyChecks / totalChecks * 100).toFixed(1) : 0; console.log(`\n${'='.repeat(80)}`); console.log(`๐Ÿ“Š Test Summary`); console.log(`${'='.repeat(80)}`); console.log(`โฑ๏ธ Total Duration: ${formatDuration(duration)}`); console.log(`๐Ÿ“ˆ Health Checks: ${healthyChecks}/${totalChecks} (${successRate}% success)`); console.log(`๐Ÿ”„ State Changes: ${stateChanges.length}`); console.log(`โŒ Errors: ${errors.length}`); if (stateChanges.length > 0) { console.log(`\n๐Ÿ“‹ State Change Timeline:`); stateChanges.forEach((change, i) => { const time = formatTimestamp(new Date(change.time)); const duration = change.duration ? ` (${formatDuration(change.duration)})` : ''; console.log(` ${time} | ${change.from || 'initial'} โ†’ ${change.to}${duration}`); }); } if (errors.length > 0) { console.log(`\nโŒ Error Summary:`); const errorCounts = {}; errors.forEach(error => { errorCounts[error.error] = (errorCounts[error.error] || 0) + 1; }); Object.entries(errorCounts).forEach(([error, count]) => { console.log(` ${count}x: ${error}`); }); } // Recommendations console.log(`\n๐Ÿ’ก Recommendations:`); if (successRate < 95) { console.log(` โš ๏ธ Low success rate (${successRate}%) - check signer service health`); } if (stateChanges.filter(c => c.to === 'connected').length === 0) { console.log(` โš ๏ธ Never achieved connected state - check configuration`); } if (errors.length > totalChecks * 0.1) { console.log(` โš ๏ธ High error rate - check network connectivity`); } if (stateChanges.length > 10) { console.log(` โš ๏ธ Frequent state changes - possible connection instability`); } if (successRate >= 95 && stateChanges.some(c => c.to === 'connected')) { console.log(` โœ… Connection resilience looks good!`); } console.log(`\n๐Ÿ”— Manual Commands:`); console.log(` Health Check: curl ${API_BASE}/api/v1/process/health/signer`); console.log(` Force Reconnect: curl -X POST ${API_BASE}/api/v1/process/admin/signer/reconnect`); } function formatTimestamp(date = new Date()) { return date.toISOString().substr(11, 8); } // Run the test runTest().catch(error => { console.error('โŒ Test failed:', error.message); process.exit(1); });