import WebSocket from 'ws'; import crypto from 'crypto'; const IP = process.env.TOLLGATE_IP || '10.192.45.1'; const RELAY_PORT = 4869; const RELAY_URL = `ws://${IP}:${RELAY_PORT}`; const TIMEOUT_MS = 8000; let passed = 0, failed = 0; function assert(condition, test) { if (condition) { console.log(` \u2713 ${test}`); passed++; } else { console.log(` \u2717 ${test}`); failed++; } } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } function connectWS(url = RELAY_URL) { return new Promise((resolve, reject) => { const ws = new WebSocket(url); const timer = setTimeout(() => { ws.close(); reject(new Error('connect timeout')); }, TIMEOUT_MS); ws.on('open', () => { clearTimeout(timer); resolve(ws); }); ws.on('error', (e) => { clearTimeout(timer); reject(e); }); }); } function collectMessages(ws, count, timeoutMs = TIMEOUT_MS) { return new Promise((resolve, reject) => { const msgs = []; const timer = setTimeout(() => { resolve(msgs); }, timeoutMs); ws.on('message', (data) => { try { msgs.push(JSON.parse(data.toString())); } catch { msgs.push(data.toString()); } if (msgs.length >= count) { clearTimeout(timer); resolve(msgs); } }); ws.on('error', (e) => { clearTimeout(timer); reject(e); }); }); } function makeEvent(kind, content, created_at) { const pubkey = 'a'.repeat(64); const id = crypto.randomBytes(32).toString('hex'); const sig = 'b'.repeat(128); return { id, pubkey, created_at: created_at || Math.floor(Date.now() / 1000), kind, content, tags: [] }; } async function runTests() { console.log(`\n=== Local Relay Integration Tests (target: ${IP}:${RELAY_PORT}) ===\n`); console.log('--- Test 1: WebSocket connect ---'); let ws; try { ws = await connectWS(); assert(true, `Connected to ${RELAY_URL}`); } catch (e) { assert(false, `Connected to ${RELAY_URL} — ${e.message}`); console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); process.exit(1); } console.log('\n--- Test 2: REQ with empty relay returns EOSE ---'); const reqMsg = JSON.stringify(['REQ', 'sub1', { limit: 1 }]); let msgs = await collectMessages(ws, 1, 5000); ws.send(reqMsg); msgs = await collectMessages(ws, 1, 5000); if (msgs.length === 0) { ws.send(reqMsg); msgs = await collectMessages(ws, 1, 5000); } const hasEOSE = msgs.some(m => Array.isArray(m) && m[0] === 'EOSE'); assert(hasEOSE, 'REQ returns EOSE for empty relay'); ws.close(); await sleep(500); console.log('\n--- Test 3: Publish event and get OK response ---'); ws = await connectWS(); const event1 = makeEvent(1, 'hello from test-local-relay'); const publishMsg = JSON.stringify(['EVENT', event1]); const okPromise = collectMessages(ws, 2, 5000); ws.send(publishMsg); const pubMsgs = await okPromise; const hasOK = pubMsgs.some(m => Array.isArray(m) && m[0] === 'OK'); if (hasOK) { assert(true, 'Publish returns OK'); } else { const hasNotice = pubMsgs.some(m => Array.isArray(m) && m[0] === 'NOTICE'); if (hasNotice) { const noticeMsg = pubMsgs.find(m => Array.isArray(m) && m[0] === 'NOTICE'); console.log(` NOTICE: ${JSON.stringify(noticeMsg)}`); assert(true, 'Publish returns NOTICE (relay running, may reject unsigned test event)'); } else { assert(false, 'Publish returns OK or NOTICE'); } } ws.close(); await sleep(500); console.log('\n--- Test 4: REQ after publish returns event ---'); ws = await connectWS(); const event2 = makeEvent(1, 'test event for REQ'); ws.send(JSON.stringify(['EVENT', event2])); await sleep(500); const reqMsg2 = JSON.stringify(['REQ', 'sub2', { limit: 10 }]); msgs = await collectMessages(ws, 5, 5000); ws.send(reqMsg2); msgs = await collectMessages(ws, 5, 5000); const hasEvents = msgs.some(m => Array.isArray(m) && m[0] === 'EVENT'); const hasEOSE2 = msgs.some(m => Array.isArray(m) && m[0] === 'EOSE'); if (hasEvents) { assert(true, 'REQ returns EVENT messages'); } else if (hasEOSE2) { assert(true, 'REQ returns EOSE (relay may have rejected unsigned test events)'); console.log(' (relay validator rejected unsigned events — expected behavior)'); } else { assert(false, 'REQ returns EVENT or EOSE'); } ws.close(); console.log('\n--- Test 5: CLOSE subscription ---'); ws = await connectWS(); ws.send(JSON.stringify(['REQ', 'sub3', { limit: 10 }])); await sleep(300); ws.send(JSON.stringify(['CLOSE', 'sub3'])); await sleep(300); assert(true, 'CLOSE sent without error'); ws.close(); console.log('\n--- Test 6: Multiple concurrent connections ---'); const connections = []; try { for (let i = 0; i < 3; i++) { connections.push(await connectWS()); } assert(connections.length === 3, '3 concurrent WebSocket connections established'); for (const c of connections) c.close(); } catch (e) { assert(false, `3 concurrent connections — ${e.message}`); for (const c of connections) c.close(); } console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); process.exit(failed > 0 ? 1 : 0); } runTests().catch(e => { console.error('Test error:', e.message); process.exit(1); });