mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
117 lines
3.4 KiB
JavaScript
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, []);
|
|
});
|