mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 03:18:09 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
const { CompletionReceiptFileStore } = require('../dist');
|
||||
const { LocalProcessLaunchError, LocalProcessLauncher } = require('../dist');
|
||||
|
||||
const RUN_ID = '019f70e0-0000-7000-8000-000000000001';
|
||||
const ATTEMPT_ID = '019f70e0-0000-7000-8000-000000000002';
|
||||
const TOKEN = '0123456789abcdef0123456789abcdef0123456789abcdef';
|
||||
|
||||
function fixture(t) {
|
||||
const directory = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'ql3-local-process-'),
|
||||
);
|
||||
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
return {
|
||||
directory,
|
||||
receiptRoot: path.join(directory, 'receipts'),
|
||||
};
|
||||
}
|
||||
|
||||
function identityProvider(onCapture = () => undefined) {
|
||||
return {
|
||||
async capture(pid) {
|
||||
onCapture(pid);
|
||||
return {
|
||||
platform: 'linux',
|
||||
bootId: '11111111-2222-3333-4444-555555555555',
|
||||
pid,
|
||||
processGroupId: pid,
|
||||
startTimeTicks: '1',
|
||||
};
|
||||
},
|
||||
async inspect(identity) {
|
||||
return { status: 'running', identityPid: identity.pid };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForReceipt(store) {
|
||||
for (let index = 0; index < 100; index += 1) {
|
||||
const receipt = await store.read(ATTEMPT_ID);
|
||||
if (receipt) return receipt;
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
throw new Error('receipt was not published');
|
||||
}
|
||||
|
||||
test('persists the journal barrier before spawn and publishes a trusted receipt', async (t) => {
|
||||
const { receiptRoot } = fixture(t);
|
||||
let registered = false;
|
||||
const journal = {
|
||||
async register(command) {
|
||||
assert.deepEqual(command, {
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
registeredAtMs: 100,
|
||||
});
|
||||
registered = true;
|
||||
},
|
||||
};
|
||||
const launcher = new LocalProcessLauncher(journal, {
|
||||
receiptRoot,
|
||||
clock: { now: () => 100 },
|
||||
createHandleId: () => 'handle-1',
|
||||
identityProvider: identityProvider(() => assert.equal(registered, true)),
|
||||
});
|
||||
const handle = await launcher.start({
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
callbackToken: TOKEN,
|
||||
command: {
|
||||
kind: 'shell',
|
||||
command:
|
||||
'test -z "${QL3_RECEIPT_CALLBACK_TOKEN-}" && test -z "${QL3_RECEIPT_TARGET-}"',
|
||||
},
|
||||
environment: { USER_VALUE: 'present' },
|
||||
});
|
||||
assert.equal(handle.startedAtMs, 100);
|
||||
assert.equal(handle.pid > 0, true);
|
||||
assert.match(handle.durableHandle, /^ql3lp1\./);
|
||||
assert.deepEqual(await handle.completion, { exitCode: 0, signal: null });
|
||||
const receipt = await waitForReceipt(
|
||||
new CompletionReceiptFileStore(receiptRoot),
|
||||
);
|
||||
assert.deepEqual(
|
||||
{ ...receipt, finishedAtMs: 100 },
|
||||
{
|
||||
schemaVersion: 1,
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
token: TOKEN,
|
||||
startedAtMs: 100,
|
||||
finishedAtMs: 100,
|
||||
exitCode: 0,
|
||||
},
|
||||
);
|
||||
assert.equal(receipt.finishedAtMs >= 100, true);
|
||||
});
|
||||
|
||||
test('journal failure prevents spawn and filesystem side effects from user code', async (t) => {
|
||||
const { directory, receiptRoot } = fixture(t);
|
||||
const marker = path.join(directory, 'spawned');
|
||||
const launcher = new LocalProcessLauncher(
|
||||
{
|
||||
async register() {
|
||||
throw new Error('database unavailable');
|
||||
},
|
||||
},
|
||||
{ receiptRoot, identityProvider: identityProvider() },
|
||||
);
|
||||
await assert.rejects(
|
||||
launcher.start({
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
callbackToken: TOKEN,
|
||||
command: { kind: 'argv', file: '/usr/bin/touch', args: [marker] },
|
||||
}),
|
||||
LocalProcessLaunchError,
|
||||
);
|
||||
assert.equal(fs.existsSync(marker), false);
|
||||
});
|
||||
|
||||
test('a modified launcher is rejected before durable registration', async (t) => {
|
||||
const { directory, receiptRoot } = fixture(t);
|
||||
const launcherPath = path.join(directory, 'launcher.sh');
|
||||
fs.writeFileSync(launcherPath, '#!/bin/sh\nexit 0\n', { mode: 0o700 });
|
||||
let registered = false;
|
||||
const launcher = new LocalProcessLauncher(
|
||||
{
|
||||
async register() {
|
||||
registered = true;
|
||||
},
|
||||
},
|
||||
{ receiptRoot, launcherPath, identityProvider: identityProvider() },
|
||||
);
|
||||
await assert.rejects(
|
||||
launcher.start({
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
callbackToken: TOKEN,
|
||||
command: { kind: 'argv', file: '/usr/bin/true', args: [] },
|
||||
}),
|
||||
/digest does not match review/,
|
||||
);
|
||||
assert.equal(registered, false);
|
||||
});
|
||||
|
||||
test('executes the verified launcher fd when its path is replaced after registration', async (t) => {
|
||||
const { directory, receiptRoot } = fixture(t);
|
||||
const launcherPath = path.join(directory, 'launcher.sh');
|
||||
fs.copyFileSync(
|
||||
path.resolve(__dirname, '../assets/ql3-launcher.sh'),
|
||||
launcherPath,
|
||||
);
|
||||
const replacementPath = path.join(directory, 'replacement.sh');
|
||||
const launcher = new LocalProcessLauncher(
|
||||
{
|
||||
async register() {
|
||||
fs.writeFileSync(replacementPath, '#!/bin/sh\nexit 99\n', {
|
||||
mode: 0o700,
|
||||
});
|
||||
fs.renameSync(replacementPath, launcherPath);
|
||||
},
|
||||
},
|
||||
{
|
||||
receiptRoot,
|
||||
launcherPath,
|
||||
clock: { now: () => 100 },
|
||||
identityProvider: identityProvider(),
|
||||
},
|
||||
);
|
||||
|
||||
const handle = await launcher.start({
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
callbackToken: TOKEN,
|
||||
command: { kind: 'argv', file: '/usr/bin/true', args: [] },
|
||||
});
|
||||
|
||||
assert.deepEqual(await handle.completion, { exitCode: 0, signal: null });
|
||||
assert.equal(
|
||||
(await waitForReceipt(new CompletionReceiptFileStore(receiptRoot)))
|
||||
.exitCode,
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
test('hard-caps durable output and publishes an immutable truncation fact', async (t) => {
|
||||
const { directory, receiptRoot } = fixture(t);
|
||||
const logArtifactId = `local-${'b'.repeat(30)}`;
|
||||
const outputDirectory = path.join(directory, 'artifacts', 'bb');
|
||||
const outputFilePath = path.join(outputDirectory, `${logArtifactId}.log`);
|
||||
const maximumBytes = 64 * 1024;
|
||||
const launcher = new LocalProcessLauncher(
|
||||
{ register: async () => undefined },
|
||||
{
|
||||
receiptRoot,
|
||||
clock: { now: () => 100 },
|
||||
identityProvider: identityProvider(),
|
||||
},
|
||||
);
|
||||
const handle = await launcher.start({
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
callbackSequence: 1,
|
||||
callbackToken: TOKEN,
|
||||
command: {
|
||||
kind: 'argv',
|
||||
file: process.execPath,
|
||||
args: [
|
||||
'-e',
|
||||
[
|
||||
'if (process.env.QL3_OUTPUT_QUOTA_FIFO) process.exit(91);',
|
||||
'if (process.env.QL3_OUTPUT_TRUNCATION_TARGET) process.exit(92);',
|
||||
'process.stdout.write(Buffer.alloc(256 * 1024, 0x61), () => process.exit(0));',
|
||||
].join(''),
|
||||
],
|
||||
},
|
||||
output: {
|
||||
filePath: outputFilePath,
|
||||
maximumBytes,
|
||||
logArtifactId,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(await handle.completion, { exitCode: 0, signal: null });
|
||||
assert.equal(fs.statSync(outputFilePath).size, maximumBytes);
|
||||
const factPath = path.join(
|
||||
outputDirectory,
|
||||
`.${logArtifactId}.log.truncated.json`,
|
||||
);
|
||||
const fact = JSON.parse(fs.readFileSync(factPath, 'utf8'));
|
||||
assert.deepEqual(
|
||||
{
|
||||
schemaVersion: fact.schemaVersion,
|
||||
runId: fact.runId,
|
||||
attemptId: fact.attemptId,
|
||||
logArtifactId: fact.logArtifactId,
|
||||
maximumBytes: fact.maximumBytes,
|
||||
quotaReached: fact.quotaReached,
|
||||
},
|
||||
{
|
||||
schemaVersion: 1,
|
||||
runId: RUN_ID,
|
||||
attemptId: ATTEMPT_ID,
|
||||
logArtifactId,
|
||||
maximumBytes,
|
||||
quotaReached: true,
|
||||
},
|
||||
);
|
||||
assert.equal(Number.isSafeInteger(fact.observedAtMs), true);
|
||||
assert.deepEqual(
|
||||
fs.readdirSync(outputDirectory).filter((name) => name.endsWith('.fifo')),
|
||||
[],
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user