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 @@ |
|||||||
|
#!/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 @@ |
|||||||
|
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