lecoffre-back-mini/test-signer-reconnection.js
2025-09-07 21:10:39 +02:00

214 lines
6.9 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);
});