mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 10:32:40 +08:00
feat(ql3): add profile-aware run log reads
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs/promises');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
LocalRunAttemptLogRangeReadError,
|
||||
LocalRunAttemptLogRangeReader,
|
||||
} = require('../dist/artifact-read/localRunAttemptLogRangeReader.js');
|
||||
|
||||
const artifactId = `local-${'a'.repeat(30)}`;
|
||||
const identity = Object.freeze({
|
||||
projectId: 'prj_default',
|
||||
runId: 'run_123',
|
||||
attemptId: 'attempt_123',
|
||||
logArtifactId: artifactId,
|
||||
});
|
||||
|
||||
async function fixture(t, content = Buffer.from('0123456789')) {
|
||||
const parent = await fs.mkdtemp(path.join(os.tmpdir(), 'ql3-log-read-'));
|
||||
t.after(() => fs.rm(parent, { recursive: true, force: true }));
|
||||
const root = path.join(parent, 'artifacts');
|
||||
const shard = path.join(root, 'aa');
|
||||
await fs.mkdir(shard, { recursive: true, mode: 0o700 });
|
||||
await fs.chmod(root, 0o700);
|
||||
await fs.chmod(shard, 0o700);
|
||||
const log = path.join(shard, `${artifactId}.log`);
|
||||
await fs.writeFile(log, content, { mode: 0o600 });
|
||||
await fs.chmod(log, 0o600);
|
||||
return { parent, root, shard, log };
|
||||
}
|
||||
|
||||
async function fact(shard, overrides = {}) {
|
||||
const file = path.join(shard, `.${artifactId}.log.truncated.json`);
|
||||
await fs.writeFile(
|
||||
file,
|
||||
JSON.stringify({
|
||||
schemaVersion: 1,
|
||||
runId: identity.runId,
|
||||
attemptId: identity.attemptId,
|
||||
logArtifactId: identity.logArtifactId,
|
||||
maximumBytes: 64 * 1024,
|
||||
quotaReached: false,
|
||||
observedAtMs: 9,
|
||||
...overrides,
|
||||
}),
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
await fs.chmod(file, 0o600);
|
||||
return file;
|
||||
}
|
||||
|
||||
test('reads one bounded private-file snapshot and canonical truncation fact', async (t) => {
|
||||
const value = await fixture(t);
|
||||
await fact(value.shard, { quotaReached: true });
|
||||
const result = await new LocalRunAttemptLogRangeReader(value.root).read(
|
||||
identity,
|
||||
{ offset: 2, length: 4 },
|
||||
);
|
||||
assert.equal(result.status, 'available');
|
||||
assert.equal(Buffer.from(result.content).toString(), '2345');
|
||||
assert.deepEqual(
|
||||
{
|
||||
start: result.start,
|
||||
endExclusive: result.endExclusive,
|
||||
totalBytes: result.totalBytes,
|
||||
nextOffset: result.nextOffset,
|
||||
truncation: result.truncation,
|
||||
},
|
||||
{
|
||||
start: 2,
|
||||
endExclusive: 6,
|
||||
totalBytes: 10,
|
||||
nextOffset: 6,
|
||||
truncation: {
|
||||
truncated: true,
|
||||
maximumBytes: 64 * 1024,
|
||||
observedAtMs: 9,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('returns unknown truncation and a stable empty range beyond the snapshot', async (t) => {
|
||||
const value = await fixture(t);
|
||||
const result = await new LocalRunAttemptLogRangeReader(value.root).read(
|
||||
identity,
|
||||
{ offset: 99, length: 4 },
|
||||
);
|
||||
assert.equal(result.status, 'available');
|
||||
assert.equal(result.content.byteLength, 0);
|
||||
assert.equal(result.start, 10);
|
||||
assert.equal(result.endExclusive, 10);
|
||||
assert.equal(result.totalBytes, 10);
|
||||
assert.equal(result.nextOffset, undefined);
|
||||
assert.deepEqual(result.truncation, { truncated: 'unknown' });
|
||||
});
|
||||
|
||||
test('treats absent root, shard and log as missing', async (t) => {
|
||||
const parent = await fs.mkdtemp(path.join(os.tmpdir(), 'ql3-log-missing-'));
|
||||
t.after(() => fs.rm(parent, { recursive: true, force: true }));
|
||||
const root = path.join(parent, 'artifacts');
|
||||
const reader = new LocalRunAttemptLogRangeReader(root);
|
||||
assert.deepEqual(await reader.read(identity, { offset: 0, length: 1 }), {
|
||||
status: 'missing',
|
||||
});
|
||||
await fs.mkdir(root, { mode: 0o700 });
|
||||
assert.deepEqual(await reader.read(identity, { offset: 0, length: 1 }), {
|
||||
status: 'missing',
|
||||
});
|
||||
const shard = path.join(root, 'aa');
|
||||
await fs.mkdir(shard, { mode: 0o700 });
|
||||
assert.deepEqual(await reader.read(identity, { offset: 0, length: 1 }), {
|
||||
status: 'missing',
|
||||
});
|
||||
});
|
||||
|
||||
test('fails closed for symlink targets and widened file permissions', async (t) => {
|
||||
const symlink = await fixture(t);
|
||||
await fs.rm(symlink.log);
|
||||
await fs.symlink(path.join(symlink.parent, 'outside'), symlink.log);
|
||||
await assert.rejects(
|
||||
new LocalRunAttemptLogRangeReader(symlink.root).read(identity, {
|
||||
offset: 0,
|
||||
length: 1,
|
||||
}),
|
||||
LocalRunAttemptLogRangeReadError,
|
||||
);
|
||||
|
||||
const widened = await fixture(t);
|
||||
await fs.chmod(widened.log, 0o644);
|
||||
await assert.rejects(
|
||||
new LocalRunAttemptLogRangeReader(widened.root).read(identity, {
|
||||
offset: 0,
|
||||
length: 1,
|
||||
}),
|
||||
LocalRunAttemptLogRangeReadError,
|
||||
);
|
||||
});
|
||||
|
||||
test('fails closed for truncation identity drift and an aborted request', async (t) => {
|
||||
const value = await fixture(t);
|
||||
await fact(value.shard, { attemptId: 'attempt_other' });
|
||||
const reader = new LocalRunAttemptLogRangeReader(value.root);
|
||||
await assert.rejects(
|
||||
reader.read(identity, { offset: 0, length: 1 }),
|
||||
(error) =>
|
||||
error instanceof LocalRunAttemptLogRangeReadError &&
|
||||
error.reason === 'integrity_mismatch',
|
||||
);
|
||||
const abort = new AbortController();
|
||||
abort.abort(new Error('cancelled'));
|
||||
await assert.rejects(
|
||||
reader.read(identity, { offset: 0, length: 1 }, abort.signal),
|
||||
/cancelled/,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user