mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-28 09:02:12 +08:00
78 lines
2.4 KiB
JavaScript
78 lines
2.4 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const ts = require('typescript');
|
|
|
|
// Exercise the production function with a deterministic process-tree race.
|
|
function fixture(descendants, probe = () => {}) {
|
|
const source = fs.readFileSync('back/config/util.ts', 'utf8');
|
|
const start = source.indexOf('export async function killTask(');
|
|
const end = source.indexOf('export async function getPid(', start);
|
|
const code = ts.transpileModule(source.slice(start, end), {
|
|
compilerOptions: {
|
|
target: ts.ScriptTarget.ES2020,
|
|
module: ts.ModuleKind.CommonJS,
|
|
},
|
|
}).outputText;
|
|
const signals = [];
|
|
const exports = {};
|
|
new Function('exports', 'process', 'psTree', 'fs', 'setTimeout', code)(
|
|
exports,
|
|
{
|
|
platform: 'linux',
|
|
kill: (pid, signal) => {
|
|
const normalized = { 15: 'SIGTERM', 2: 'SIGINT' }[signal] || signal;
|
|
signals.push([pid, normalized]);
|
|
probe(pid, normalized);
|
|
},
|
|
},
|
|
async () => [...descendants],
|
|
{},
|
|
setTimeout,
|
|
);
|
|
return { killTask: exports.killTask, signals };
|
|
}
|
|
|
|
test('an exited descendant does not prevent stopping its siblings and parent', async () => {
|
|
const { killTask, signals } = fixture([101, 102], (pid) => {
|
|
if (pid === 102) throw Object.assign(Error('gone'), { code: 'ESRCH' });
|
|
});
|
|
await killTask(100);
|
|
assert.deepEqual(signals, [
|
|
[102, 'SIGTERM'],
|
|
[101, 'SIGTERM'],
|
|
[100, 'SIGTERM'],
|
|
]);
|
|
});
|
|
|
|
test('an already exited leaf can be stopped repeatedly', async () => {
|
|
const { killTask } = fixture([], () => {
|
|
throw Object.assign(Error('gone'), { code: 'ESRCH' });
|
|
});
|
|
await killTask(100);
|
|
await killTask(100);
|
|
});
|
|
|
|
for (const descendants of [[], [101]]) {
|
|
test(`signal permission failure is surfaced with ${descendants.length} descendants`, async () => {
|
|
const denied = Object.assign(Error('denied'), { code: 'EPERM' });
|
|
const { killTask } = fixture(descendants, () => {
|
|
throw denied;
|
|
});
|
|
await assert.rejects(killTask(100), (error) => error === denied);
|
|
});
|
|
}
|
|
|
|
test('a leaf retains SIGINT and a process tree retains child-first SIGTERM', async () => {
|
|
const leaf = fixture([]);
|
|
await leaf.killTask(100);
|
|
assert.deepEqual(leaf.signals, [[100, 'SIGINT']]);
|
|
const tree = fixture([101, 102]);
|
|
await tree.killTask(100);
|
|
assert.deepEqual(tree.signals, [
|
|
[102, 'SIGTERM'],
|
|
[101, 'SIGTERM'],
|
|
[100, 'SIGTERM'],
|
|
]);
|
|
});
|