Some checks are pending
offline / test (push) Waiting to run
Central fleet.json sets min/max copies. Tree-node keepFloor respawns until three healthy unpaused replicas remain. CLI and loopback UI pause, resume, stop, and restart instances that fail health checks.
99 lines
3.3 KiB
JavaScript
99 lines
3.3 KiB
JavaScript
import { describe, it, after } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { loadFleet, listServices } from '../src/load.js';
|
|
import { Supervisor } from '../src/supervisor.js';
|
|
import { Monitor } from '../src/monitor.js';
|
|
|
|
function treeOnly(healthBase) {
|
|
const loaded = loadFleet();
|
|
for (const s of Object.values(loaded.services)) {
|
|
s.enabled = s.id === 'tree-node';
|
|
if (s.id === 'tree-node') {
|
|
s.min = 3;
|
|
s.max = 6;
|
|
s.keepFloor = true;
|
|
s.ports = { healthBase };
|
|
}
|
|
}
|
|
return loaded;
|
|
}
|
|
|
|
describe('verae-fleet', () => {
|
|
/** @type {Supervisor[]} */
|
|
const supervisors = [];
|
|
after(async () => {
|
|
for (const s of supervisors) await s.stopAll();
|
|
});
|
|
|
|
it('lists every service with a config file and central replica spec', () => {
|
|
const loaded = loadFleet();
|
|
const rows = listServices(loaded);
|
|
const ids = rows.map((r) => r.id);
|
|
for (const need of [
|
|
'nats',
|
|
'zappier-edge',
|
|
'middleware-http',
|
|
'job-poller',
|
|
'webhook-deliver',
|
|
'archive-aggregator',
|
|
'archive-worm',
|
|
'tree-node',
|
|
'zapier-simulator',
|
|
'zapier-platform-app',
|
|
]) {
|
|
assert.ok(ids.includes(need), `missing ${need}`);
|
|
}
|
|
const tree = loaded.services['tree-node'];
|
|
assert.equal(tree.min, 3);
|
|
assert.equal(tree.keepFloor, true);
|
|
assert.equal(tree.configPath, 'services/tree-node.json');
|
|
assert.equal(loaded.fleetPath, 'fleet.json');
|
|
});
|
|
|
|
it('starts the tree-node floor (3 available copies)', async () => {
|
|
const sup = new Supervisor({ loaded: treeOnly(14600) });
|
|
supervisors.push(sup);
|
|
await sup.startService('tree-node');
|
|
const st = sup.status().services['tree-node'];
|
|
assert.equal(st.available, 3);
|
|
assert.equal(st.belowFloor, false);
|
|
assert.equal(st.instances.length, 3);
|
|
});
|
|
|
|
it('pauses one tree-node and respawns so min available stays 3', async () => {
|
|
const sup = new Supervisor({ loaded: treeOnly(14700) });
|
|
supervisors.push(sup);
|
|
await sup.startService('tree-node');
|
|
await sup.pauseInstance('tree-node-0');
|
|
const st = sup.status().services['tree-node'];
|
|
assert.ok(st.available >= 3, `available ${st.available}`);
|
|
assert.ok(st.paused >= 1);
|
|
assert.ok(st.running >= 4);
|
|
assert.equal(st.belowFloor, false);
|
|
});
|
|
|
|
it('restarts an unhealthy replica and keeps the floor', async () => {
|
|
const sup = new Supervisor({ loaded: treeOnly(14800) });
|
|
supervisors.push(sup);
|
|
await sup.startService('tree-node');
|
|
const before = sup.instances.get('tree-node-1').pid;
|
|
await sup.markUnhealthy('tree-node-1');
|
|
const mon = new Monitor(sup, { intervalMs: 50 });
|
|
await mon.tick();
|
|
const rec = sup.instances.get('tree-node-1');
|
|
assert.ok(rec.pid);
|
|
assert.notEqual(rec.pid, before);
|
|
assert.equal(rec.healthy, true);
|
|
assert.equal(sup.status().services['tree-node'].available, 3);
|
|
});
|
|
|
|
it('stop+replace of a replica keeps min tree-nodes available', async () => {
|
|
const sup = new Supervisor({ loaded: treeOnly(14900) });
|
|
supervisors.push(sup);
|
|
await sup.startService('tree-node');
|
|
await sup.stopInstance('tree-node-2', { replace: true });
|
|
const st = sup.status().services['tree-node'];
|
|
assert.equal(st.available, 3);
|
|
assert.ok(st.instances.some((i) => i.id === 'tree-node-2' || i.id === 'tree-node-0'));
|
|
});
|
|
});
|