interacmanagernew / scripts /test-websocket.ts
MichaelEdou
Initial commit — ICC Interac Manager full-stack app
149698e
#!/usr/bin/env node
// scripts/test-websocket.ts
//
// Tests Socket.io WebSocket connection and event handling
// Run: npx tsx scripts/test-websocket.ts
// Requires server running.
import 'dotenv/config';
import { io, type Socket } from 'socket.io-client';
const PORT = process.env.PORT || '3001';
const BASE_URL = `http://localhost:${PORT}`;
const PASS = '\x1b[32m✅\x1b[0m';
const FAIL = '\x1b[31m❌\x1b[0m';
let passed = 0;
let failed = 0;
function pass(msg: string) { console.log(` ${PASS} ${msg}`); passed++; }
function fail(msg: string) { console.log(` ${FAIL} ${msg}`); failed++; }
function connectSocket(timeout = 5000): Promise<Socket> {
return new Promise((resolve, reject) => {
const socket = io(BASE_URL, {
path: '/ws/socket.io',
transports: ['websocket', 'polling'],
timeout,
reconnection: false,
});
const timer = setTimeout(() => {
socket.disconnect();
reject(new Error('Connection timeout'));
}, timeout);
socket.on('connect', () => {
clearTimeout(timer);
resolve(socket);
});
socket.on('connect_error', (err) => {
clearTimeout(timer);
reject(new Error(`Connection failed: ${err.message}`));
});
});
}
async function main() {
console.log('\n\x1b[1m🔌 ICC WebSocket (Socket.io) Tests\x1b[0m');
console.log(` Server: ${BASE_URL}`);
console.log('═'.repeat(50));
// Check server is running first
try {
await fetch(`${BASE_URL}/api/health`, { signal: AbortSignal.timeout(2000) });
} catch {
fail('Server not running');
console.log(' → Start with: cd packages/server && npx tsx src/index.ts');
process.exit(1);
}
// Test 1: Basic connection
console.log('\n\x1b[1mConnection\x1b[0m');
let mainSocket: Socket | null = null;
try {
mainSocket = await connectSocket(5000);
pass(`Socket.io connects successfully (id: ${mainSocket.id})`);
} catch (error: any) {
fail(`Socket.io connection failed: ${error.message}`);
// Try polling-only as fallback info
console.log(' Trying HTTP polling endpoint...');
try {
const res = await fetch(`${BASE_URL}/ws/socket.io/?EIO=4&transport=polling`, { signal: AbortSignal.timeout(3000) });
if (res.ok) {
pass('Socket.io HTTP polling endpoint responds');
} else {
fail(`Socket.io polling returned ${res.status}`);
}
} catch (e: any) {
fail(`Socket.io polling failed: ${e.message}`);
}
printSummary();
return;
}
// Test 2: Multiple concurrent connections (stress test)
console.log('\n\x1b[1mStress Test\x1b[0m');
const clients: Socket[] = [];
let connectFails = 0;
for (let i = 0; i < 5; i++) {
try {
const client = await connectSocket(3000);
clients.push(client);
} catch {
connectFails++;
}
}
if (connectFails === 0) {
pass('5 concurrent connections handled');
} else {
fail(`${connectFails}/5 connections failed`);
}
// Clean up
clients.forEach((c) => c.disconnect());
// Test 3: Graceful disconnect
console.log('\n\x1b[1mDisconnect\x1b[0m');
mainSocket.disconnect();
await new Promise((r) => setTimeout(r, 500));
if (!mainSocket.connected) {
pass('Clean disconnect');
} else {
fail('Socket still connected after disconnect');
}
// Test 4: Reconnect after disconnect
try {
const ws2 = await connectSocket(5000);
pass('Reconnection after disconnect works');
ws2.disconnect();
} catch (e: any) {
fail(`Reconnection failed: ${e.message}`);
}
printSummary();
}
function printSummary() {
console.log('\n' + '═'.repeat(50));
console.log(`\x1b[1m📋 Results: ${passed} passed, ${failed} failed\x1b[0m`);
console.log('═'.repeat(50) + '\n');
process.exit(failed > 0 ? 1 : 0);
}
main().catch(console.error);