const path = require('path');
const { startRelay, stopAll } = require('./helpers/relay');
const { setupPanic, teardownPanic } = require('./helpers/setup');
const binPath = path.resolve(__dirname, '../../target/debug/beam');
const config = {
panicPort: 8765,
relayPort: 9100,
numClients: 6,
each: 200,
soul: 'loadtest',
};
const { clients } = setupPanic({ numClients: config.numClients, panicPort: config.panicPort });
describe(`12. Load: ${config.numClients} clients × ${config.each} msgs, all verified by all`, function () {
this.timeout(300000);
it(`${config.numClients} clients connected to panic-server`, function () {
return clients.atLeast(config.numClients);
});
it('Relay starts', function () {
this.timeout(10000);
startRelay({ binPath, port: config.relayPort });
return new Promise((resolve) => setTimeout(resolve, 2000));
});
it(`All ${config.numClients} clients put ${config.each} msgs and receive all ${config.numClients * config.each}`, function () {
this.timeout(240000);
const ids = [];
for (let i = 0; i < config.numClients; i++) ids.push('c' + i);
const total = config.numClients * config.each;
const tests = [];
let idx = 0;
clients.each(function (client, id) {
const myId = ids[idx++];
tests.push(client.run(function (test) {
test.async();
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:' + test.props.relayPort);
const myId = test.props.myId;
const each = test.props.each;
const total = test.props.total;
const soul = test.props.soul;
const seen = new Set();
let putAcked = 0;
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
ws.close();
test.done();
};
const fail = (msg) => {
if (finished) return;
finished = true;
try { ws.close(); } catch (e) {}
test.fail(msg);
};
const start = Date.now();
const check = setInterval(() => {
if (seen.size >= total) {
clearInterval(check);
finish();
} else if (Date.now() - start > 210000) {
clearInterval(check);
fail(`timeout — received ${seen.size}/${total} distinct keys`);
}
}, 200);
ws.on('open', () => {
ws.send(JSON.stringify({
get: { '#': soul },
'#': 'ldsub' + myId,
}));
subscribed = true;
const ts = Date.now();
for (let batch = 0; batch < each; batch += 10) {
const frame = [];
for (let j = batch; j < Math.min(batch + 10, each); j++) {
const key = myId + '_' + j;
seen.add(key);
frame.push({
'#': 'ld' + myId + 'p' + j,
put: {
[soul]: {
_: { '#': soul, '>': { [key]: ts + j } },
[key]: 'Hello world, ' + key + '!',
},
},
});
}
ws.send(JSON.stringify(frame));
}
});
ws.on('message', (raw) => {
let msgs;
try { msgs = JSON.parse(raw.toString()); } catch (e) { return; }
if (!Array.isArray(msgs)) msgs = [msgs];
for (const m of msgs) {
if (m['@'] && m['@'].startsWith('ld' + myId + 'p')) {
putAcked++;
}
if (m.put && m.put[soul]) {
for (const k of Object.keys(m.put[soul])) {
if (k !== '_') seen.add(k);
}
}
}
});
ws.on('error', (err) => fail('WS error: ' + err.message));
ws.on('close', () => { if (!finished) fail('WS closed early'); });
}, { relayPort: config.relayPort, myId, each: config.each, total, soul: config.soul }));
});
return Promise.all(tests);
});
after(function () {
teardownPanic();
});
});