'use strict' // Live Cabal Core/Hyperswarm smoke test for the same S-expression IPC used by // cable-thang. Run with `node tests/interop/cabal/cable_cabal_hyperswarm_live.js`. const { spawn } = require('child_process') const crypto = require('crypto') const fs = require('fs') const os = require('os') const path = require('path') const readline = require('readline') const root = path.resolve(__dirname, '../..') const bridgePath = path.join(__dirname, 'cable_hyperswarm.js') const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'dat-gpt-cabal-live-')) const room = crypto.randomBytes(32).toString('hex') const peers = [] function startPeer (name) { const child = spawn(process.execPath, [bridgePath], { cwd: root, stdio: ['pipe', 'pipe', 'inherit'] }) const peer = { name, child, lines: [], error: null, rl: readline.createInterface({ input: child.stdout }) } peer.rl.on('line', line => { peer.lines.push(line); process.stdout.write(name + ' ' + line + '\n') }) child.on('error', err => { peer.error = err }) child.on('exit', (code, signal) => { if (code && code !== 0) peer.error = new Error(`${name} exited ${code} (${signal || 'no signal'})`) }) peers.push(peer) return peer } function send (peer, expression) { if (!peer.child.stdin.write(expression + '\n')) throw new Error(`${peer.name} bridge stdin closed`) } async function waitFor (peer, predicate, description, timeout = 90000) { const start = Date.now() while (Date.now() - start < timeout) { if (peer.error) throw peer.error const found = peer.lines.find(predicate) if (found) return found await new Promise(resolve => setTimeout(resolve, 100)) } throw new Error(`timeout waiting for ${description}; ${peer.name} last output: ${peer.lines.slice(-5).join(' | ')}`) } function startExpression (peer, nick) { const storage = path.join(tempRoot, peer.name) return `(start ${JSON.stringify(room)} ${JSON.stringify(storage)} ${JSON.stringify(nick)} "default")` } async function main () { const alice = startPeer('alice') const bob = startPeer('bob') send(alice, startExpression(alice, 'Alice')) send(bob, startExpression(bob, 'Bob')) await Promise.all([waitFor(alice, line => line.startsWith('(event ready '), 'Alice Cabal feed'), waitFor(bob, line => line.startsWith('(event ready '), 'Bob Cabal feed')]) await Promise.all([waitFor(alice, line => line.startsWith('(event peer-up '), 'Alice Hyperswarm peer discovery'), waitFor(bob, line => line.startsWith('(event peer-up '), 'Bob Hyperswarm peer discovery')]) send(alice, `(send "default" "Cable bridge says hello")`) await waitFor(bob, line => line.includes('Cable bridge says hello'), 'Alice-to-Bob Cabal message') send(bob, `(send "default" "Cabal bridge replies")`) await waitFor(alice, line => line.includes('Cabal bridge replies'), 'Bob-to-Alice Cabal message') console.log('Cabal Core Hyperswarm discovery and bidirectional chat: PASS') } main().catch(err => { console.error(err.stack || err); process.exitCode = 1 }).finally(async () => { for (const peer of peers) { try { send(peer, '(close)') } catch (_) {} peer.child.stdin.end() } await Promise.all(peers.map(peer => new Promise(resolve => { if (peer.child.exitCode !== null) return resolve() const timer = setTimeout(() => { peer.child.kill('SIGTERM'); resolve() }, 5000) peer.child.once('exit', () => { clearTimeout(timer); resolve() }) }))) const resolvedRoot = path.resolve(tempRoot) if (resolvedRoot.startsWith(path.resolve(os.tmpdir()) + path.sep) && path.basename(resolvedRoot).startsWith('dat-gpt-cabal-live-')) fs.rmSync(resolvedRoot, { recursive: true, force: true }) })