Files
qinglong/packages/ql3-local-execution/test/localRunAttemptLogRetirementStore.test.cjs

132 lines
4.0 KiB
JavaScript

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 {
LocalRunAttemptLogCapacityProbe,
LocalRunAttemptLogRetirementError,
LocalRunAttemptLogRetirementStore,
} = require('../dist/artifact-read/localRunAttemptLogRetirementStore.js');
const ARTIFACT_ID = `local-${'a'.repeat(30)}`;
function fixture(t) {
const parent = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-log-retire-'));
const root = path.join(parent, 'artifacts');
fs.mkdirSync(root, { mode: 0o700 });
fs.chmodSync(root, 0o700);
const directory = path.join(root, 'aa');
fs.mkdirSync(directory, { mode: 0o700 });
fs.chmodSync(directory, 0o700);
t.after(() => fs.rmSync(parent, { recursive: true, force: true }));
return {
parent,
root,
directory,
target: path.join(directory, `${ARTIFACT_ID}.log`),
fact: path.join(directory, `.${ARTIFACT_ID}.log.truncated.json`),
};
}
function candidate() {
return {
projectId: 'prj_default',
runId: 'run_1',
attemptId: 'attempt_1',
logArtifactId: ARTIFACT_ID,
executorType: 'local_process',
finishedAtMs: 1,
};
}
function privateFile(filePath, content) {
fs.writeFileSync(filePath, content, { mode: 0o600 });
fs.chmodSync(filePath, 0o600);
}
function fact() {
return JSON.stringify({
schemaVersion: 1,
runId: 'run_1',
attemptId: 'attempt_1',
logArtifactId: ARTIFACT_ID,
maximumBytes: 64 * 1024,
quotaReached: true,
observedAtMs: 2,
});
}
test('deletes only the exact private log and truncation fact', async (t) => {
const value = fixture(t);
privateFile(value.target, 'hello');
privateFile(value.fact, fact());
privateFile(path.join(value.directory, 'unrelated'), 'keep');
const retired = await new LocalRunAttemptLogRetirementStore(
value.root,
).retire(candidate());
assert.deepEqual(retired, {
disposition: 'deleted',
byteLength: 5,
truncation: {
truncated: true,
maximumBytes: 64 * 1024,
observedAtMs: 2,
},
});
assert.equal(fs.existsSync(value.target), false);
assert.equal(fs.existsSync(value.fact), false);
assert.equal(fs.existsSync(path.join(value.directory, 'unrelated')), true);
});
test('converges an unlink-before-tombstone crash and removes its orphan fact', async (t) => {
const value = fixture(t);
privateFile(value.fact, fact());
const retired = await new LocalRunAttemptLogRetirementStore(
value.root,
).retire(candidate());
assert.equal(retired.disposition, 'already_absent');
assert.equal(retired.byteLength, 0);
assert.equal(retired.truncation.truncated, true);
assert.equal(fs.existsSync(value.fact), false);
});
test('fails closed for links, unsafe modes and fact identity drift', async (t) => {
const cases = [];
const hardLink = fixture(t);
privateFile(hardLink.target, 'hello');
fs.linkSync(hardLink.target, path.join(hardLink.directory, 'second-link'));
cases.push(hardLink);
const unsafeMode = fixture(t);
privateFile(unsafeMode.target, 'hello');
fs.chmodSync(unsafeMode.target, 0o644);
cases.push(unsafeMode);
const drift = fixture(t);
privateFile(drift.target, 'hello');
privateFile(drift.fact, fact().replace('"attempt_1"', '"attempt_other"'));
cases.push(drift);
for (const value of cases) {
await assert.rejects(
new LocalRunAttemptLogRetirementStore(value.root).retire(candidate()),
LocalRunAttemptLogRetirementError,
);
assert.equal(fs.existsSync(value.target), true);
}
});
test('capacity probe uses the nearest existing parent without creating roots', async (t) => {
const value = fixture(t);
const missing = path.join(value.parent, 'future', 'artifacts');
const snapshot = await new LocalRunAttemptLogCapacityProbe(missing).inspect();
assert.equal(snapshot.totalBytes > 0n, true);
assert.equal(snapshot.availableBytes >= 0n, true);
assert.equal(snapshot.availableBytes <= snapshot.totalBytes, true);
assert.equal(fs.existsSync(missing), false);
});