- Introduced two new test scripts: `test-relay-connection.js` and `test-websocket-close.js` to verify WebSocket connection stability and closure behavior. - `test-relay-connection.js` tests multiple connections, monitors their open/close events, and analyzes premature closures. - `test-websocket-close.js` focuses on connection closure issues with concurrent connections and logs results for connected, closed, and error states. - Both scripts utilize the NostrWebSocket from the @nostr-dev-kit/ndk package for testing purposes.
168 lines
5.0 KiB
JavaScript
Executable File
168 lines
5.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
// Test script to verify websocket connections are not closed prematurely
|
|
// This is a Node.js test script that can be run with: node test-relay-connection.js
|
|
|
|
import { NostrWebSocket } from '@nostr-dev-kit/ndk';
|
|
|
|
const RELAY = process.env.RELAY || 'ws://localhost:8080';
|
|
const MAX_CONNECTIONS = 10;
|
|
const TEST_DURATION = 30000; // 30 seconds
|
|
|
|
let connectionsClosed = 0;
|
|
let connectionsOpened = 0;
|
|
let messagesReceived = 0;
|
|
let errors = 0;
|
|
|
|
const stats = {
|
|
premature: 0,
|
|
normal: 0,
|
|
errors: 0,
|
|
};
|
|
|
|
class TestConnection {
|
|
constructor(id) {
|
|
this.id = id;
|
|
this.ws = null;
|
|
this.closed = false;
|
|
this.openTime = null;
|
|
this.closeTime = null;
|
|
this.lastError = null;
|
|
}
|
|
|
|
connect() {
|
|
return new Promise((resolve, reject) => {
|
|
this.ws = new NostrWebSocket(RELAY);
|
|
|
|
this.ws.addEventListener('open', () => {
|
|
this.openTime = Date.now();
|
|
connectionsOpened++;
|
|
console.log(`[Connection ${this.id}] Opened`);
|
|
resolve();
|
|
});
|
|
|
|
this.ws.addEventListener('close', (event) => {
|
|
this.closeTime = Date.now();
|
|
this.closed = true;
|
|
connectionsClosed++;
|
|
const duration = this.closeTime - this.openTime;
|
|
console.log(`[Connection ${this.id}] Closed: code=${event.code}, reason="${event.reason || ''}", duration=${duration}ms`);
|
|
|
|
if (duration < 5000 && event.code !== 1000) {
|
|
stats.premature++;
|
|
console.log(`[Connection ${this.id}] PREMATURE CLOSE DETECTED: duration=${duration}ms < 5s`);
|
|
} else {
|
|
stats.normal++;
|
|
}
|
|
});
|
|
|
|
this.ws.addEventListener('error', (error) => {
|
|
this.lastError = error;
|
|
stats.errors++;
|
|
console.error(`[Connection ${this.id}] Error:`, error);
|
|
});
|
|
|
|
this.ws.addEventListener('message', (event) => {
|
|
messagesReceived++;
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
console.log(`[Connection ${this.id}] Message:`, data[0]);
|
|
} catch (e) {
|
|
console.log(`[Connection ${this.id}] Message (non-JSON):`, event.data);
|
|
}
|
|
});
|
|
|
|
setTimeout(reject, 5000); // Timeout after 5 seconds if not opened
|
|
});
|
|
}
|
|
|
|
sendReq() {
|
|
if (this.ws && !this.closed) {
|
|
this.ws.send(JSON.stringify(['REQ', `test-sub-${this.id}`, { kinds: [1], limit: 10 }]));
|
|
console.log(`[Connection ${this.id}] Sent REQ`);
|
|
}
|
|
}
|
|
|
|
close() {
|
|
if (this.ws && !this.closed) {
|
|
this.ws.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
async function runTest() {
|
|
console.log('='.repeat(60));
|
|
console.log('Testing Relay Connection Stability');
|
|
console.log('='.repeat(60));
|
|
console.log(`Relay: ${RELAY}`);
|
|
console.log(`Duration: ${TEST_DURATION}ms`);
|
|
console.log(`Connections: ${MAX_CONNECTIONS}`);
|
|
console.log('='.repeat(60));
|
|
console.log();
|
|
|
|
const connections = [];
|
|
|
|
// Open connections
|
|
console.log('Opening connections...');
|
|
for (let i = 0; i < MAX_CONNECTIONS; i++) {
|
|
const conn = new TestConnection(i);
|
|
try {
|
|
await conn.connect();
|
|
connections.push(conn);
|
|
} catch (error) {
|
|
console.error(`Failed to open connection ${i}:`, error);
|
|
}
|
|
}
|
|
|
|
console.log(`Opened ${connections.length} connections`);
|
|
console.log();
|
|
|
|
// Send requests from each connection
|
|
console.log('Sending REQ messages...');
|
|
for (const conn of connections) {
|
|
conn.sendReq();
|
|
}
|
|
|
|
// Wait and let connections run
|
|
console.log(`Waiting ${TEST_DURATION / 1000}s...`);
|
|
await new Promise(resolve => setTimeout(resolve, TEST_DURATION));
|
|
|
|
// Close all connections
|
|
console.log('Closing all connections...');
|
|
for (const conn of connections) {
|
|
conn.close();
|
|
}
|
|
|
|
// Wait for close events
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
// Print results
|
|
console.log();
|
|
console.log('='.repeat(60));
|
|
console.log('Test Results:');
|
|
console.log('='.repeat(60));
|
|
console.log(`Connections Opened: ${connectionsOpened}`);
|
|
console.log(`Connections Closed: ${connectionsClosed}`);
|
|
console.log(`Messages Received: ${messagesReceived}`);
|
|
console.log();
|
|
console.log('Closure Analysis:');
|
|
console.log(`- Premature Closes: ${stats.premature}`);
|
|
console.log(`- Normal Closes: ${stats.normal}`);
|
|
console.log(`- Errors: ${stats.errors}`);
|
|
console.log('='.repeat(60));
|
|
|
|
if (stats.premature > 0) {
|
|
console.error('FAILED: Detected premature connection closures!');
|
|
process.exit(1);
|
|
} else {
|
|
console.log('PASSED: No premature connection closures detected.');
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
runTest().catch(error => {
|
|
console.error('Test failed:', error);
|
|
process.exit(1);
|
|
});
|
|
|