mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
require('ts-node/register/transpile-only');
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
createLocalProcessDurableHandle,
|
||||
} = require('../../back/runtime/adapters/local-process/localProcessIdentity');
|
||||
const {
|
||||
LocalProcessPersistedExecutionController,
|
||||
} = require('../../back/runtime/adapters/local-process/persistedLocalProcessController');
|
||||
|
||||
const IDENTITY = {
|
||||
platform: 'linux',
|
||||
bootId: '11111111-2222-3333-4444-555555555555',
|
||||
pid: 4321,
|
||||
processGroupId: 4321,
|
||||
startTimeTicks: '123456',
|
||||
};
|
||||
const HANDLE = createLocalProcessDurableHandle('handle-1', IDENTITY);
|
||||
|
||||
function controller(inspections, overrides = {}) {
|
||||
const signals = [];
|
||||
let index = 0;
|
||||
return {
|
||||
signals,
|
||||
value: new LocalProcessPersistedExecutionController({
|
||||
identityProvider: {
|
||||
async capture() {
|
||||
throw new Error('not used');
|
||||
},
|
||||
async inspect(identity) {
|
||||
assert.deepEqual(identity, IDENTITY);
|
||||
const status = inspections[Math.min(index, inspections.length - 1)];
|
||||
index += 1;
|
||||
return { status };
|
||||
},
|
||||
},
|
||||
sendSignal(pid, signal) {
|
||||
signals.push({ pid, signal });
|
||||
},
|
||||
graceMs: 10,
|
||||
pollIntervalMs: 5,
|
||||
sleep: async () => undefined,
|
||||
...overrides,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const REASON = { kind: 'user', requestedAtMs: 1_750_000_000_000 };
|
||||
|
||||
test('refuses malformed, PID-mismatched, and non-leader handles without signals', async () => {
|
||||
const instance = controller(['running']);
|
||||
assert.equal(
|
||||
(await instance.value.stop({ durableHandle: 'bad', reason: REASON }))
|
||||
.status,
|
||||
'invalid',
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await instance.value.stop({
|
||||
durableHandle: HANDLE,
|
||||
expectedPid: 9999,
|
||||
reason: REASON,
|
||||
})
|
||||
).status,
|
||||
'pid_mismatch',
|
||||
);
|
||||
const nonLeader = createLocalProcessDurableHandle('handle-2', {
|
||||
...IDENTITY,
|
||||
processGroupId: 4000,
|
||||
});
|
||||
assert.equal(
|
||||
(await instance.value.stop({ durableHandle: nonLeader, reason: REASON }))
|
||||
.status,
|
||||
'identity_mismatch',
|
||||
);
|
||||
assert.deepEqual(instance.signals, []);
|
||||
});
|
||||
|
||||
test('sends TERM only when the persisted process exits during grace', async () => {
|
||||
const instance = controller(['running', 'exited']);
|
||||
const result = await instance.value.stop({
|
||||
durableHandle: HANDLE,
|
||||
expectedPid: 4321,
|
||||
reason: REASON,
|
||||
});
|
||||
assert.deepEqual(result, {
|
||||
status: 'termination_requested',
|
||||
termSignalSent: true,
|
||||
killSignalSent: false,
|
||||
});
|
||||
assert.deepEqual(instance.signals, [{ pid: -4321, signal: 'SIGTERM' }]);
|
||||
});
|
||||
|
||||
test('revalidates identity before escalating a persistent process to KILL', async () => {
|
||||
const persistent = controller(['running', 'running', 'running', 'running']);
|
||||
const killed = await persistent.value.stop({
|
||||
durableHandle: HANDLE,
|
||||
reason: REASON,
|
||||
});
|
||||
assert.equal(killed.killSignalSent, true);
|
||||
assert.deepEqual(persistent.signals, [
|
||||
{ pid: -4321, signal: 'SIGTERM' },
|
||||
{ pid: -4321, signal: 'SIGKILL' },
|
||||
]);
|
||||
|
||||
const changed = controller([
|
||||
'running',
|
||||
'running',
|
||||
'running',
|
||||
'identity_mismatch',
|
||||
]);
|
||||
const refused = await changed.value.stop({
|
||||
durableHandle: HANDLE,
|
||||
reason: REASON,
|
||||
});
|
||||
assert.equal(refused.status, 'identity_mismatch');
|
||||
assert.equal(refused.killSignalSent, false);
|
||||
assert.deepEqual(changed.signals, [{ pid: -4321, signal: 'SIGTERM' }]);
|
||||
});
|
||||
|
||||
test('treats an already exited process and ESRCH as idempotent completion', async () => {
|
||||
const exited = controller(['exited']);
|
||||
assert.deepEqual(
|
||||
await exited.value.stop({ durableHandle: HANDLE, reason: REASON }),
|
||||
{
|
||||
status: 'already_exited',
|
||||
termSignalSent: false,
|
||||
killSignalSent: false,
|
||||
},
|
||||
);
|
||||
|
||||
const noProcess = controller(['running'], {
|
||||
sendSignal() {
|
||||
const error = new Error('gone');
|
||||
error.code = 'ESRCH';
|
||||
throw error;
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
(await noProcess.value.stop({ durableHandle: HANDLE, reason: REASON }))
|
||||
.status,
|
||||
'already_exited',
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user