57 lines
2.7 KiB
JavaScript
57 lines
2.7 KiB
JavaScript
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const vm = require('node:vm');
|
|
|
|
// Execute the actual server shutdown registration/handler, without booting DB or routes.
|
|
for (const failure of [null, 'audit', 'db', 'mcp']) {
|
|
test('server drains HTTP, audit and DB before MCP/exit: ' + (failure || 'success'), async () => {
|
|
const source = fs.readFileSync(require.resolve('../server'), 'utf8');
|
|
const events = [];
|
|
const signals = {};
|
|
let httpClosed;
|
|
let deadline;
|
|
let deadlineMs;
|
|
let releaseAudit;
|
|
const auditGate = new Promise(resolve => { releaseAudit = resolve; });
|
|
let releaseDb;
|
|
const dbGate = new Promise(resolve => { releaseDb = resolve; });
|
|
const context = {
|
|
console: { log() {}, error() {} },
|
|
imageWorker: { async stop() { events.push('images.stop'); } },
|
|
server: { close(callback) { events.push('http.close'); httpClosed = callback; } },
|
|
process: { on(signal, callback) { signals[signal] = callback; }, exit(code) { events.push('exit:' + code); } },
|
|
setTimeout(callback, ms) { deadline = callback; deadlineMs = ms; return { unref() { events.push('guard.unref'); } }; },
|
|
clearInterval(id) { assert.equal(id, 123); events.push('cleanup.clear'); },
|
|
require(name) {
|
|
if (name.endsWith('/auditQueue')) return { async drainAll() {
|
|
events.push('audit.start'); await auditGate; events.push('audit.end');
|
|
if (failure === 'audit') throw new Error('failure');
|
|
} };
|
|
if (name.endsWith('/database')) return { _cleanupInterval: 123, pool: { async end() {
|
|
events.push('db.start'); await dbGate; events.push('db.end');
|
|
if (failure === 'db') throw new Error('failure');
|
|
} } };
|
|
if (name.endsWith('/clinicalMcpClient')) return { async closeMcpSession() {
|
|
events.push('mcp.close'); if (failure === 'mcp') throw new Error('secret');
|
|
} };
|
|
throw new Error('unexpected dependency');
|
|
}
|
|
};
|
|
vm.runInNewContext(source.slice(source.indexOf('var shuttingDown = false;')), context);
|
|
signals.SIGTERM();
|
|
signals.SIGINT();
|
|
assert.deepEqual(events, ['images.stop', 'http.close', 'guard.unref']);
|
|
assert.equal(deadlineMs, 9000);
|
|
const done = httpClosed();
|
|
assert.deepEqual(events.slice(3), ['audit.start']);
|
|
releaseAudit();
|
|
await new Promise(resolve => setImmediate(resolve));
|
|
assert.deepEqual(events.slice(3), ['audit.start', 'audit.end', 'cleanup.clear', 'db.start']);
|
|
releaseDb();
|
|
await done;
|
|
assert.deepEqual(events.slice(3), ['audit.start', 'audit.end', 'cleanup.clear', 'db.start', 'db.end', 'mcp.close', 'exit:0']);
|
|
deadline();
|
|
assert.equal(events.at(-1), 'exit:1');
|
|
});
|
|
}
|