Files
qinglong/packages/ql3-local-process/test/controller.test.cjs
T

117 lines
3.4 KiB
JavaScript

const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
LocalProcessController,
createLocalProcessDurableHandle,
} = require('../dist');
const IDENTITY = Object.freeze({
platform: 'linux',
bootId: '11111111-2222-3333-4444-555555555555',
pid: 1234,
processGroupId: 1234,
startTimeTicks: '987654',
});
const HANDLE = createLocalProcessDurableHandle('handle-1', IDENTITY);
function fixture(inspections) {
let now = 0;
const signals = [];
let index = 0;
const controller = new LocalProcessController({
identityProvider: {
async inspect(identity) {
assert.deepEqual(identity, IDENTITY);
return inspections[Math.min(index++, inspections.length - 1)];
},
},
terminateGraceMs: 10,
killGraceMs: 10,
pollIntervalMs: 5,
clock: { now: () => now },
wait: async (delayMs) => {
now += delayMs;
},
signalProcessGroup(processGroupId, signal) {
signals.push({ processGroupId, signal });
},
});
return { controller, signals };
}
test('stops only after exact durable identity inspection', async () => {
const value = fixture([
{ status: 'running', identityPid: 1234 },
{ status: 'not_running', identityPid: 1234 },
]);
assert.deepEqual(await value.controller.stop(HANDLE), {
status: 'stopped',
signal: 'SIGTERM',
});
assert.deepEqual(value.signals, [
{ processGroupId: 1234, signal: 'SIGTERM' },
]);
});
test('revalidates identity before escalating to SIGKILL', async () => {
const value = fixture([
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'not_running', identityPid: 1234 },
]);
assert.deepEqual(await value.controller.stop(HANDLE), {
status: 'stopped',
signal: 'SIGKILL',
});
assert.deepEqual(value.signals, [
{ processGroupId: 1234, signal: 'SIGTERM' },
{ processGroupId: 1234, signal: 'SIGKILL' },
]);
});
test('does not escalate after identity is no longer running', async () => {
const value = fixture([
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'running', identityPid: 1234 },
{ status: 'not_running', identityPid: 1234 },
]);
assert.deepEqual(await value.controller.stop(HANDLE), {
status: 'stopped',
signal: 'SIGTERM',
});
assert.deepEqual(value.signals, [
{ processGroupId: 1234, signal: 'SIGTERM' },
]);
});
test('rejects an unparseable handle without signaling', async () => {
const value = fixture([{ status: 'running', identityPid: 1234 }]);
assert.deepEqual(await value.controller.stop('not-a-durable-handle'), {
status: 'unknown',
reason: 'invalid_handle',
});
assert.deepEqual(value.signals, []);
});
test('maps identity provider failure to unknown without signaling', async () => {
const signals = [];
const controller = new LocalProcessController({
identityProvider: {
inspect: async () => Promise.reject(new Error('proc unavailable')),
},
signalProcessGroup(processGroupId, signal) {
signals.push({ processGroupId, signal });
},
});
assert.deepEqual(await controller.stop(HANDLE), {
status: 'unknown',
reason: 'provider_unavailable',
});
assert.deepEqual(signals, []);
});