Files
stack/packages/runs/tests/runs.test.mjs
T
jason.woltjeandClaude Opus 5.5 71d874764a feat(runs): read-only runs and releases reader module (#1545, row 56)
New packages/runs reads run records, result.json, the release pointer
and the activation log without writing, pruning or following a link out
of the data root. mosaic-task.mjs list and show use it, so a malformed
result.json or a run that is a regular file no longer crashes them.
Deliberate deltas are listed in the README.

Rocko built it. Round 1 (ed3c5392) was approved with notes by Darkwing
(27115) and Filbert (27116); round 2 (2727198f, tests and wording only)
was approved by Darkwing (27123) and Filbert (27124). Sage's gate on
c9a25a47 plus the candidate: runs 41/0, queue 148/0, webui 22/0,
conversation 182/0 (181/1 in the full run on the K12 cgroup timing
test under load, 182/0 alone), control-board 124/0, every
scripts/test-*.sh 0 failed (task 98/0 with Docker, 26/0 without).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:22:37 -05:00

244 lines
11 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import {
RunsError, isRunId, listRunIds, listRunRecords, readRunDocument, readRunRecord,
} from "../src/index.mjs";
import { RESULT, scratch, tree, write } from "./helpers.mjs";
const A = "r-20260101T000000Z-aaaaaa";
const B = "r-20260101T000100Z-bbbbbb";
test("isRunId accepts the run id shape and nothing else", () => {
for (const id of [A, "r-x", "r-zz.weird_name-1", `r-${"a".repeat(64)}`]) assert.equal(isRunId(id), true, id);
for (const id of ["r-", "r-.x", "r-_x", `r-${"a".repeat(65)}`, "x-1", "r-a/b", "../r-a", "r-a b", 1, null, undefined]) {
assert.equal(isRunId(id), false, String(id));
}
});
test("a missing data root or runs directory lists nothing", (t) => {
const { dir, dataRoot } = scratch(t);
assert.deepEqual(listRunIds(path.join(dir, "absent")), []);
assert.deepEqual(listRunIds(dataRoot), []);
assert.deepEqual(listRunRecords(dataRoot), []);
});
test("runs as a regular file lists nothing, as before", (t) => {
const { dataRoot } = scratch(t);
write(path.join(dataRoot, "runs"), "not a directory\n");
assert.deepEqual(listRunIds(dataRoot), []);
});
test("listRunIds keeps r- names only, sorted oldest first", (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
for (const name of [B, A, "x-other", "r-zz"]) fs.mkdirSync(path.join(runs, name), { recursive: true });
write(path.join(runs, ".pruned.log"), "{}\n");
write(path.join(runs, "r-a-file"), "x\n");
assert.deepEqual(listRunIds(dataRoot), [A, B, "r-a-file", "r-zz"]);
});
test("listRunRecords returns each result, or null for an incomplete or unreadable one", (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
write(path.join(runs, A, "result.json"), RESULT);
write(path.join(runs, B, "task.json"), { id: "t" });
write(path.join(runs, "r-c", "result.json"), "{ truncated");
write(path.join(runs, "r-d", "result.json"), "null\n");
write(path.join(runs, "r-e", "result.json"), "5\n");
write(path.join(runs, "r-f", "result.json"), "[1]\n");
fs.mkdirSync(path.join(runs, "r-g", "result.json"), { recursive: true });
write(path.join(runs, "r-h"), "a file\n");
const records = listRunRecords(dataRoot);
assert.deepEqual(records.map((r) => r.runId), [A, B, "r-c", "r-d", "r-e", "r-f", "r-g", "r-h"]);
assert.deepEqual(records[0].result, RESULT);
for (const record of records.slice(1)) assert.equal(record.result, null, record.runId);
});
test("readRunRecord returns documents and artifacts in directory order", (t) => {
const { dataRoot } = scratch(t);
const dir = path.join(dataRoot, "runs", A);
write(path.join(dir, "result.json"), RESULT);
write(path.join(dir, "task.json"), { taskVersion: 1, id: "t-one" });
write(path.join(dir, "mission.json"), { id: "m", objective: "o" });
write(path.join(dir, "stderr.txt"), "");
fs.mkdirSync(path.join(dir, "workspace"));
const record = readRunRecord(dataRoot, A);
assert.equal(record.runId, A);
assert.deepEqual(record.result, RESULT);
assert.deepEqual(record.task, { taskVersion: 1, id: "t-one" });
assert.deepEqual(record.mission, { id: "m", objective: "o" });
assert.deepEqual(record.artifacts, fs.readdirSync(dir));
});
test("readRunRecord is null for a missing run, a dangling link or a file", (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
assert.equal(readRunRecord(dataRoot, A), null);
fs.mkdirSync(runs);
fs.symlinkSync("r-nowhere", path.join(runs, A));
write(path.join(runs, B), "a file\n");
assert.equal(readRunRecord(dataRoot, A), null);
assert.equal(readRunRecord(dataRoot, B), null);
});
test("readRunRecord and readRunDocument refuse an invalid run id before touching the disk", (t) => {
const { dataRoot } = scratch(t);
for (const id of ["../data", "r-a/../../x", "", "r-"]) {
assert.throws(() => readRunRecord(dataRoot, id), (e) => e instanceof RunsError && e.exitCode === 4 && /invalid run id/.test(e.message));
assert.throws(() => readRunDocument(dataRoot, id, "result.json"), RunsError);
}
});
test("readRunDocument reads only the three run documents", (t) => {
const { dataRoot } = scratch(t);
write(path.join(dataRoot, "runs", A, "stderr.txt"), "{}\n");
assert.throws(() => readRunDocument(dataRoot, A, "stderr.txt"), /unknown run document/);
assert.throws(() => readRunDocument(dataRoot, A, "../../x.json"), /unknown run document/);
assert.equal(readRunDocument(dataRoot, A, "task.json"), null);
});
test("a link inside the data root is followed", (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
write(path.join(runs, A, "result.json"), RESULT);
fs.symlinkSync(A, path.join(runs, B));
assert.deepEqual(readRunRecord(dataRoot, B).result, RESULT);
assert.deepEqual(listRunRecords(dataRoot)[1], { runId: B, result: RESULT });
});
test("a data root that is itself a link is trusted as configured", (t) => {
const { dir, dataRoot } = scratch(t);
write(path.join(dataRoot, "runs", A, "result.json"), RESULT);
const alias = path.join(dir, "alias");
fs.symlinkSync(dataRoot, alias);
assert.deepEqual(listRunRecords(alias), [{ runId: A, result: RESULT }]);
});
test("a run directory linked out of the data root is never read", (t) => {
const { dataRoot, outside } = scratch(t);
write(path.join(outside, "result.json"), RESULT);
write(path.join(outside, "task.json"), { id: "t" });
fs.mkdirSync(path.join(dataRoot, "runs"));
fs.symlinkSync(outside, path.join(dataRoot, "runs", A));
assert.deepEqual(listRunRecords(dataRoot), [{ runId: A, result: null }]);
assert.throws(() => readRunRecord(dataRoot, A), (e) => e instanceof RunsError && e.exitCode === 4 && /resolves outside the data root/.test(e.message));
});
test("a document linked out of the data root reads as null", (t) => {
const { dataRoot, outside } = scratch(t);
write(path.join(outside, "secret.json"), RESULT);
fs.mkdirSync(path.join(dataRoot, "runs", A), { recursive: true });
for (const name of ["result.json", "task.json", "mission.json"]) {
fs.symlinkSync(path.join(outside, "secret.json"), path.join(dataRoot, "runs", A, name));
}
const record = readRunRecord(dataRoot, A);
assert.equal(record.result, null);
assert.equal(record.task, null);
assert.equal(record.mission, null);
assert.deepEqual(listRunRecords(dataRoot), [{ runId: A, result: null }]);
});
test("a runs directory linked out of the data root refuses", (t) => {
const { dataRoot, outside } = scratch(t);
write(path.join(outside, A, "result.json"), RESULT);
fs.symlinkSync(outside, path.join(dataRoot, "runs"));
assert.throws(() => listRunIds(dataRoot), /runs resolves outside the data root/);
assert.throws(() => listRunRecords(dataRoot), RunsError);
assert.throws(() => readRunRecord(dataRoot, A), /runs resolves outside the data root/);
});
test("a relative link that climbs out of the data root refuses", (t) => {
const { dataRoot } = scratch(t);
fs.mkdirSync(path.join(dataRoot, "runs"));
fs.symlinkSync("../../outside", path.join(dataRoot, "runs", A));
assert.throws(() => readRunRecord(dataRoot, A), /resolves outside the data root/);
});
test("a sibling whose name starts with the data root's name is outside it", (t) => {
const { dir, dataRoot } = scratch(t);
const sibling = path.join(dir, "data-sibling");
write(path.join(sibling, "result.json"), RESULT);
fs.mkdirSync(path.join(dataRoot, "runs"));
fs.symlinkSync(sibling, path.join(dataRoot, "runs", A));
assert.throws(() => readRunRecord(dataRoot, A), /resolves outside the data root/);
});
test("a link loop refuses instead of reading as missing", (t) => {
const { dataRoot } = scratch(t);
fs.mkdirSync(path.join(dataRoot, "runs"));
fs.symlinkSync(B, path.join(dataRoot, "runs", A));
fs.symlinkSync(A, path.join(dataRoot, "runs", B));
assert.throws(() => readRunRecord(dataRoot, A), (e) => e instanceof RunsError && /ELOOP/.test(e.message));
assert.deepEqual(listRunRecords(dataRoot), [{ runId: A, result: null }, { runId: B, result: null }]);
});
test("an unreadable runs directory refuses instead of listing nothing", { skip: process.getuid?.() === 0 && "root reads anything" }, (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
fs.mkdirSync(path.join(runs, A), { recursive: true });
fs.chmodSync(runs, 0o000);
try {
assert.throws(() => listRunIds(dataRoot), (e) => e instanceof RunsError && /EACCES/.test(e.message));
} finally {
fs.chmodSync(runs, 0o755);
}
});
test("an unreadable run directory refuses instead of reading as missing", { skip: process.getuid?.() === 0 && "root reads anything" }, (t) => {
const { dataRoot } = scratch(t);
const run = path.join(dataRoot, "runs", A);
write(path.join(run, "result.json"), RESULT);
fs.chmodSync(run, 0o000);
try {
assert.throws(() => readRunRecord(dataRoot, A), (e) => e instanceof RunsError && /EACCES/.test(e.message));
} finally {
fs.chmodSync(run, 0o755);
}
});
test("a link to the data root's parent is outside it", (t) => {
const { dataRoot } = scratch(t);
fs.mkdirSync(path.join(dataRoot, "runs"));
fs.symlinkSync("../..", path.join(dataRoot, "runs", A));
assert.throws(() => readRunRecord(dataRoot, A), /resolves outside the data root/);
});
// readdir order is the filesystem's; node may already return it sorted, so
// the directory listing is mocked to come back reversed.
test("listRunIds sorts whatever order the directory returns", (t) => {
const { dataRoot } = scratch(t);
for (const id of [A, B]) fs.mkdirSync(path.join(dataRoot, "runs", id), { recursive: true });
const readdirSync = fs.readdirSync;
t.mock.method(fs, "readdirSync", (...args) => readdirSync(...args).sort().reverse());
assert.deepEqual(fs.readdirSync(path.join(dataRoot, "runs")), [B, A]);
assert.deepEqual(listRunIds(dataRoot), [A, B]);
});
// list has always shown any r- name, including ones show refuses as ids.
test("listRunRecords lists an r- name that isn't a valid run id, as before", (t) => {
const { dataRoot } = scratch(t);
write(path.join(dataRoot, "runs", "r-.bad", "result.json"), {});
assert.equal(isRunId("r-.bad"), false);
assert.deepEqual(listRunIds(dataRoot), ["r-.bad"]);
assert.deepEqual(listRunRecords(dataRoot), [{ runId: "r-.bad", result: {} }]);
});
test("the readers write nothing", (t) => {
const { dataRoot } = scratch(t);
const runs = path.join(dataRoot, "runs");
write(path.join(runs, A, "result.json"), RESULT);
write(path.join(runs, A, "task.json"), { id: "t" });
write(path.join(runs, B, "result.json"), "{ bad");
fs.symlinkSync(A, path.join(runs, "r-link"));
const before = tree(dataRoot);
listRunIds(dataRoot);
listRunRecords(dataRoot);
readRunRecord(dataRoot, A);
readRunRecord(dataRoot, B);
readRunRecord(dataRoot, "r-absent");
readRunDocument(dataRoot, A, "mission.json");
assert.equal(tree(dataRoot), before);
});