Files
dat-thang/tests/interop/cabal/cable_cabal_hyperswarm_live.js
T
divorce b735900f2e 0.0.002
ddat interop with dat js
cable interop with cabal js
ddat's frontend = dat-thang
cable fe = cable-thang
dillo support based on cel's dillo-dat
bootstrap node
xplatform support

Signed-off-by: divo <418@cryspace.space>
2026-10-19 00:17:02 -04:00

79 lines
3.6 KiB
JavaScript

'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 })
})