upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/integration/test-local-relay.mjs
blob: cc8d6594151ddae14c39f87240fbfd5f8e61458b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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);
});