cluster.js (9537B)
1 const path = require('path'); 2 const { 3 spawnDaemon, 4 killDaemon, 5 killAllDaemons, 6 connectApi, 7 apiCommand, 8 findFreePort, 9 sleep, 10 createUdpEchoServer, 11 sendUdp, 12 TIMEOUT 13 } = require('./helpers'); 14 15 const CLUSTER_CONFIG_PATH = path.join(__dirname, 'config-cluster.ini'); 16 const NODE1_CONFIG_PATH = path.join(__dirname, 'config-node1.ini'); 17 const NODE2_CONFIG_PATH = path.join(__dirname, 'config-node2.ini'); 18 const CLUSTER_API_PORT = 19121; 19 const NODE1_API_PORT = 19122; 20 const NODE2_API_PORT = 19123; 21 22 async function runTest() { 23 let cluster = null; 24 let node1 = null; 25 let node2 = null; 26 let clusterApi = null; 27 let node1Api = null; 28 let node2Api = null; 29 let returnCode = 0; 30 let resp; 31 32 console.log('=== Cluster Test ===\n'); 33 34 await killAllDaemons(); 35 36 try { 37 console.log('1. Starting backing daemon node1...'); 38 node1 = await spawnDaemon(NODE1_CONFIG_PATH, 'daemon'); 39 console.log(` Node1 started (PID: ${node1.pid})`); 40 41 console.log('2. Starting backing daemon node2...'); 42 node2 = await spawnDaemon(NODE2_CONFIG_PATH, 'daemon'); 43 console.log(` Node2 started (PID: ${node2.pid})`); 44 45 console.log('3. Starting cluster...'); 46 cluster = await spawnDaemon(CLUSTER_CONFIG_PATH, 'cluster'); 47 console.log(` Cluster started (PID: ${cluster.pid})`); 48 49 await sleep(6000); // Wait for healthcheck to run 50 51 console.log('4. Connecting to cluster API...'); 52 clusterApi = await connectApi(CLUSTER_API_PORT); 53 console.log(' Connected to cluster'); 54 55 console.log('5. Connecting to node1 API...'); 56 node1Api = await connectApi(NODE1_API_PORT); 57 console.log(' Connected to node1'); 58 resp = await apiCommand(node1Api, 'auth', 'finwo', 'testsecret'); 59 console.log(` Auth response: ${resp}`); 60 61 console.log('6. Connecting to node2 API...'); 62 node2Api = await connectApi(NODE2_API_PORT); 63 console.log(' Connected to node2'); 64 resp = await apiCommand(node2Api, 'auth', 'finwo', 'testsecret'); 65 console.log(` Auth response: ${resp}`); 66 67 console.log('7. Authenticating with cluster...'); 68 resp = await apiCommand(clusterApi, 'auth', 'test', 'testsecret'); 69 console.log(` Auth response: ${resp}`); 70 if (resp !== 'OK') throw new Error('Cluster authentication failed'); 71 72 console.log('8. Testing session.count on cluster (no sessions)...'); 73 resp = await apiCommand(clusterApi, 'session.count'); 74 console.log(` cluster session.count: ${resp}`); 75 if (typeof resp !== 'number' || resp !== 0) { 76 throw new Error(`Expected 0 sessions, got ${resp}`); 77 } 78 79 console.log('9. Creating session on cluster...'); 80 resp = await apiCommand(clusterApi, 'session.create', 'test-session-1', '60'); 81 console.log(` session.create: ${resp}`); 82 if (resp !== 'OK') throw new Error('Failed to create session via cluster'); 83 84 console.log('10. Verifying session created on one of the nodes...'); 85 let node1Count = await apiCommand(node1Api, 'session.count'); 86 let node2Count = await apiCommand(node2Api, 'session.count'); 87 console.log(` Node1 session.count: ${node1Count}, Node2 session.count: ${node2Count}`); 88 let totalCount = node1Count + node2Count; 89 if (totalCount !== 1) { 90 throw new Error(`Expected 1 total session across nodes, got ${totalCount}`); 91 } 92 93 console.log('11. Testing session.list aggregation...'); 94 resp = await apiCommand(clusterApi, 'session.list'); 95 console.log(` cluster session.list: ${JSON.stringify(resp)}`); 96 if (!Array.isArray(resp) || resp.length !== 1) { 97 throw new Error(`Expected 1 session in list, got ${resp.length}`); 98 } 99 100 console.log('12. Testing session.count aggregation...'); 101 resp = await apiCommand(clusterApi, 'session.count'); 102 console.log(` cluster session.count: ${resp}`); 103 if (resp !== totalCount) { 104 throw new Error(`Expected ${totalCount} sessions, got ${resp}`); 105 } 106 107 console.log('13. Creating another session (should go to different node)...'); 108 resp = await apiCommand(clusterApi, 'session.create', 'test-session-2', '60'); 109 console.log(` session.create: ${resp}`); 110 if (resp !== 'OK') throw new Error('Failed to create second session'); 111 112 console.log('14. Verifying sessions distributed...'); 113 node1Count = await apiCommand(node1Api, 'session.count'); 114 node2Count = await apiCommand(node2Api, 'session.count'); 115 console.log(` Node1: ${node1Count}, Node2: ${node2Count}`); 116 if (node1Count + node2Count !== 2) { 117 throw new Error('Expected 2 total sessions'); 118 } 119 120 console.log('15. Testing session.info routing...'); 121 resp = await apiCommand(clusterApi, 'session.info', 'test-session-1'); 122 console.log(` session.info: ${JSON.stringify(resp).substring(0, 80)}...`); 123 if (!Array.isArray(resp)) { 124 throw new Error('Expected array response for session.info'); 125 } 126 127 console.log('16. Testing session.destroy...'); 128 resp = await apiCommand(clusterApi, 'session.destroy', 'test-session-1'); 129 console.log(` session.destroy: ${resp}`); 130 if (resp !== 'OK') throw new Error('Failed to destroy session'); 131 132 console.log('17. Verifying session destroyed...'); 133 resp = await apiCommand(clusterApi, 'session.count'); 134 console.log(` cluster session.count after destroy: ${resp}`); 135 if (resp !== 1) { 136 throw new Error(`Expected 1 session after destroy, got ${resp}`); 137 } 138 139 console.log('18. Testing socket creation on existing session...'); 140 resp = await apiCommand(clusterApi, 'session.socket.create.listen', 'test-session-2', 'socket1'); 141 console.log(` socket.create.listen: ${JSON.stringify(resp)}`); 142 if (!Array.isArray(resp) || resp.length < 1) { 143 throw new Error('Expected port in response'); 144 } 145 146 console.log('18b. Testing advertise address and UDP forwarding...'); 147 const listenPort = resp[0]; 148 const advertiseAddr = resp[1]; 149 console.log(` Listen port: ${listenPort}, advertise addr: ${advertiseAddr}`); 150 if (advertiseAddr !== '127.0.0.2' && advertiseAddr !== '127.0.0.3') { 151 throw new Error(`Expected advertise address 127.0.0.2 or 127.0.0.3, got ${advertiseAddr}`); 152 } 153 154 console.log(' Starting echo server...'); 155 const echoServer = await createUdpEchoServer(); 156 console.log(` Echo server on port: ${echoServer.port}`); 157 158 console.log(' Creating connect socket to echo server...'); 159 resp = await apiCommand(clusterApi, 'session.socket.create.connect', 'test-session-2', 'relay', '127.0.0.1', echoServer.port); 160 console.log(` socket.create.connect: ${JSON.stringify(resp)}`); 161 162 console.log(' Creating forward: socket1 -> relay...'); 163 resp = await apiCommand(clusterApi, 'session.forward.create', 'test-session-2', 'socket1', 'relay'); 164 console.log(` forward.create: ${resp}`); 165 166 console.log(' Sending UDP packet to listen socket...'); 167 await sendUdp(listenPort, advertiseAddr, 'hello'); 168 console.log(' Sent "hello"'); 169 170 console.log(' Waiting for echo response...'); 171 const messages = echoServer.getMessages(); 172 const start = Date.now(); 173 while (messages.length === 0 && Date.now() - start < TIMEOUT) { 174 await new Promise(r => setTimeout(r, 50)); 175 } 176 if (messages.length === 0) { 177 throw new Error('Timeout: no message received by echo server'); 178 } 179 const msg = messages[0]; 180 console.log(` Received: "${msg.data}" from ${msg.rinfo.address}:${msg.rinfo.port}`); 181 if (msg.data !== 'hello') { 182 throw new Error(`Expected "hello", got "${msg.data}"`); 183 } 184 echoServer.socket.close(); 185 186 console.log('19. Testing node failure handling...'); 187 console.log(' Killing node1...'); 188 await killDaemon(node1); 189 node1Api.end(); 190 node1Api = null; 191 await sleep(6000); // Wait for healthcheck to detect failure 192 193 console.log(' Creating session while node1 is down...'); 194 resp = await apiCommand(clusterApi, 'session.create', 'test-session-3', '60'); 195 console.log(` session.create: ${resp}`); 196 if (resp !== 'OK') throw new Error('Failed to create session while node1 down'); 197 198 console.log(' Verifying session via cluster session.list...'); 199 resp = await apiCommand(clusterApi, 'session.list'); 200 console.log(' cluster session.list:', resp); 201 if (!Array.isArray(resp) || resp.length < 1) { 202 throw new Error('Expected at least 1 session in cluster'); 203 } 204 205 console.log(' Restarting node1...'); 206 node1 = await spawnDaemon(NODE1_CONFIG_PATH, 'daemon'); 207 console.log(` Node1 restarted (PID: ${node1.pid})`); 208 await sleep(1000); 209 node1Api = await connectApi(NODE1_API_PORT); 210 await apiCommand(node1Api, 'auth', 'finwo', 'testsecret'); 211 await sleep(6000); // Wait for healthcheck to detect recovery 212 213 console.log(' Creating another session after node1 recovery...'); 214 resp = await apiCommand(clusterApi, 'session.create', 'test-session-4', '60'); 215 console.log(` session.create: ${resp}`); 216 if (resp !== 'OK') throw new Error('Failed to create session after node1 recovery'); 217 218 console.log('\n✓ PASS: All cluster tests passed'); 219 220 } catch (err) { 221 console.error(`\n✗ FAIL: ${err.message}`); 222 console.error(err.stack); 223 returnCode = 1; 224 } finally { 225 if (clusterApi) clusterApi.end(); 226 if (node1Api) node1Api.end(); 227 if (node2Api) node2Api.end(); 228 if (cluster) await killDaemon(cluster); 229 if (node1) await killDaemon(node1); 230 if (node2) await killDaemon(node2); 231 await killAllDaemons(); 232 process.exit(returnCode); 233 } 234 } 235 236 runTest();