const path = require('path');
const fs = require('fs');
const panic = require('panic-server');
const manager = require('panic-manager')();
const { startRelay, stopAll } = require('./helpers/relay');
const { assert } = require('chai');
const config = {
ip: 'localhost',
panicPort: 8765,
beamPort: 9100,
beamPath: process.env.BEAM_PATH || path.resolve(__dirname, '../../target/debug/beam'),
};
const routes = {
'/': __dirname + '/helpers/index.html',
'/panic.js': require.resolve('panic-client'),
};
const beamWasmDir = path.resolve(__dirname, '../../browser-test');
if (fs.existsSync(path.join(beamWasmDir, 'beam.js'))) {
routes['/beam.js'] = path.join(beamWasmDir, 'beam.js');
routes['/beam_bg.wasm'] = path.join(beamWasmDir, 'beam_bg.wasm');
}
const srv = panic.server();
srv.on('request', (req, res) => {
const filePath = routes[req.url];
if (filePath && !res.headersSent) {
fs.createReadStream(filePath).pipe(res);
}
});
srv.listen(config.panicPort, () => {
console.log('[panic] server on :' + config.panicPort);
});
manager.start({
clients: [
{ type: 'node', port: config.panicPort + 1 },
{ type: 'node', port: config.panicPort + 2 },
{ type: 'node', port: config.panicPort + 3 },
{ type: 'node', port: config.panicPort + 4 },
],
panic: 'http://' + config.ip + ':' + config.panicPort,
});
const clients = panic.clients;
const alice = clients.pluck(1);
describe('2. Convergence: multi-client write/fan-out', function () {
this.timeout(60 * 1000);
const soul = 'panic/convergence/' + Date.now();
it('All 4 clients connected to panic-server', function () {
return clients.atLeast(4);
});
it('BEAM relay started', function () {
this.timeout(10 * 1000);
startRelay({ binPath: config.beamPath, port: config.beamPort });
return new Promise((resolve) => setTimeout(resolve, 2000));
});
it('Alice puts data through BEAM relay', function () {
return alice.run(function (test) {
test.async();
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:' + test.props.beamPort);
ws.on('open', () => {
ws.send(JSON.stringify({
'#': 'putconv',
put: {
[test.props.soul]: {
_: { '#': test.props.soul, '>': { hello: Date.now() } },
hello: 'convergence-world',
},
},
}));
});
ws.on('message', (raw) => {
let msgs = JSON.parse(raw.toString());
if (!Array.isArray(msgs)) msgs = [msgs];
for (const msg of msgs) {
if (msg['@'] === 'putconv') {
ws.close();
test.done();
return;
}
}
});
ws.on('error', (err) => test.fail('WebSocket error: ' + err.message));
setTimeout(() => test.fail('timeout — no ack in 5s'), 5000);
}, { soul, beamPort: config.beamPort });
});
it('All 4 clients can Get Alice\'s data', function () {
this.timeout(30 * 1000);
return clients.run(function (test) {
test.async();
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:' + test.props.beamPort);
let gotData = null;
ws.on('message', (raw) => {
let msgs = JSON.parse(raw.toString());
if (!Array.isArray(msgs)) msgs = [msgs];
for (const msg of msgs) {
if (msg.put && msg.put[test.props.soul] && msg.put[test.props.soul].hello) {
gotData = msg.put[test.props.soul].hello;
}
}
});
ws.on('open', () => {
setTimeout(() => {
ws.send(JSON.stringify({
get: { '#': test.props.soul },
'#': 'check' + Math.random().toString(36).slice(2),
}));
}, 500);
});
ws.on('error', (err) => test.fail('WebSocket error: ' + err.message));
const start = Date.now();
const check = setInterval(() => {
if (gotData) {
clearInterval(check);
ws.close();
test.done();
} else if (Date.now() - start > 5000) {
clearInterval(check);
test.fail('timeout — no data in 5s');
}
}, 100);
}, { soul, beamPort: config.beamPort });
});
after(function () {
try { manager.stop(); } catch(e) {}
stopAll();
});
});