Browse Source
- 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.main
3 changed files with 240 additions and 4 deletions
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
#!/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); |
||||
}); |
||||
|
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
import { NostrWebSocket } from '@nostr-dev-kit/ndk'; |
||||
|
||||
const RELAY = process.env.RELAY || 'ws://localhost:8080'; |
||||
|
||||
async function testConnectionClosure() { |
||||
console.log('Testing websocket connection closure issues...'); |
||||
console.log('Connecting to:', RELAY); |
||||
|
||||
// Create multiple connections to test concurrency
|
||||
const connections = []; |
||||
const results = { connected: 0, closed: 0, errors: 0 }; |
||||
|
||||
for (let i = 0; i < 5; i++) { |
||||
const ws = new NostrWebSocket(RELAY); |
||||
|
||||
ws.addEventListener('open', () => { |
||||
console.log(`Connection ${i} opened`); |
||||
results.connected++; |
||||
}); |
||||
|
||||
ws.addEventListener('close', (event) => { |
||||
console.log(`Connection ${i} closed:`, event.code, event.reason); |
||||
results.closed++; |
||||
}); |
||||
|
||||
ws.addEventListener('error', (error) => { |
||||
console.error(`Connection ${i} error:`, error); |
||||
results.errors++; |
||||
}); |
||||
|
||||
connections.push(ws); |
||||
} |
||||
|
||||
// Wait a bit then send REQs
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); |
||||
|
||||
// Send some REQ messages
|
||||
for (const ws of connections) { |
||||
ws.send(JSON.stringify(['REQ', 'test-sub', { kinds: [1] }])); |
||||
} |
||||
|
||||
// Wait and observe behavior
|
||||
await new Promise(resolve => setTimeout(resolve, 5000)); |
||||
|
||||
console.log('\nTest Results:'); |
||||
console.log(`- Connected: ${results.connected}`); |
||||
console.log(`- Closed prematurely: ${results.closed}`); |
||||
console.log(`- Errors: ${results.errors}`); |
||||
|
||||
// Close all connections
|
||||
for (const ws of connections) { |
||||
ws.close(); |
||||
} |
||||
} |
||||
|
||||
testConnectionClosure().catch(console.error); |
||||
|
||||
Loading…
Reference in new issue