feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
+457
View File
@@ -0,0 +1,457 @@
require('ts-node/register/transpile-only');
const assert = require('node:assert/strict');
const { test } = require('node:test');
const { setTimeout: delay } = require('node:timers/promises');
const {
LocalProcessExecutor,
} = require('../../back/runtime/adapters/local-process/localProcessExecutor');
const {
PosixProcessTerminator,
} = require('../../back/runtime/adapters/local-process/processTerminator');
const {
ExecutorCapabilityUnavailableError,
ExecutorHandleNotFoundError,
ExecutorStartError,
InvalidExecutionSpecError,
} = require('../../back/runtime/domain/executorErrors');
let idSequence = 300;
function nextId() {
idSequence += 1;
return `019f70e0-0000-7000-8000-${String(idSequence).padStart(12, '0')}`;
}
function createSpec(overrides = {}) {
return {
runId: nextId(),
attemptId: nextId(),
projectId: 'default',
taskId: 'executor-contract-test',
taskRevision: 'revision-1',
command: {
kind: 'argv',
file: process.execPath,
args: ['-e', "process.stdout.write('ok')"],
},
environmentPolicy: 'isolated',
terminationGraceMs: 100,
...overrides,
};
}
function createOutputCollector(write) {
const chunks = { stdout: [], stderr: [] };
return {
chunks,
context: {
environment: {},
output: {
async write(output) {
chunks[output.stream].push(Buffer.from(output.chunk));
await write?.(output);
},
},
},
text(stream) {
return Buffer.concat(chunks[stream]).toString('utf8');
},
};
}
test(
'executes argv commands and drains ordered stdout/stderr before completion',
{ timeout: 5_000 },
async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector(async () => delay(2));
const handle = await executor.start(
createSpec({
command: {
kind: 'argv',
file: process.execPath,
args: [
'-e',
"process.stdout.write('stdout-value'); process.stderr.write('stderr-value')",
],
},
}),
output.context,
);
assert.ok(handle.pid > 0);
assert.equal(handle.executorType, 'local_process');
const result = await handle.completion;
assert.equal(result.outcome, 'succeeded');
assert.equal(result.exitCode, 0);
assert.equal(output.text('stdout'), 'stdout-value');
assert.equal(output.text('stderr'), 'stderr-value');
assert.deepEqual(await executor.inspect(handle), {
status: 'exited',
result,
});
},
);
test(
'supports explicit shell compatibility commands and stable non-zero results',
{ timeout: 5_000 },
async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
const handle = await executor.start(
createSpec({
command: {
kind: 'shell',
command: "printf 'shell-stdout'; printf 'shell-stderr' >&2; exit 7",
shell: '/bin/bash',
},
}),
output.context,
);
const result = await handle.completion;
assert.equal(result.outcome, 'failed');
assert.equal(result.exitCode, 7);
assert.equal(result.errorCode, 'PROCESS_EXIT_NON_ZERO');
assert.equal(output.text('stdout'), 'shell-stdout');
assert.equal(output.text('stderr'), 'shell-stderr');
},
);
test(
'honors isolated environment and an authorized working directory',
{ timeout: 5_000 },
async () => {
const previous = process.env.QL3_PARENT_ONLY_VALUE;
process.env.QL3_PARENT_ONLY_VALUE = 'must-not-leak';
try {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
output.context.environment = { QL3_SUPPLIED_VALUE: 'visible' };
const handle = await executor.start(
createSpec({
workingDirectory: process.cwd(),
command: {
kind: 'argv',
file: process.execPath,
args: [
'-e',
'process.stdout.write(JSON.stringify({ cwd: process.cwd(), supplied: process.env.QL3_SUPPLIED_VALUE, inherited: process.env.QL3_PARENT_ONLY_VALUE }))',
],
},
}),
output.context,
);
assert.equal((await handle.completion).outcome, 'succeeded');
assert.deepEqual(JSON.parse(output.text('stdout')), {
cwd: process.cwd(),
supplied: 'visible',
});
} finally {
if (previous === undefined) delete process.env.QL3_PARENT_ONLY_VALUE;
else process.env.QL3_PARENT_ONLY_VALUE = previous;
}
},
);
test(
'applies output backpressure without buffering the complete process output',
{ timeout: 10_000 },
async () => {
let activeWrites = 0;
let maximumActiveWrites = 0;
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector(async () => {
activeWrites += 1;
maximumActiveWrites = Math.max(maximumActiveWrites, activeWrites);
await delay(1);
activeWrites -= 1;
});
const handle = await executor.start(
createSpec({
command: {
kind: 'argv',
file: process.execPath,
args: [
'-e',
"for (let i = 0; i < 1000; i += 1) process.stdout.write('x'.repeat(1024))",
],
},
}),
output.context,
);
assert.equal((await handle.completion).outcome, 'succeeded');
assert.equal(output.text('stdout').length, 1_024_000);
assert.equal(maximumActiveWrites, 1);
},
);
test(
'continues draining output and reports a bounded diagnostic when the sink fails',
{ timeout: 5_000 },
async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
let writes = 0;
const context = {
environment: {},
output: {
async write() {
writes += 1;
throw new Error('sink contains potentially sensitive details');
},
},
};
const handle = await executor.start(
createSpec({
command: {
kind: 'argv',
file: process.execPath,
args: [
'-e',
"for (let i = 0; i < 200; i += 1) process.stdout.write('x'.repeat(1024))",
],
},
}),
context,
);
const result = await handle.completion;
assert.equal(result.outcome, 'succeeded');
assert.equal(writes, 1);
assert.deepEqual(result.diagnostics, [
{
code: 'OUTPUT_SINK_FAILED',
summary: 'Execution output sink failed; output may be incomplete',
},
]);
assert.doesNotMatch(JSON.stringify(result), /potentially sensitive/);
},
);
test(
'cancels a process group and keeps repeated stop calls idempotent',
{ timeout: 5_000 },
async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
const handle = await executor.start(
createSpec({
command: {
kind: 'argv',
file: process.execPath,
args: ['-e', 'setInterval(() => undefined, 1000)'],
},
}),
output.context,
);
const stopResult = await executor.stop(handle, {
kind: 'user',
requestedAtMs: Date.now(),
});
const result = await handle.completion;
assert.deepEqual(stopResult, {
status: 'termination_requested',
termSignalSent: true,
killSignalSent: false,
});
assert.equal(result.outcome, 'cancelled');
assert.equal(result.signal, 'SIGTERM');
assert.deepEqual(
await executor.stop(handle, {
kind: 'user',
requestedAtMs: Date.now(),
}),
{
status: 'already_exited',
termSignalSent: false,
killSignalSent: false,
},
);
},
);
test(
'escalates ignored timeout termination to SIGKILL and reports timed_out',
{ timeout: 5_000 },
async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
const handle = await executor.start(
createSpec({
// The complete test suite runs files concurrently; leave enough time
// for the child Node process to install its SIGTERM handler first.
timeoutMs: 1_000,
terminationGraceMs: 30,
command: {
kind: 'argv',
file: process.execPath,
args: [
'-e',
"process.on('SIGTERM', () => undefined); setInterval(() => undefined, 1000)",
],
},
}),
output.context,
);
const result = await handle.completion;
assert.equal(result.outcome, 'timed_out');
assert.equal(result.signal, 'SIGKILL');
assert.equal(result.errorCode, 'EXECUTION_TIMED_OUT');
},
);
test(
'observes AbortSignal cancellation without leaking listeners into completion',
{ timeout: 5_000 },
async () => {
const controller = new AbortController();
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
output.context.signal = controller.signal;
const handle = await executor.start(
createSpec({
command: {
kind: 'argv',
file: process.execPath,
args: ['-e', 'setInterval(() => undefined, 1000)'],
},
}),
output.context,
);
controller.abort();
assert.equal((await handle.completion).outcome, 'cancelled');
},
);
test('rejects unsupported required limits and invalid specs before spawn', async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
await assert.rejects(
executor.start(
createSpec({
resourcePolicy: {
memoryBytes: { value: 64 * 1024 * 1024, enforcement: 'required' },
},
}),
output.context,
),
ExecutorCapabilityUnavailableError,
);
await assert.rejects(
executor.start(
createSpec({ workingDirectory: 'relative/path' }),
output.context,
),
InvalidExecutionSpecError,
);
const bestEffort = await executor.start(
createSpec({
resourcePolicy: {
memoryBytes: {
value: 64 * 1024 * 1024,
enforcement: 'best_effort',
},
networkIsolation: 'best_effort',
},
}),
output.context,
);
assert.deepEqual((await bestEffort.completion).diagnostics, [
{
code: 'RESOURCE_POLICY_BEST_EFFORT_UNAVAILABLE',
summary:
'Best-effort capabilities were unavailable: memoryLimit, networkIsolation',
},
]);
});
test('maps spawn failures and rejects handles owned by another executor', async () => {
const executor = new LocalProcessExecutor({ createHandleId: nextId });
const output = createOutputCollector();
await assert.rejects(
executor.start(
createSpec({
command: {
kind: 'argv',
file: '/path/that/does/not/exist/ql3-command',
args: [],
},
}),
output.context,
),
ExecutorStartError,
);
await assert.rejects(
executor.inspect({
id: nextId(),
executorType: 'local_process',
runId: nextId(),
attemptId: nextId(),
startedAtMs: Date.now(),
completion: Promise.resolve({
outcome: 'lost',
startedAtMs: Date.now(),
finishedAtMs: Date.now(),
}),
}),
ExecutorHandleNotFoundError,
);
});
test('process terminator stops after TERM or escalates after the grace window', async () => {
let resolveClosed;
const closed = new Promise((resolve) => {
resolveClosed = resolve;
});
const signals = [];
const graceful = new PosixProcessTerminator((pid, signal) => {
signals.push([pid, signal]);
if (signal === 'SIGTERM') resolveClosed();
});
assert.deepEqual(
await graceful.terminate({
pid: 42,
processGroup: true,
graceMs: 50,
closed,
}),
{
alreadyExited: false,
termSignalSent: true,
killSignalSent: false,
},
);
assert.deepEqual(signals, [[-42, 'SIGTERM']]);
const escalatedSignals = [];
const escalated = new PosixProcessTerminator((pid, signal) => {
escalatedSignals.push([pid, signal]);
});
assert.deepEqual(
await escalated.terminate({
pid: 43,
processGroup: false,
graceMs: 1,
closed: new Promise(() => undefined),
}),
{
alreadyExited: false,
termSignalSent: true,
killSignalSent: true,
},
);
assert.deepEqual(escalatedSignals, [
[43, 'SIGTERM'],
[43, 'SIGKILL'],
]);
});