review(56): Darkwing round 1 packet, approve (#1545 comment 27115)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
1afe07062349b097b955f0391bffb839b083492d02644421def6c39cc19ff4a4 agents/rocko/work/queue-56/byte-check.sh
|
||||
96416ea2eb84490b79e98b9aa0f95673e76070f15ba2876f83dbf1bdaa919f97 agents/rocko/work/queue-56/delta-check.sh
|
||||
15d575cc45313906114f7edda9379b66af7624d9e894bb98812deb7acf5d87ba agents/rocko/work/queue-56/seed.sh
|
||||
146b236a37bf665979e28f1794c4c4437b6e85feb43b892e8d9d697506fc453f packages/runs/package.json
|
||||
594eb42518f73c94608320ce381868026aca69d8f5104711c9685ebdd8df41c9 packages/runs/README.md
|
||||
f686212fa10e8541a1d8055fe30d65558a516245276aa53ecefd97bd7ca0a20c packages/runs/src/errors.mjs
|
||||
eb7063a8ee0417699b45f4bfec69c3ffcde8b3b276956ca4fea266ec0abd6b23 packages/runs/src/index.mjs
|
||||
3f9d025f0615a08edd440bb2aa6cea48f21cafa1f680332fffca3c7ee586cc65 packages/runs/src/paths.mjs
|
||||
ba64e50c3a63e693fc313278ff6cf440234649a6719dd9229bce20272a518ec1 packages/runs/src/runs.mjs
|
||||
c013ed1912bb97cbfb5209170741d9a20c2955a9acbd1fff9e397fac1fab742a packages/runs/src/state.mjs
|
||||
7c941596020b14d8627ccb09b66e0e51195318e86add60eba9d2c4409e6f8dd6 packages/runs/tests/helpers.mjs
|
||||
4125ffc8787f3cd9441bc2a557f42fde6948b884b8b870b503acef6eee6b3913 packages/runs/tests/runs.test.mjs
|
||||
7857d2c8c94011df328dbf5013626dbcdd577945781d104dff533f08b1859130 packages/runs/tests/state.test.mjs
|
||||
6bf3e758c191e23fddec6bcf30d881cdda1bb37d4e3ed1cd4ef47be79fb36bd8 packages/runs/tests/task-cli.test.mjs
|
||||
4873187609897c643e62acf7f1d074fa3c03369fe6702b9b0c7776138e776401 scripts/mosaic-task.mjs
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# Row 56 gate: packages/runs node suite, then every scripts/test-*.sh with Docker unreachable.
|
||||
export TMPDIR=~/darkwing-scratch/r56a/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
cd ~/darkwing-scratch/r56a/cand
|
||||
O=~/darkwing-scratch/r56a/out
|
||||
: > $O/summary.txt
|
||||
echo "start $(date -u +%FT%TZ)" >> $O/summary.txt
|
||||
env -u NODE_TEST_CONTEXT node --test packages/runs/tests/*.test.mjs > $O/node-runs.txt 2>&1; e=$?
|
||||
echo "node-runs exit=$e $(grep -E '^ℹ (pass|fail)' $O/node-runs.txt | tr '\n' ' ')" >> $O/summary.txt
|
||||
for f in scripts/test-*.sh; do
|
||||
s=$(basename $f .sh)
|
||||
$f > $O/$s.txt 2>&1; e=$?
|
||||
echo "$s exit=$e $(grep -E 'passed, [0-9]+ failed' $O/$s.txt | tail -1)" >> $O/summary.txt
|
||||
done
|
||||
echo "end $(date -u +%FT%TZ)" >> $O/summary.txt
|
||||
echo DONE >> $O/summary.txt
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/scripts/mosaic-task.mjs b/scripts/mosaic-task.mjs
|
||||
index cf8399a0..b5a9564e 100755
|
||||
--- a/scripts/mosaic-task.mjs
|
||||
+++ b/scripts/mosaic-task.mjs
|
||||
@@ -486,7 +486,7 @@ function listRuns() {
|
||||
}
|
||||
|
||||
function showRun(runId) {
|
||||
- if (!isRunId(runId)) {
|
||||
+ if (false) {
|
||||
fail(4, `invalid run id: ${JSON.stringify(runId)} (expected r-<id>)`);
|
||||
}
|
||||
const resolved = loadConfig();
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.129303ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.363755ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.81288ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.872847ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.892326ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.545032ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.947125ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.861621ms)
|
||||
✔ readRunDocument reads only the three run documents (0.744194ms)
|
||||
✔ a link inside the data root is followed (2.213785ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.840807ms)
|
||||
✔ a run directory linked out of the data root is never read (0.832753ms)
|
||||
✔ a document linked out of the data root reads as null (1.088125ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.797926ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.741627ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.847544ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.021958ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.767933ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.687222ms)
|
||||
✔ a link to the data root's parent is outside it (0.483537ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.065985ms)
|
||||
✔ the readers write nothing (3.274403ms)
|
||||
✔ no pointer is null (2.908017ms)
|
||||
✔ the pointer release.sh writes reads back (1.898894ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.141146ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.931434ms)
|
||||
✔ a state file linked out of the data root refuses (2.22612ms)
|
||||
✔ a state directory linked out of the data root refuses (0.984988ms)
|
||||
✔ a missing log is empty (0.69135ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.524874ms)
|
||||
✔ last must be a positive integer (1.101485ms)
|
||||
✔ the state readers write nothing (1.595899ms)
|
||||
✔ list prints each run in the established format (102.108158ms)
|
||||
✔ show prints the run in the established format (98.414356ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (161.801585ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (218.443475ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (197.170014ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 845.97083
|
||||
@@ -0,0 +1,14 @@
|
||||
diff --git a/packages/runs/src/paths.mjs b/packages/runs/src/paths.mjs
|
||||
index 1dbb5539..f7c5f4ea 100644
|
||||
--- a/packages/runs/src/paths.mjs
|
||||
+++ b/packages/runs/src/paths.mjs
|
||||
@@ -43,7 +43,8 @@ export function readJsonObject(dataRoot, ...parts) {
|
||||
if (file === null) return null;
|
||||
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
||||
- } catch {
|
||||
+ } catch (error) {
|
||||
+ if (error instanceof RunsError) throw error;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.956544ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.725076ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.059244ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.387509ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.768494ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.559464ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.211241ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.141838ms)
|
||||
✔ readRunDocument reads only the three run documents (0.910973ms)
|
||||
✔ a link inside the data root is followed (1.969226ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.360016ms)
|
||||
✖ a run directory linked out of the data root is never read (2.59955ms)
|
||||
✖ a document linked out of the data root reads as null (1.129541ms)
|
||||
✔ a runs directory linked out of the data root refuses (2.924601ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.900903ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.987308ms)
|
||||
✖ a link loop refuses instead of reading as missing (1.102282ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.771326ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.015364ms)
|
||||
✔ a link to the data root's parent is outside it (2.042409ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.683733ms)
|
||||
✔ the readers write nothing (3.50432ms)
|
||||
✔ no pointer is null (3.237439ms)
|
||||
✔ the pointer release.sh writes reads back (2.002114ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.94932ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.676768ms)
|
||||
✔ a state file linked out of the data root refuses (1.657011ms)
|
||||
✔ a state directory linked out of the data root refuses (1.062717ms)
|
||||
✔ a missing log is empty (0.687978ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.731535ms)
|
||||
✔ last must be a positive integer (0.797464ms)
|
||||
✔ the state readers write nothing (1.515893ms)
|
||||
✔ list prints each run in the established format (142.248626ms)
|
||||
✔ show prints the run in the established format (102.945184ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (148.67361ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (192.921615ms)
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (213.400604ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 33
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 871.096655
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:119:1
|
||||
✖ a run directory linked out of the data root is never read (2.59955ms)
|
||||
Error [RunsError]: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-PvDpe8/data/runs/r-20260101T000000Z-aaaaaa/result.json resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-PvDpe8/data)
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:33:11)
|
||||
at readJsonObject (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:42:18)
|
||||
at file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:46:13
|
||||
at Array.map (<anonymous>)
|
||||
at listRunRecords (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:44:31)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:125:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:129:1
|
||||
✖ a document linked out of the data root reads as null (1.129541ms)
|
||||
Error [RunsError]: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-WcZej3/data/runs/r-20260101T000000Z-aaaaaa/result.json resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-WcZej3/data)
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:33:11)
|
||||
at readJsonObject (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:42:18)
|
||||
at readRunDocument (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:37:10)
|
||||
at readRunRecord (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:67:13)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:136:18)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:168:1
|
||||
✖ a link loop refuses instead of reading as missing (1.102282ms)
|
||||
Error [RunsError]: cannot resolve /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-x1gAr0/data/runs/r-20260101T000000Z-aaaaaa/result.json: ELOOP
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:30:11)
|
||||
at readJsonObject (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:42:18)
|
||||
at file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:46:13
|
||||
at Array.map (<anonymous>)
|
||||
at listRunRecords (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:44:31)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:174:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:82:1
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (213.400604ms)
|
||||
AssertionError [ERR_ASSERTION]: mosaic-task: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-223gdI/data/runs/r-20260101T000000Z-aaaaaa/result.json resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-223gdI/data)
|
||||
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:92:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..5096f921 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -52,7 +52,6 @@ export function listRunRecords(dataRoot) {
|
||||
export function readRunRecord(dataRoot, runId) {
|
||||
requireRunId(runId);
|
||||
// The runs directory first, so a refusal names the link that escapes.
|
||||
- if (resolveInside(dataRoot, RUNS_DIRNAME) === null) return null;
|
||||
const dir = resolveInside(dataRoot, RUNS_DIRNAME, runId);
|
||||
if (dir === null) return null;
|
||||
let artifacts;
|
||||
@@ -0,0 +1,99 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.68092ms)
|
||||
✔ a missing data root or runs directory lists nothing (3.048389ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.963654ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.872482ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.199887ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.277031ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.810683ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.290617ms)
|
||||
✔ readRunDocument reads only the three run documents (0.831152ms)
|
||||
✔ a link inside the data root is followed (1.288609ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.929194ms)
|
||||
✔ a run directory linked out of the data root is never read (1.220122ms)
|
||||
✔ a document linked out of the data root reads as null (1.495058ms)
|
||||
✖ a runs directory linked out of the data root refuses (1.812269ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.695909ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.712215ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.934066ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.655307ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.742938ms)
|
||||
✔ a link to the data root's parent is outside it (0.576201ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.174841ms)
|
||||
✔ the readers write nothing (2.73421ms)
|
||||
✔ no pointer is null (2.202006ms)
|
||||
✔ the pointer release.sh writes reads back (1.472039ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.816169ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.423693ms)
|
||||
✔ a state file linked out of the data root refuses (1.147291ms)
|
||||
✔ a state directory linked out of the data root refuses (0.647667ms)
|
||||
✔ a missing log is empty (1.318528ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.349691ms)
|
||||
✔ last must be a positive integer (0.767877ms)
|
||||
✔ the state readers write nothing (1.128232ms)
|
||||
✔ list prints each run in the established format (118.264972ms)
|
||||
✔ show prints the run in the established format (82.606742ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (129.149189ms)
|
||||
✖ list and show refuse a runs directory linked out of the data root (187.309472ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (222.898306ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 35
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 812.977588
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:143:1
|
||||
✖ a runs directory linked out of the data root refuses (1.812269ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /runs resolves outside the data root/. Input:
|
||||
|
||||
'RunsError: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-zX0oF7/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-zX0oF7/data)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:149:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: RunsError: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-zX0oF7/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-zX0oF7/data)
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:33:11)
|
||||
at readRunRecord (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:55:15)
|
||||
at file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:149:23
|
||||
at getActual (node:assert:580:5)
|
||||
at strict.throws (node:assert:728:24)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:149:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19),
|
||||
expected: /runs resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:70:1
|
||||
✖ list and show refuse a runs directory linked out of the data root (187.309472ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^mosaic-task: .*runs resolves outside the data root/. Input:
|
||||
|
||||
'mosaic-task: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-v5t5hA/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-v5t5hA/data)\n'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:78:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'mosaic-task: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-v5t5hA/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-v5t5hA/data)\n',
|
||||
expected: /^mosaic-task: .*runs resolves outside the data root/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..c32758bc 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -78,5 +78,5 @@ export function readActivationLog(dataRoot, { last } = {}) {
|
||||
if (entry === null) malformed += 1;
|
||||
else entries.push(entry);
|
||||
}
|
||||
- return { entries: last === undefined ? entries : entries.slice(-last), malformed };
|
||||
+ return { entries: last === undefined ? entries : entries.slice(0, last), malformed };
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.680565ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.794757ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.731147ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.864372ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.326643ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.783473ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.188716ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.093319ms)
|
||||
✔ readRunDocument reads only the three run documents (0.846733ms)
|
||||
✔ a link inside the data root is followed (1.17458ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.781634ms)
|
||||
✔ a run directory linked out of the data root is never read (0.777016ms)
|
||||
✔ a document linked out of the data root reads as null (1.278597ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.649479ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.502717ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.58152ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.775651ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.659139ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.013293ms)
|
||||
✔ a link to the data root's parent is outside it (0.653539ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.135619ms)
|
||||
✔ the readers write nothing (2.823246ms)
|
||||
✔ no pointer is null (2.918785ms)
|
||||
✔ the pointer release.sh writes reads back (1.860026ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.823183ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.736666ms)
|
||||
✔ a state file linked out of the data root refuses (1.677527ms)
|
||||
✔ a state directory linked out of the data root refuses (0.987759ms)
|
||||
✔ a missing log is empty (0.672303ms)
|
||||
✖ the log reads oldest first and counts malformed lines (3.332551ms)
|
||||
✔ last must be a positive integer (0.804321ms)
|
||||
✔ the state readers write nothing (2.095782ms)
|
||||
✔ list prints each run in the established format (98.447736ms)
|
||||
✔ show prints the run in the established format (94.546953ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (141.127685ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (186.311477ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (200.292033ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 810.059294
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:80:1
|
||||
✖ the log reads oldest first and counts malformed lines (3.332551ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
+ at: '2026-09-03T20:57:00Z',
|
||||
+ event: 'package',
|
||||
+ imageTag: 'mosaic-poc-agent:0.84.4-r0.0.12',
|
||||
+ release: '0.0.12'
|
||||
- at: '2026-09-03T20:57:50Z',
|
||||
- event: 'refused',
|
||||
- imageTag: 'mosaic-poc-agent:0.84.4-r0.0.11',
|
||||
- note: 'health check failed (fault-injected)',
|
||||
- release: '0.0.11'
|
||||
},
|
||||
{
|
||||
+ at: '2026-09-03T20:57:47Z',
|
||||
+ event: 'activate',
|
||||
+ imageTag: 'mosaic-poc-agent:0.84.4-r0.0.12',
|
||||
+ release: '0.0.12'
|
||||
- at: '2026-09-03T20:58:15Z',
|
||||
- event: 'rollback',
|
||||
- imageTag: 'mosaic-poc-agent:0.84.4-r0.0.11',
|
||||
- release: '0.0.11'
|
||||
}
|
||||
],
|
||||
malformed: 5
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:102:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { entries: [ [Object], [Object] ], malformed: 5 },
|
||||
expected: { entries: [ [Object], [Object] ], malformed: 5 },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..1002fb94 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -56,7 +56,6 @@ function logEntry(line) {
|
||||
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return null;
|
||||
if (Object.keys(entry).some((key) => !LOG_KEYS.includes(key))) return null;
|
||||
if (!["at", "event", "release", "imageTag"].every((key) => typeof entry[key] === "string")) return null;
|
||||
- if (entry.note !== undefined && typeof entry.note !== "string") return null;
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.849966ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.966701ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.044085ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.404037ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.671713ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.978123ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.320202ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.202015ms)
|
||||
✔ readRunDocument reads only the three run documents (0.982657ms)
|
||||
✔ a link inside the data root is followed (1.840354ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.271595ms)
|
||||
✔ a run directory linked out of the data root is never read (1.363834ms)
|
||||
✔ a document linked out of the data root reads as null (1.561635ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.727258ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.670099ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.641573ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.934577ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.850297ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.87724ms)
|
||||
✔ a link to the data root's parent is outside it (0.770198ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.036885ms)
|
||||
✔ the readers write nothing (3.831769ms)
|
||||
✔ no pointer is null (2.316454ms)
|
||||
✔ the pointer release.sh writes reads back (1.598365ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.175811ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.778144ms)
|
||||
✔ a state file linked out of the data root refuses (2.517371ms)
|
||||
✔ a state directory linked out of the data root refuses (0.870816ms)
|
||||
✔ a missing log is empty (0.611372ms)
|
||||
✖ the log reads oldest first and counts malformed lines (2.862969ms)
|
||||
✔ last must be a positive integer (0.64267ms)
|
||||
✔ the state readers write nothing (1.472105ms)
|
||||
✔ list prints each run in the established format (103.761545ms)
|
||||
✔ show prints the run in the established format (97.443115ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (161.288152ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (176.117228ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (238.782496ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 865.082532
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:80:1
|
||||
✖ the log reads oldest first and counts malformed lines (2.862969ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
... Skipped lines
|
||||
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
at: '2026-09-03T20:57:00Z',
|
||||
event: 'package',
|
||||
...
|
||||
{
|
||||
+ at: '2026-09-03T20:57:47Z',
|
||||
+ event: 'activate',
|
||||
+ imageTag: 'mosaic-poc-agent:0.84.4-r0.0.12',
|
||||
+ note: null,
|
||||
+ release: '0.0.12'
|
||||
+ },
|
||||
+ {
|
||||
at: '2026-09-03T20:57:50Z',
|
||||
event: 'refused',
|
||||
imageTag: 'mosaic-poc-agent:0.84.4-r0.0.11',
|
||||
note: 'health check failed (fault-injected)',
|
||||
release: '0.0.11'
|
||||
...
|
||||
],
|
||||
+ malformed: 4
|
||||
- malformed: 5
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:101:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { entries: [ [Object], [Object], [Object], [Object], [Object] ], malformed: 4 },
|
||||
expected: { entries: [ [Object], [Object], [Object], [Object] ], malformed: 5 },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..e02fea8e 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -23,7 +23,7 @@ export function listRunIds(dataRoot) {
|
||||
try {
|
||||
names = fs.readdirSync(root);
|
||||
} catch (error) {
|
||||
- if (isMissing(error)) return [];
|
||||
+ return [];
|
||||
throw new RunsError(`cannot read ${root}: ${error.code ?? error.message}`);
|
||||
}
|
||||
return names.filter((name) => name.startsWith("r-")).sort();
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.74403ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.198048ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.749386ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.943488ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (4.593534ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (2.041033ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.316339ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.130767ms)
|
||||
✔ readRunDocument reads only the three run documents (0.938903ms)
|
||||
✔ a link inside the data root is followed (1.911798ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.338795ms)
|
||||
✔ a run directory linked out of the data root is never read (1.316168ms)
|
||||
✔ a document linked out of the data root reads as null (1.553329ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.940651ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.763816ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.831365ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.081862ms)
|
||||
✖ an unreadable runs directory refuses instead of listing nothing (1.920097ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.910463ms)
|
||||
✔ a link to the data root's parent is outside it (0.731845ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.261893ms)
|
||||
✔ the readers write nothing (4.021246ms)
|
||||
✔ no pointer is null (3.059529ms)
|
||||
✔ the pointer release.sh writes reads back (1.661181ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.521897ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.465131ms)
|
||||
✔ a state file linked out of the data root refuses (1.638679ms)
|
||||
✔ a state directory linked out of the data root refuses (0.691235ms)
|
||||
✔ a missing log is empty (0.580753ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.082188ms)
|
||||
✔ last must be a positive integer (0.648072ms)
|
||||
✔ the state readers write nothing (1.944511ms)
|
||||
✔ list prints each run in the established format (135.505629ms)
|
||||
✔ show prints the run in the established format (124.440305ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (139.637014ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (207.914443ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (222.421367ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 912.133056
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:177:1
|
||||
✖ an unreadable runs directory refuses instead of listing nothing (1.920097ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:183:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..1c7c4378 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -18,6 +18,7 @@ function readStateFile(dataRoot, name) {
|
||||
try {
|
||||
return { file, text: fs.readFileSync(file, "utf8") };
|
||||
} catch (error) {
|
||||
+ return null;
|
||||
throw new RunsError(`cannot read ${file}: ${error.code ?? error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (2.461841ms)
|
||||
✔ a missing data root or runs directory lists nothing (4.122833ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.049435ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.484169ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (5.953769ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (2.533866ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.781628ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.272589ms)
|
||||
✔ readRunDocument reads only the three run documents (1.222564ms)
|
||||
✔ a link inside the data root is followed (2.349315ms)
|
||||
✔ a data root that is itself a link is trusted as configured (4.990435ms)
|
||||
✔ a run directory linked out of the data root is never read (1.847834ms)
|
||||
✔ a document linked out of the data root reads as null (1.825285ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.095073ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.798642ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.914727ms)
|
||||
✔ a link loop refuses instead of reading as missing (2.417719ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.968089ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.041544ms)
|
||||
✔ a link to the data root's parent is outside it (0.822282ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.693855ms)
|
||||
✔ the readers write nothing (4.240004ms)
|
||||
✔ no pointer is null (4.711471ms)
|
||||
✔ the pointer release.sh writes reads back (3.19144ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.698419ms)
|
||||
✖ a pointer that is a directory refuses with exit 4 (2.271643ms)
|
||||
✔ a state file linked out of the data root refuses (3.820894ms)
|
||||
✔ a state directory linked out of the data root refuses (1.328242ms)
|
||||
✔ a missing log is empty (0.757558ms)
|
||||
✔ the log reads oldest first and counts malformed lines (3.341732ms)
|
||||
✔ last must be a positive integer (1.01732ms)
|
||||
✔ the state readers write nothing (2.075785ms)
|
||||
✔ list prints each run in the established format (134.172955ms)
|
||||
✔ show prints the run in the established format (111.604381ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (176.970683ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (212.899331ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (174.178303ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 930.307733
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:51:1
|
||||
✖ a pointer that is a directory refuses with exit 4 (2.271643ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:54:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..9e424f9f 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -37,7 +37,7 @@ export function readActivePointer(dataRoot) {
|
||||
const invalid = (why) => new RunsError(`release pointer ${why} (${state.file})`, 2);
|
||||
if (typeof pointer !== "object" || pointer === null || Array.isArray(pointer)) throw invalid("must be a JSON object");
|
||||
for (const key of Object.keys(pointer)) {
|
||||
- if (!POINTER_KEYS.includes(key)) throw invalid(`has an unsupported key: "${key}"`);
|
||||
+ if (false) throw invalid(`has an unsupported key: "${key}"`);
|
||||
}
|
||||
if (pointer.pointerVersion !== 1) throw invalid(`has unsupported pointerVersion ${JSON.stringify(pointer.pointerVersion)}`);
|
||||
for (const key of ["release", "imageTag", "activatedAt"]) {
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.76778ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.441016ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.970565ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.262129ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.361776ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.832576ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.389704ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.129822ms)
|
||||
✔ readRunDocument reads only the three run documents (0.922434ms)
|
||||
✔ a link inside the data root is followed (1.811731ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.209871ms)
|
||||
✔ a run directory linked out of the data root is never read (1.162347ms)
|
||||
✔ a document linked out of the data root reads as null (1.753983ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.898425ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.561041ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.862277ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.825058ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.584625ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.750071ms)
|
||||
✔ a link to the data root's parent is outside it (0.445297ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (0.954512ms)
|
||||
✔ the readers write nothing (2.660378ms)
|
||||
✔ no pointer is null (2.198032ms)
|
||||
✔ the pointer release.sh writes reads back (1.29844ms)
|
||||
✖ a pointer that isn't the version 1 shape refuses with exit 2 (8.313848ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.040667ms)
|
||||
✔ a state file linked out of the data root refuses (2.188204ms)
|
||||
✔ a state directory linked out of the data root refuses (0.785082ms)
|
||||
✔ a missing log is empty (0.511055ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.260842ms)
|
||||
✔ last must be a positive integer (0.5446ms)
|
||||
✔ the state readers write nothing (1.511675ms)
|
||||
✔ list prints each run in the established format (106.978183ms)
|
||||
✔ show prints the run in the established format (82.075624ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (150.338163ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (213.344597ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (189.567749ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 817.276399
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:32:1
|
||||
✖ a pointer that isn't the version 1 shape refuses with exit 2 (8.313848ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: /unsupported key: "extra"/
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:47:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/paths.mjs b/packages/runs/src/paths.mjs
|
||||
index 1dbb5539..fe56cb9b 100644
|
||||
--- a/packages/runs/src/paths.mjs
|
||||
+++ b/packages/runs/src/paths.mjs
|
||||
@@ -11,7 +11,7 @@ export function isMissing(error) {
|
||||
|
||||
function isInside(root, target) {
|
||||
const relative = path.relative(root, target);
|
||||
- return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
||||
+ return false || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
||||
}
|
||||
|
||||
// Resolves <dataRoot>/<parts...> through any symbolic links and returns the
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.51662ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.475089ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.23105ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.353463ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.784699ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.983165ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.313701ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.315203ms)
|
||||
✔ readRunDocument reads only the three run documents (1.070086ms)
|
||||
✔ a link inside the data root is followed (1.390231ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.13756ms)
|
||||
✔ a run directory linked out of the data root is never read (1.289428ms)
|
||||
✔ a document linked out of the data root reads as null (1.707909ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.751734ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.807517ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.930121ms)
|
||||
✔ a link loop refuses instead of reading as missing (4.279406ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (1.054034ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.037179ms)
|
||||
✔ a link to the data root's parent is outside it (0.766076ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.561169ms)
|
||||
✔ the readers write nothing (4.251191ms)
|
||||
✔ no pointer is null (2.961101ms)
|
||||
✔ the pointer release.sh writes reads back (2.02442ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (5.513029ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.812389ms)
|
||||
✔ a state file linked out of the data root refuses (1.807408ms)
|
||||
✔ a state directory linked out of the data root refuses (1.064741ms)
|
||||
✔ a missing log is empty (0.778107ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.719825ms)
|
||||
✔ last must be a positive integer (0.79633ms)
|
||||
✔ the state readers write nothing (1.960078ms)
|
||||
✔ list prints each run in the established format (121.641214ms)
|
||||
✔ show prints the run in the established format (103.243231ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (168.245538ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (247.465101ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (182.280266ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 914.239511
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/scripts/mosaic-task.mjs b/scripts/mosaic-task.mjs
|
||||
index cf8399a0..7c4bfb80 100755
|
||||
--- a/scripts/mosaic-task.mjs
|
||||
+++ b/scripts/mosaic-task.mjs
|
||||
@@ -462,7 +462,7 @@ function readRuns(read) {
|
||||
try {
|
||||
return read();
|
||||
} catch (error) {
|
||||
- if (error instanceof RunsError) fail(error.exitCode, error.message);
|
||||
+ if (false) fail(error.exitCode, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.335247ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.044238ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.825059ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.992615ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.448335ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.940595ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.88747ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.887944ms)
|
||||
✔ readRunDocument reads only the three run documents (0.742112ms)
|
||||
✔ a link inside the data root is followed (1.800967ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.347045ms)
|
||||
✔ a run directory linked out of the data root is never read (1.219752ms)
|
||||
✔ a document linked out of the data root reads as null (1.142642ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.77569ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.508687ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.818208ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.874875ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.617573ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.789617ms)
|
||||
✔ a link to the data root's parent is outside it (0.744195ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.123154ms)
|
||||
✔ the readers write nothing (3.640519ms)
|
||||
✔ no pointer is null (2.825548ms)
|
||||
✔ the pointer release.sh writes reads back (1.614689ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.000458ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.784002ms)
|
||||
✔ a state file linked out of the data root refuses (2.224001ms)
|
||||
✔ a state directory linked out of the data root refuses (1.043958ms)
|
||||
✔ a missing log is empty (0.707165ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.568329ms)
|
||||
✔ last must be a positive integer (0.801038ms)
|
||||
✔ the state readers write nothing (1.64136ms)
|
||||
✔ list prints each run in the established format (114.475779ms)
|
||||
✔ show prints the run in the established format (92.906292ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (173.27576ms)
|
||||
✖ list and show refuse a runs directory linked out of the data root (104.319544ms)
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (123.359118ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 35
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 689.166126
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:70:1
|
||||
✖ list and show refuse a runs directory linked out of the data root (104.319544ms)
|
||||
AssertionError [ERR_ASSERTION]: list
|
||||
|
||||
1 !== 4
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:76:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 1,
|
||||
expected: 4,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:82:1
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (123.359118ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
1 !== 4
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:88:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 1,
|
||||
expected: 4,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..943dadfa 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -59,7 +59,6 @@ export function readRunRecord(dataRoot, runId) {
|
||||
try {
|
||||
artifacts = fs.readdirSync(dir);
|
||||
} catch (error) {
|
||||
- if (isMissing(error)) return null;
|
||||
throw new RunsError(`cannot read ${dir}: ${error.code ?? error.message}`);
|
||||
}
|
||||
return {
|
||||
@@ -0,0 +1,61 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.279176ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.009751ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.780142ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.907817ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.573369ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.415964ms)
|
||||
✖ readRunRecord is null for a missing run, a dangling link or a file (1.425009ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.074088ms)
|
||||
✔ readRunDocument reads only the three run documents (0.943201ms)
|
||||
✔ a link inside the data root is followed (1.78009ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.185862ms)
|
||||
✔ a run directory linked out of the data root is never read (1.204155ms)
|
||||
✔ a document linked out of the data root reads as null (1.549239ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.03995ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.689295ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.923991ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.150588ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.729845ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.891325ms)
|
||||
✔ a link to the data root's parent is outside it (1.221017ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.108483ms)
|
||||
✔ the readers write nothing (3.377499ms)
|
||||
✔ no pointer is null (2.369876ms)
|
||||
✔ the pointer release.sh writes reads back (1.445943ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (4.334902ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.757318ms)
|
||||
✔ a state file linked out of the data root refuses (1.27317ms)
|
||||
✔ a state directory linked out of the data root refuses (0.743687ms)
|
||||
✔ a missing log is empty (0.463828ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.107084ms)
|
||||
✔ last must be a positive integer (0.692277ms)
|
||||
✔ the state readers write nothing (1.18172ms)
|
||||
✔ list prints each run in the established format (105.980567ms)
|
||||
✔ show prints the run in the established format (115.012534ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (135.038631ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (172.97353ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (197.370536ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 805.379529
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:75:1
|
||||
✖ readRunRecord is null for a missing run, a dangling link or a file (1.425009ms)
|
||||
Error [RunsError]: cannot read /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-FPVVtO/data/runs/r-20260101T000100Z-bbbbbb: ENOTDIR
|
||||
at readRunRecord (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:62:11)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:83:16)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
exitCode: 4
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/paths.mjs b/packages/runs/src/paths.mjs
|
||||
index 1dbb5539..7ce9d52c 100644
|
||||
--- a/packages/runs/src/paths.mjs
|
||||
+++ b/packages/runs/src/paths.mjs
|
||||
@@ -24,7 +24,7 @@ export function resolveInside(dataRoot, ...parts) {
|
||||
let real;
|
||||
try {
|
||||
root = fs.realpathSync(dataRoot);
|
||||
- real = fs.realpathSync(target);
|
||||
+ real = path.resolve(target);
|
||||
} catch (error) {
|
||||
if (isMissing(error)) return null;
|
||||
throw new RunsError(`cannot resolve ${target}: ${error.code ?? error.message}`);
|
||||
@@ -0,0 +1,334 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (2.337061ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.812348ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.989212ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.36075ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.67149ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (2.491353ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.899895ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.568632ms)
|
||||
✔ readRunDocument reads only the three run documents (1.626551ms)
|
||||
✔ a link inside the data root is followed (1.731397ms)
|
||||
✖ a data root that is itself a link is trusted as configured (1.465017ms)
|
||||
✖ a run directory linked out of the data root is never read (3.090379ms)
|
||||
✖ a document linked out of the data root reads as null (1.761344ms)
|
||||
✖ a runs directory linked out of the data root refuses (0.967405ms)
|
||||
✖ a relative link that climbs out of the data root refuses (1.918539ms)
|
||||
✖ a sibling whose name starts with the data root's name is outside it (1.08471ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.050719ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.697011ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.878691ms)
|
||||
✖ a link to the data root's parent is outside it (0.945372ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.249209ms)
|
||||
✔ the readers write nothing (2.766612ms)
|
||||
✖ no pointer is null (3.426697ms)
|
||||
✔ the pointer release.sh writes reads back (2.049083ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.05565ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.036013ms)
|
||||
✖ a state file linked out of the data root refuses (2.970028ms)
|
||||
✖ a state directory linked out of the data root refuses (2.142321ms)
|
||||
✖ a missing log is empty (0.990974ms)
|
||||
✔ the log reads oldest first and counts malformed lines (2.655752ms)
|
||||
✔ last must be a positive integer (0.876981ms)
|
||||
✔ the state readers write nothing (2.744ms)
|
||||
✔ list prints each run in the established format (170.137522ms)
|
||||
✔ show prints the run in the established format (120.109446ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (151.012044ms)
|
||||
✖ list and show refuse a runs directory linked out of the data root (124.06692ms)
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (140.926544ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 24
|
||||
ℹ fail 13
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 811.148685
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:111:1
|
||||
✖ a data root that is itself a link is trusted as configured (1.465017ms)
|
||||
Error [RunsError]: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-4NH1n7/alias/runs resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-4NH1n7/data)
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:33:11)
|
||||
at listRunIds (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:20:16)
|
||||
at listRunRecords (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:44:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:116:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:119:1
|
||||
✖ a run directory linked out of the data root is never read (3.090379ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
{
|
||||
+ result: {
|
||||
+ durationMs: 1000,
|
||||
+ exitCode: 0,
|
||||
+ expectedExact: null,
|
||||
+ finishedAt: '2026-01-01T00:00:01.000Z',
|
||||
+ missionId: null,
|
||||
+ model: 'm',
|
||||
+ provider: 'zai',
|
||||
+ reason: null,
|
||||
+ request: 'hi',
|
||||
+ response: 'hi',
|
||||
+ runId: 'r-20260101T000000Z-aaaaaa',
|
||||
+ runVersion: 1,
|
||||
+ session: null,
|
||||
+ sessionForkFrom: null,
|
||||
+ signal: null,
|
||||
+ startedAt: '2026-01-01T00:00:00.000Z',
|
||||
+ status: 'succeeded',
|
||||
+ taskId: 't-one',
|
||||
+ tools: null,
|
||||
+ workspace: null
|
||||
+ },
|
||||
- result: null,
|
||||
runId: 'r-20260101T000000Z-aaaaaa'
|
||||
}
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:125:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { runId: 'r-20260101T000000Z-aaaaaa', result: [Object] } ],
|
||||
expected: [ { runId: 'r-20260101T000000Z-aaaaaa', result: null } ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:129:1
|
||||
✖ a document linked out of the data root reads as null (1.761344ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ {
|
||||
+ durationMs: 1000,
|
||||
+ exitCode: 0,
|
||||
+ expectedExact: null,
|
||||
+ finishedAt: '2026-01-01T00:00:01.000Z',
|
||||
+ missionId: null,
|
||||
+ model: 'm',
|
||||
+ provider: 'zai',
|
||||
+ reason: null,
|
||||
+ request: 'hi',
|
||||
+ response: 'hi',
|
||||
+ runId: 'r-20260101T000000Z-aaaaaa',
|
||||
+ runVersion: 1,
|
||||
+ session: null,
|
||||
+ sessionForkFrom: null,
|
||||
+ signal: null,
|
||||
+ startedAt: '2026-01-01T00:00:00.000Z',
|
||||
+ status: 'succeeded',
|
||||
+ taskId: 't-one',
|
||||
+ tools: null,
|
||||
+ workspace: null
|
||||
+ }
|
||||
- null
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:137:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { runVersion: 1, runId: 'r-20260101T000000Z-aaaaaa', taskId: 't-one', missionId: null, status: 'succeeded', reason: null, request: 'hi', response: 'hi', expectedExact: null, workspace: null, tools: null, session: null, sessionForkFrom: null, exitCode: 0, signal: null, provider: 'zai', model: 'm', startedAt: '2026-01-01T00:00:00.000Z', finishedAt: '2026-01-01T00:00:01.000Z', durationMs: 1000 },
|
||||
expected: null,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:143:1
|
||||
✖ a runs directory linked out of the data root refuses (0.967405ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:147:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /runs resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:152:1
|
||||
✖ a relative link that climbs out of the data root refuses (1.918539ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:156:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:159:1
|
||||
✖ a sibling whose name starts with the data root's name is outside it (1.08471ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:165:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:201:1
|
||||
✖ a link to the data root's parent is outside it (0.945372ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:205:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:18:1
|
||||
✖ no pointer is null (3.426697ms)
|
||||
Error [RunsError]: cannot read /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-jLz66S/data/state/active.json: ENOENT
|
||||
at readStateFile (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/state.mjs:21:11)
|
||||
at readActivePointer (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/state.mjs:29:17)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:20:16)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:57:1
|
||||
✖ a state file linked out of the data root refuses (2.970028ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:64:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:68:1
|
||||
✖ a state directory linked out of the data root refuses (2.142321ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:72:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /state\/active.json resolves outside the data root/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:75:1
|
||||
✖ a missing log is empty (0.990974ms)
|
||||
Error [RunsError]: cannot read /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-2x3xPI/data/state/activation-log.jsonl: ENOENT
|
||||
at readStateFile (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/state.mjs:21:11)
|
||||
at readActivationLog (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/state.mjs:71:17)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:77:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
exitCode: 4
|
||||
}
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:70:1
|
||||
✖ list and show refuse a runs directory linked out of the data root (124.06692ms)
|
||||
AssertionError [ERR_ASSERTION]: list
|
||||
|
||||
0 !== 4
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:76:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 4,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/runs/tests/task-cli.test.mjs:82:1
|
||||
✖ show refuses a run linked out of the data root; list reports it unknown (140.926544ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
0 !== 4
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/task-cli.test.mjs:88:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 4,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/paths.mjs b/packages/runs/src/paths.mjs
|
||||
index 1dbb5539..2cbffca6 100644
|
||||
--- a/packages/runs/src/paths.mjs
|
||||
+++ b/packages/runs/src/paths.mjs
|
||||
@@ -23,7 +23,7 @@ export function resolveInside(dataRoot, ...parts) {
|
||||
let root;
|
||||
let real;
|
||||
try {
|
||||
- root = fs.realpathSync(dataRoot);
|
||||
+ root = path.resolve(dataRoot);
|
||||
real = fs.realpathSync(target);
|
||||
} catch (error) {
|
||||
if (isMissing(error)) return null;
|
||||
@@ -0,0 +1,63 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.225949ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.892082ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.747282ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.931027ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.487142ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.153245ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.788032ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.781425ms)
|
||||
✔ readRunDocument reads only the three run documents (0.64425ms)
|
||||
✔ a link inside the data root is followed (1.443248ms)
|
||||
✖ a data root that is itself a link is trusted as configured (0.723306ms)
|
||||
✔ a run directory linked out of the data root is never read (0.774806ms)
|
||||
✔ a document linked out of the data root reads as null (1.18996ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.624807ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.520406ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.623168ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.641501ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.470743ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.559037ms)
|
||||
✔ a link to the data root's parent is outside it (0.503187ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (16.993379ms)
|
||||
✔ the readers write nothing (3.04089ms)
|
||||
✔ no pointer is null (2.102694ms)
|
||||
✔ the pointer release.sh writes reads back (1.420335ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (99.446402ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.965912ms)
|
||||
✔ a state file linked out of the data root refuses (1.584736ms)
|
||||
✔ a state directory linked out of the data root refuses (0.926403ms)
|
||||
✔ a missing log is empty (0.585862ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.76685ms)
|
||||
✔ last must be a positive integer (0.522534ms)
|
||||
✔ the state readers write nothing (1.138156ms)
|
||||
✔ list prints each run in the established format (99.14821ms)
|
||||
✔ show prints the run in the established format (112.530433ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (168.188718ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (200.199186ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (210.660658ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 877.697783
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:111:1
|
||||
✖ a data root that is itself a link is trusted as configured (0.723306ms)
|
||||
Error [RunsError]: /home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-5lxeUT/alias/runs resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/tmp/mosaic-runs-test-5lxeUT/alias)
|
||||
at resolveInside (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/paths.mjs:33:11)
|
||||
at listRunIds (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:20:16)
|
||||
at listRunRecords (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/src/runs.mjs:44:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:116:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
exitCode: 4
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..08a71fab 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import { RunsError } from "./errors.mjs";
|
||||
import { RUNS_DIRNAME, isMissing, readJsonObject, resolveInside } from "./paths.mjs";
|
||||
|
||||
-export const RUN_ID_PATTERN = /^r-[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
||||
+export const RUN_ID_PATTERN = /^r-[A-Za-z0-9][A-Za-z0-9._-]{0,64}$/;
|
||||
export const RUN_DOCUMENTS = ["result.json", "task.json", "mission.json"];
|
||||
|
||||
export function isRunId(value) {
|
||||
@@ -0,0 +1,66 @@
|
||||
✖ isRunId accepts the run id shape and nothing else (2.621121ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.873428ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.877432ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.2193ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (4.17104ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (2.167047ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.610572ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.237909ms)
|
||||
✔ readRunDocument reads only the three run documents (1.490464ms)
|
||||
✔ a link inside the data root is followed (2.047317ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.310886ms)
|
||||
✔ a run directory linked out of the data root is never read (1.375476ms)
|
||||
✔ a document linked out of the data root reads as null (1.919519ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.177495ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.809767ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.918443ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.269903ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (1.13428ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.208613ms)
|
||||
✔ a link to the data root's parent is outside it (1.309226ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.665016ms)
|
||||
✔ the readers write nothing (4.268303ms)
|
||||
✔ no pointer is null (2.482055ms)
|
||||
✔ the pointer release.sh writes reads back (1.622475ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.093312ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.37517ms)
|
||||
✔ a state file linked out of the data root refuses (1.304846ms)
|
||||
✔ a state directory linked out of the data root refuses (0.707381ms)
|
||||
✔ a missing log is empty (0.514771ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.170055ms)
|
||||
✔ last must be a positive integer (0.690868ms)
|
||||
✔ the state readers write nothing (1.127808ms)
|
||||
✔ list prints each run in the established format (111.188746ms)
|
||||
✔ show prints the run in the established format (107.486029ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (143.908228ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (191.94812ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (212.7919ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 858.033888
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:13:1
|
||||
✖ isRunId accepts the run id shape and nothing else (2.621121ms)
|
||||
AssertionError [ERR_ASSERTION]: r-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:16:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..db7d57c1 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -43,7 +43,7 @@ export function readRunDocument(dataRoot, runId, name) {
|
||||
export function listRunRecords(dataRoot) {
|
||||
return listRunIds(dataRoot).map((runId) => ({
|
||||
runId,
|
||||
- result: readJsonObject(dataRoot, RUNS_DIRNAME, runId, "result.json"),
|
||||
+ result: readRunDocument(dataRoot, runId, "result.json"),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.84367ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.700807ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.033316ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.32646ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.42376ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.65341ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.089868ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.018263ms)
|
||||
✔ readRunDocument reads only the three run documents (2.607684ms)
|
||||
✔ a link inside the data root is followed (1.72714ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.101493ms)
|
||||
✔ a run directory linked out of the data root is never read (1.204336ms)
|
||||
✔ a document linked out of the data root reads as null (2.271677ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.930102ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.643793ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.780072ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.056981ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.657066ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.82135ms)
|
||||
✔ a link to the data root's parent is outside it (0.603085ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.157629ms)
|
||||
✔ the readers write nothing (3.625933ms)
|
||||
✔ no pointer is null (2.573356ms)
|
||||
✔ the pointer release.sh writes reads back (1.601548ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.886359ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.00578ms)
|
||||
✔ a state file linked out of the data root refuses (1.827811ms)
|
||||
✔ a state directory linked out of the data root refuses (0.966807ms)
|
||||
✔ a missing log is empty (0.590111ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.749904ms)
|
||||
✔ last must be a positive integer (0.741643ms)
|
||||
✔ the state readers write nothing (1.380717ms)
|
||||
✔ list prints each run in the established format (109.05177ms)
|
||||
✔ show prints the run in the established format (106.424344ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (151.998567ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (198.84273ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (214.31565ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 861.341071
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..067bd09e 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -73,7 +73,7 @@ export function readActivationLog(dataRoot, { last } = {}) {
|
||||
const entries = [];
|
||||
let malformed = 0;
|
||||
for (const line of state.text.split("\n")) {
|
||||
- if (line.trim() === "") continue;
|
||||
+ if (line === "") continue;
|
||||
const entry = logEntry(line);
|
||||
if (entry === null) malformed += 1;
|
||||
else entries.push(entry);
|
||||
@@ -0,0 +1,79 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.81005ms)
|
||||
✔ a missing data root or runs directory lists nothing (3.788851ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.332528ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.521814ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (4.886328ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (3.564755ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (2.859036ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.951484ms)
|
||||
✔ readRunDocument reads only the three run documents (1.289383ms)
|
||||
✔ a link inside the data root is followed (3.726081ms)
|
||||
✔ a data root that is itself a link is trusted as configured (5.725889ms)
|
||||
✔ a run directory linked out of the data root is never read (2.611536ms)
|
||||
✔ a document linked out of the data root reads as null (2.941983ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.42401ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.981488ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (1.093183ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.426419ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.900191ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (1.392808ms)
|
||||
✔ a link to the data root's parent is outside it (0.919184ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.382679ms)
|
||||
✔ the readers write nothing (3.80633ms)
|
||||
✔ no pointer is null (3.365628ms)
|
||||
✔ the pointer release.sh writes reads back (2.160411ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (4.729941ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (2.975348ms)
|
||||
✔ a state file linked out of the data root refuses (1.960951ms)
|
||||
✔ a state directory linked out of the data root refuses (1.362152ms)
|
||||
✔ a missing log is empty (0.915277ms)
|
||||
✖ the log reads oldest first and counts malformed lines (4.787537ms)
|
||||
✔ last must be a positive integer (1.174335ms)
|
||||
✔ the state readers write nothing (8.296235ms)
|
||||
✔ list prints each run in the established format (135.967139ms)
|
||||
✔ show prints the run in the established format (126.514675ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (248.881535ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (178.634661ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (318.758144ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1106.083123
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:80:1
|
||||
✖ the log reads oldest first and counts malformed lines (4.787537ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
... Skipped lines
|
||||
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
at: '2026-09-03T20:57:00Z',
|
||||
event: 'package',
|
||||
...
|
||||
],
|
||||
+ malformed: 6
|
||||
- malformed: 5
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:101:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { entries: [ [Object], [Object], [Object], [Object] ], malformed: 6 },
|
||||
expected: { entries: [ [Object], [Object], [Object], [Object] ], malformed: 5 },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..d8cb0e58 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -32,7 +32,6 @@ export function listRunIds(dataRoot) {
|
||||
// One of RUN_DOCUMENTS from a run, or null when it is missing, unreadable,
|
||||
// not a JSON object or outside the data root.
|
||||
export function readRunDocument(dataRoot, runId, name) {
|
||||
- requireRunId(runId);
|
||||
if (!RUN_DOCUMENTS.includes(name)) throw new RunsError(`unknown run document: ${JSON.stringify(name)}`);
|
||||
return readJsonObject(dataRoot, RUNS_DIRNAME, runId, name);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.177262ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.120403ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.678388ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.861957ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.547546ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.538135ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.13107ms)
|
||||
✖ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.271159ms)
|
||||
✔ readRunDocument reads only the three run documents (0.654773ms)
|
||||
✔ a link inside the data root is followed (1.097525ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.753622ms)
|
||||
✔ a run directory linked out of the data root is never read (0.834684ms)
|
||||
✔ a document linked out of the data root reads as null (1.039271ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.69181ms)
|
||||
✔ a relative link that climbs out of the data root refuses (1.855925ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (1.188475ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.134003ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.730721ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.863693ms)
|
||||
✔ a link to the data root's parent is outside it (0.64587ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.228953ms)
|
||||
✔ the readers write nothing (4.005022ms)
|
||||
✔ no pointer is null (3.229517ms)
|
||||
✔ the pointer release.sh writes reads back (2.000427ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (22.490669ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.097015ms)
|
||||
✔ a state file linked out of the data root refuses (3.625438ms)
|
||||
✔ a state directory linked out of the data root refuses (1.557756ms)
|
||||
✔ a missing log is empty (0.857274ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.909144ms)
|
||||
✔ last must be a positive integer (0.916328ms)
|
||||
✔ the state readers write nothing (2.334238ms)
|
||||
✔ list prints each run in the established format (112.749358ms)
|
||||
✔ show prints the run in the established format (93.106249ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (149.979418ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (171.947155ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (302.989078ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 900.360942
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/runs.test.mjs:86:1
|
||||
✖ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.271159ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception (RunsError).
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/runs.test.mjs:90:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/runs.mjs b/packages/runs/src/runs.mjs
|
||||
index 03730c33..3814b6a5 100644
|
||||
--- a/packages/runs/src/runs.mjs
|
||||
+++ b/packages/runs/src/runs.mjs
|
||||
@@ -26,7 +26,7 @@ export function listRunIds(dataRoot) {
|
||||
if (isMissing(error)) return [];
|
||||
throw new RunsError(`cannot read ${root}: ${error.code ?? error.message}`);
|
||||
}
|
||||
- return names.filter((name) => name.startsWith("r-")).sort();
|
||||
+ return names.filter(isRunId).sort();
|
||||
}
|
||||
|
||||
// One of RUN_DOCUMENTS from a run, or null when it is missing, unreadable,
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.306378ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.195703ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.832745ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.032055ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.96098ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.386366ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.079913ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.059646ms)
|
||||
✔ readRunDocument reads only the three run documents (0.919524ms)
|
||||
✔ a link inside the data root is followed (1.7667ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.154799ms)
|
||||
✔ a run directory linked out of the data root is never read (1.194447ms)
|
||||
✔ a document linked out of the data root reads as null (1.545639ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.012007ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.840459ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.862225ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.091533ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.700403ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.823368ms)
|
||||
✔ a link to the data root's parent is outside it (0.661858ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.208556ms)
|
||||
✔ the readers write nothing (3.397935ms)
|
||||
✔ no pointer is null (2.922658ms)
|
||||
✔ the pointer release.sh writes reads back (1.955593ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.059019ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.993624ms)
|
||||
✔ a state file linked out of the data root refuses (2.525011ms)
|
||||
✔ a state directory linked out of the data root refuses (1.000341ms)
|
||||
✔ a missing log is empty (0.67567ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.536433ms)
|
||||
✔ last must be a positive integer (0.791281ms)
|
||||
✔ the state readers write nothing (1.583337ms)
|
||||
✔ list prints each run in the established format (111.618545ms)
|
||||
✔ show prints the run in the established format (86.235796ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (147.991565ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (221.002247ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (185.706284ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 897.558738
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..1789e568 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -39,7 +39,7 @@ export function readActivePointer(dataRoot) {
|
||||
for (const key of Object.keys(pointer)) {
|
||||
if (!POINTER_KEYS.includes(key)) throw invalid(`has an unsupported key: "${key}"`);
|
||||
}
|
||||
- if (pointer.pointerVersion !== 1) throw invalid(`has unsupported pointerVersion ${JSON.stringify(pointer.pointerVersion)}`);
|
||||
+ if (false) throw invalid(`has unsupported pointerVersion ${JSON.stringify(pointer.pointerVersion)}`);
|
||||
for (const key of ["release", "imageTag", "activatedAt"]) {
|
||||
if (!isNonEmptyString(pointer[key])) throw invalid(`needs a non-empty string "${key}"`);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.127867ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.72466ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.646415ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.82023ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.39311ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.164002ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.814071ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.453894ms)
|
||||
✔ readRunDocument reads only the three run documents (0.658026ms)
|
||||
✔ a link inside the data root is followed (1.155118ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.793186ms)
|
||||
✔ a run directory linked out of the data root is never read (0.791204ms)
|
||||
✔ a document linked out of the data root reads as null (0.969944ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.658788ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.519731ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.554988ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.740597ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.508563ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.585049ms)
|
||||
✔ a link to the data root's parent is outside it (0.436398ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (0.926292ms)
|
||||
✔ the readers write nothing (2.24403ms)
|
||||
✔ no pointer is null (2.531076ms)
|
||||
✔ the pointer release.sh writes reads back (1.633448ms)
|
||||
✖ a pointer that isn't the version 1 shape refuses with exit 2 (130.614238ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.786002ms)
|
||||
✔ a state file linked out of the data root refuses (2.341036ms)
|
||||
✔ a state directory linked out of the data root refuses (0.805877ms)
|
||||
✔ a missing log is empty (0.47975ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.125972ms)
|
||||
✔ last must be a positive integer (0.54587ms)
|
||||
✔ the state readers write nothing (1.23264ms)
|
||||
✔ list prints each run in the established format (95.611062ms)
|
||||
✔ show prints the run in the established format (94.055273ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (138.36289ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (184.218135ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (314.21614ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 897.837373
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:32:1
|
||||
✖ a pointer that isn't the version 1 shape refuses with exit 2 (130.614238ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: /unsupported pointerVersion 2/
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:47:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..c76edde2 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -55,7 +55,7 @@ function logEntry(line) {
|
||||
}
|
||||
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return null;
|
||||
if (Object.keys(entry).some((key) => !LOG_KEYS.includes(key))) return null;
|
||||
- if (!["at", "event", "release", "imageTag"].every((key) => typeof entry[key] === "string")) return null;
|
||||
+ if (!["at", "event", "release"].every((key) => typeof entry[key] === "string")) return null;
|
||||
if (entry.note !== undefined && typeof entry.note !== "string") return null;
|
||||
return entry;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.834258ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.636105ms)
|
||||
✔ runs as a regular file lists nothing, as before (2.901636ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (3.100526ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (6.571513ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (3.744909ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (2.315471ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (2.654758ms)
|
||||
✔ readRunDocument reads only the three run documents (1.530565ms)
|
||||
✔ a link inside the data root is followed (3.351352ms)
|
||||
✔ a data root that is itself a link is trusted as configured (3.308438ms)
|
||||
✔ a run directory linked out of the data root is never read (1.199258ms)
|
||||
✔ a document linked out of the data root reads as null (4.723514ms)
|
||||
✔ a runs directory linked out of the data root refuses (2.543252ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.642465ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.865087ms)
|
||||
✔ a link loop refuses instead of reading as missing (2.746857ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.67741ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.735708ms)
|
||||
✔ a link to the data root's parent is outside it (0.592559ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (2.772116ms)
|
||||
✔ the readers write nothing (5.349486ms)
|
||||
✔ no pointer is null (6.038816ms)
|
||||
✔ the pointer release.sh writes reads back (2.191058ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (4.422889ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (3.41713ms)
|
||||
✔ a state file linked out of the data root refuses (2.798861ms)
|
||||
✔ a state directory linked out of the data root refuses (2.478415ms)
|
||||
✔ a missing log is empty (1.394344ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.455927ms)
|
||||
✔ last must be a positive integer (2.396006ms)
|
||||
✔ the state readers write nothing (1.735056ms)
|
||||
✔ list prints each run in the established format (131.488575ms)
|
||||
✔ show prints the run in the established format (92.166358ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (158.462467ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (170.034985ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (201.838401ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 872.50546
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/runs/src/state.mjs b/packages/runs/src/state.mjs
|
||||
index a92c8713..49d8c1fd 100644
|
||||
--- a/packages/runs/src/state.mjs
|
||||
+++ b/packages/runs/src/state.mjs
|
||||
@@ -65,7 +65,7 @@ function logEntry(line) {
|
||||
// release.sh rollback skips them. A missing log is empty. With last, only
|
||||
// the newest last well-formed entries are returned.
|
||||
export function readActivationLog(dataRoot, { last } = {}) {
|
||||
- if (last !== undefined && (!Number.isInteger(last) || last < 1)) {
|
||||
+ if (last !== undefined && (!Number.isInteger(last) || last < 0)) {
|
||||
throw new RunsError(`last must be a positive integer (got ${JSON.stringify(last)})`);
|
||||
}
|
||||
const state = readStateFile(dataRoot, ACTIVATION_LOG_FILE);
|
||||
@@ -0,0 +1,65 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (2.040623ms)
|
||||
✔ a missing data root or runs directory lists nothing (3.167511ms)
|
||||
✔ runs as a regular file lists nothing, as before (1.148056ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.529862ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (4.035385ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (2.356077ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.382285ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.21881ms)
|
||||
✔ readRunDocument reads only the three run documents (1.081942ms)
|
||||
✔ a link inside the data root is followed (1.529533ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.029457ms)
|
||||
✔ a run directory linked out of the data root is never read (1.182624ms)
|
||||
✔ a document linked out of the data root reads as null (1.55228ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.964271ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.735934ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.835571ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.106386ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.726369ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.851219ms)
|
||||
✔ a link to the data root's parent is outside it (0.763768ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.218202ms)
|
||||
✔ the readers write nothing (3.280544ms)
|
||||
✔ no pointer is null (2.733337ms)
|
||||
✔ the pointer release.sh writes reads back (1.378784ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (5.183544ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.322104ms)
|
||||
✔ a state file linked out of the data root refuses (1.28962ms)
|
||||
✔ a state directory linked out of the data root refuses (0.633332ms)
|
||||
✔ a missing log is empty (0.439431ms)
|
||||
✔ the log reads oldest first and counts malformed lines (0.988303ms)
|
||||
✖ last must be a positive integer (1.077906ms)
|
||||
✔ the state readers write nothing (1.24199ms)
|
||||
✔ list prints each run in the established format (110.647803ms)
|
||||
✔ show prints the run in the established format (110.994361ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (129.235851ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (177.07234ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (199.262239ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 806.760074
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/runs/tests/state.test.mjs:106:1
|
||||
✖ last must be a positive integer (1.077906ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: 0
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r56a/mutwt/packages/runs/tests/state.test.mjs:109:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /last must be a positive integer/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
agents/rocko/work/queue-56/byte-check.sh: OK
|
||||
agents/rocko/work/queue-56/delta-check.sh: OK
|
||||
agents/rocko/work/queue-56/seed.sh: OK
|
||||
packages/runs/package.json: OK
|
||||
packages/runs/README.md: OK
|
||||
packages/runs/src/errors.mjs: OK
|
||||
packages/runs/src/index.mjs: OK
|
||||
packages/runs/src/paths.mjs: OK
|
||||
packages/runs/src/runs.mjs: OK
|
||||
packages/runs/src/state.mjs: OK
|
||||
packages/runs/tests/helpers.mjs: OK
|
||||
packages/runs/tests/runs.test.mjs: OK
|
||||
packages/runs/tests/state.test.mjs: OK
|
||||
packages/runs/tests/task-cli.test.mjs: OK
|
||||
scripts/mosaic-task.mjs: OK
|
||||
@@ -0,0 +1,32 @@
|
||||
# mutate.py <id>: apply one named mutant to the candidate (exact text, must match once). Run from the tree root.
|
||||
import sys
|
||||
P, R, T, X = "packages/runs/src/paths.mjs", "packages/runs/src/runs.mjs", "packages/runs/src/state.mjs", "scripts/mosaic-task.mjs"
|
||||
M = {
|
||||
"D01": [(X, " if (!isRunId(runId)) {\n fail(4, `invalid run id", " if (false) {\n fail(4, `invalid run id")],
|
||||
"D03": [(P, " } catch {\n return null;\n }\n}", " } catch (error) {\n if (error instanceof RunsError) throw error;\n return null;\n }\n}")],
|
||||
"D04": [(R, " if (resolveInside(dataRoot, RUNS_DIRNAME) === null) return null;\n", "")],
|
||||
"D05": [(T, "entries.slice(-last)", "entries.slice(0, last)")],
|
||||
"D06": [(T, ' if (entry.note !== undefined && typeof entry.note !== "string") return null;\n', "")],
|
||||
"D07": [(R, " if (isMissing(error)) return [];\n throw new RunsError(`cannot read ${root}", " return [];\n throw new RunsError(`cannot read ${root}")],
|
||||
"D08": [(T, " } catch (error) {\n throw new RunsError(`cannot read ${file}", " } catch (error) {\n return null;\n throw new RunsError(`cannot read ${file}")],
|
||||
"D09": [(T, "if (!POINTER_KEYS.includes(key))", "if (false)")],
|
||||
"D10": [(P, 'return relative === "" ||', "return false ||")],
|
||||
"D11": [(X, "if (error instanceof RunsError) fail(error.exitCode, error.message);\n throw error;\n }\n}\n\nfunction listRuns", "if (false) fail(error.exitCode, error.message);\n throw error;\n }\n}\n\nfunction listRuns")],
|
||||
"D13": [(R, " if (isMissing(error)) return null;\n throw new RunsError(`cannot read ${dir}", " throw new RunsError(`cannot read ${dir}")],
|
||||
"D14": [(P, "real = fs.realpathSync(target);", "real = path.resolve(target);")],
|
||||
"D15": [(P, "root = fs.realpathSync(dataRoot);", "root = path.resolve(dataRoot);")],
|
||||
"D16": [(R, "{0,63}$/", "{0,64}$/")],
|
||||
"D17": [(R, ' result: readJsonObject(dataRoot, RUNS_DIRNAME, runId, "result.json"),', ' result: readRunDocument(dataRoot, runId, "result.json"),')],
|
||||
"D18": [(T, 'if (line.trim() === "") continue;', 'if (line === "") continue;')],
|
||||
"D19": [(R, " requireRunId(runId);\n if (!RUN_DOCUMENTS", " if (!RUN_DOCUMENTS")],
|
||||
"D20": [(R, 'filter((name) => name.startsWith("r-")).sort()', "filter(isRunId).sort()")],
|
||||
"D21": [(T, "if (pointer.pointerVersion !== 1)", "if (false)")],
|
||||
"D22": [(T, '["at", "event", "release", "imageTag"].every', '["at", "event", "release"].every')],
|
||||
"D23": [(T, "last < 1)", "last < 0)")],
|
||||
}
|
||||
for f, old, new in M[sys.argv[1]]:
|
||||
s = open(f).read()
|
||||
n = s.count(old)
|
||||
if n != 1: sys.exit(f"{sys.argv[1]}: {n} matches in {f}")
|
||||
open(f, "w").write(s.replace(old, new))
|
||||
print(sys.argv[1], "applied")
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
== P1 show ../x with no config file (mutwt carries D01 when run by this script)
|
||||
base: exit 4
|
||||
err| mosaic-task: invalid run id: "../x" (expected r-<id>)
|
||||
cand: exit 4
|
||||
err| mosaic-task: invalid run id: "../x" (expected r-<id>)
|
||||
mutwt: exit 3
|
||||
err| mosaic-task: configuration problem (exit 3); run scripts/bootstrap.sh or fix config.json
|
||||
|
||||
== P2 show of a run directory with mode 000
|
||||
base: exit 1
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
err| node:fs:1954
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot read /home/jwoltje/darkwing-scratch/r56a/probes/p2/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
mutwt: exit 4
|
||||
err| mosaic-task: cannot read /home/jwoltje/darkwing-scratch/r56a/probes/p2/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
|
||||
== P3 list with r- names that aren't valid run ids
|
||||
base: exit 0
|
||||
out| r- unknown task=- ws=- session=-
|
||||
out| r-.bad succeeded task=t ws=- session=-
|
||||
cand: exit 0
|
||||
out| r- unknown task=- ws=- session=-
|
||||
out| r-.bad succeeded task=t ws=- session=-
|
||||
mutwt: exit 0
|
||||
out| r- unknown task=- ws=- session=-
|
||||
out| r-.bad succeeded task=t ws=- session=-
|
||||
|
||||
== P4 runs/ is a link inside the data root
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t ws=- session=-
|
||||
out| r-self unknown task=- ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t ws=- session=-
|
||||
out| r-self unknown task=- ws=- session=-
|
||||
mutwt: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t ws=- session=-
|
||||
out| r-self unknown task=- ws=- session=-
|
||||
|
||||
== P5 state/ linked out of the data root, no active.json or log there
|
||||
readActivePointer: null
|
||||
readActivationLog: {"entries":[],"malformed":0}
|
||||
|
||||
== P5b same, with active.json outside
|
||||
readActivePointer: RunsError 4 <D>/state/active.json resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/probes/p5/data)
|
||||
|
||||
== P6 log lines release.sh rollback would still use (an extra key, a non-string imageTag)
|
||||
{"entries":[],"malformed":2}
|
||||
|
||||
DONE
|
||||
|
||||
== P3 list under D17
|
||||
| mosaic-task: invalid run id: "r-" (expected r-<id>)
|
||||
exit 4
|
||||
|
||||
== P3 list under D20
|
||||
exit 0
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Each mutant against the packages/runs suite in a second candidate worktree, then restores from the index.
|
||||
export TMPDIR=~/darkwing-scratch/r56a/tmp
|
||||
S=~/darkwing-scratch/r56a; M=$S/mut; W=$S/mutwt
|
||||
: > $M/summary.txt
|
||||
echo "start $(date -u +%FT%TZ)" >> $M/summary.txt
|
||||
for v in $(python3 -c 'import re;print(" ".join(sorted(set(re.findall(r"\"(D\d\d)\"", open("'$M'/mutate.py").read())))))'); do
|
||||
(cd $W && git checkout -q -- packages scripts && python3 $M/mutate.py $v > /dev/null && git diff > $M/$v.diff) || { echo "$v APPLY-FAILED" >> $M/summary.txt; continue; }
|
||||
(cd $W && env -u NODE_TEST_CONTEXT node --test packages/runs/tests/*.test.mjs > $M/$v.txt 2>&1); e=$?
|
||||
r=killed; [ $e = 0 ] && r=SURVIVED
|
||||
echo "$v $r exit=$e $(grep -E '^ℹ (pass|fail)' $M/$v.txt | tr '\n' ' ')" >> $M/summary.txt
|
||||
done
|
||||
(cd $W && git checkout -q -- packages scripts && sha256sum -c $S/pkt/candidate-manifest.sha256) > $M/manifest-after.txt 2>&1
|
||||
echo "end $(date -u +%FT%TZ)" >> $M/summary.txt
|
||||
echo DONE >> $M/summary.txt
|
||||
@@ -0,0 +1,24 @@
|
||||
start 2026-10-10T16:57:19Z
|
||||
D01 SURVIVED exit=0 ℹ pass 37 ℹ fail 0
|
||||
D03 killed exit=1 ℹ pass 33 ℹ fail 4
|
||||
D04 killed exit=1 ℹ pass 35 ℹ fail 2
|
||||
D05 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D06 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D07 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D08 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D09 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D10 SURVIVED exit=0 ℹ pass 37 ℹ fail 0
|
||||
D11 killed exit=1 ℹ pass 35 ℹ fail 2
|
||||
D13 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D14 killed exit=1 ℹ pass 24 ℹ fail 13
|
||||
D15 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D16 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D17 SURVIVED exit=0 ℹ pass 37 ℹ fail 0
|
||||
D18 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D19 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D20 SURVIVED exit=0 ℹ pass 37 ℹ fail 0
|
||||
D21 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
D22 SURVIVED exit=0 ℹ pass 37 ℹ fail 0
|
||||
D23 killed exit=1 ℹ pass 36 ℹ fail 1
|
||||
end 2026-10-10T16:57:40Z
|
||||
DONE
|
||||
@@ -0,0 +1,2 @@
|
||||
byte-check: 16 cases, stdout, stderr and exit identical
|
||||
byte-check: data root unchanged
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
== run directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== run directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /home/jwoltje/darkwing-scratch/r56a/deltacheck/run-link/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/deltacheck/run-link/data)
|
||||
|
||||
== result.json linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json
|
||||
|
||||
== runs directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 4
|
||||
err| mosaic-task: /home/jwoltje/darkwing-scratch/r56a/deltacheck/runs-link/data/runs resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/deltacheck/runs-link/data)
|
||||
|
||||
== runs directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /home/jwoltje/darkwing-scratch/r56a/deltacheck/runs-link/data/runs resolves outside the data root (/home/jwoltje/darkwing-scratch/r56a/deltacheck/runs-link/data)
|
||||
|
||||
== result.json is 5 and task.json is []: list
|
||||
base: exit 1
|
||||
err| file:///home/jwoltje/darkwing-scratch/r56a/base/scripts/mosaic-task.mjs:483
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json is 5 and task.json is []: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: undefined
|
||||
out| task: undefined
|
||||
out| adapter: (see config) provider=undefined model=undefined
|
||||
out| request: undefined
|
||||
out| response: undefined
|
||||
out| timing: undefined -> undefined (undefined ms)
|
||||
out| exit: undefined
|
||||
out| task snapshot: task.json present
|
||||
out| artifacts: result.json, task.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json, task.json
|
||||
|
||||
== run is a regular file: show
|
||||
base: exit 1
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
err| node:fs:1954
|
||||
cand: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /home/jwoltje/darkwing-scratch/r56a/deltacheck/run-file/data/runs)
|
||||
|
||||
== run is a link loop: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /home/jwoltje/darkwing-scratch/r56a/deltacheck/loop/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /home/jwoltje/darkwing-scratch/r56a/deltacheck/loop/data/runs/r-20260101T000000Z-aaaaaa: ELOOP
|
||||
|
||||
== runs directory unreadable: list
|
||||
base: exit 0
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot read /home/jwoltje/darkwing-scratch/r56a/deltacheck/unreadable/data/runs: EACCES
|
||||
|
||||
== runs directory unreadable: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /home/jwoltje/darkwing-scratch/r56a/deltacheck/unreadable/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /home/jwoltje/darkwing-scratch/r56a/deltacheck/unreadable/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
@@ -0,0 +1,45 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.748725ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.62682ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.946792ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.233291ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (3.291242ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.713813ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.29214ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.833633ms)
|
||||
✔ readRunDocument reads only the three run documents (0.879084ms)
|
||||
✔ a link inside the data root is followed (1.653918ms)
|
||||
✔ a data root that is itself a link is trusted as configured (1.299716ms)
|
||||
✔ a run directory linked out of the data root is never read (3.611395ms)
|
||||
✔ a document linked out of the data root reads as null (4.654578ms)
|
||||
✔ a runs directory linked out of the data root refuses (1.553974ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.913072ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.956986ms)
|
||||
✔ a link loop refuses instead of reading as missing (1.278185ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.756923ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.953906ms)
|
||||
✔ a link to the data root's parent is outside it (0.694287ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (2.201433ms)
|
||||
✔ the readers write nothing (3.639856ms)
|
||||
✔ no pointer is null (2.887848ms)
|
||||
✔ the pointer release.sh writes reads back (2.51019ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (3.716029ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.96141ms)
|
||||
✔ a state file linked out of the data root refuses (2.368395ms)
|
||||
✔ a state directory linked out of the data root refuses (1.703149ms)
|
||||
✔ a missing log is empty (0.58268ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.176573ms)
|
||||
✔ last must be a positive integer (0.542446ms)
|
||||
✔ the state readers write nothing (1.947461ms)
|
||||
✔ list prints each run in the established format (113.565222ms)
|
||||
✔ show prints the run in the established format (82.167185ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (136.70629ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (178.235466ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (191.565769ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 777.874059
|
||||
@@ -0,0 +1,13 @@
|
||||
start 2026-10-10T16:55:29Z
|
||||
node-runs exit=0 ℹ pass 37 ℹ fail 0
|
||||
test-auth exit=0 selftest: 15 passed, 0 failed
|
||||
test-conductor exit=0 selftest: 17 passed, 0 failed
|
||||
test-config exit=0 selftest: 24 passed, 0 failed
|
||||
test-discord exit=0 discord suite: 66 passed, 0 failed
|
||||
test-extension-package exit=0 extension package selftest: 18 passed, 0 failed
|
||||
test-foundation exit=0 selftest: 44 passed, 0 failed
|
||||
test-queue exit=0 queue suite: 27 passed, 0 failed
|
||||
test-release exit=0 selftest: 4 passed, 0 failed
|
||||
test-task exit=0 selftest: 26 passed, 0 failed
|
||||
end 2026-10-10T16:57:50Z
|
||||
DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,55 @@
|
||||
Note: switching to 'dc96f87b1776cc74d0ea5ba7701db418cc21e1b8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to 'dc96f87b1776cc74d0ea5ba7701db418cc21e1b8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 778025 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 148
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r56a/cand) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
@@ -0,0 +1,7 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
skip state-machine cases (docker daemon unavailable)
|
||||
|
||||
selftest: 4 passed, 0 failed
|
||||
@@ -0,0 +1,32 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 0 failed
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Row 56 probes: edge cases outside Rocko's byte-check and delta-check, base against candidate.
|
||||
export TMPDIR=~/darkwing-scratch/r56a/tmp
|
||||
S=~/darkwing-scratch/r56a; W=$S/probes; rm -rf $W; mkdir -p $W
|
||||
A=r-20260101T000000Z-aaaaaa
|
||||
RESULT='{"runVersion":1,"taskId":"t","status":"succeeded","request":"r","response":"s","provider":"p","model":"m","startedAt":"a","finishedAt":"b","durationMs":1,"exitCode":0,"signal":null}'
|
||||
root() { mkdir -p "$W/$1/data" "$W/$1/outside"; printf '{"configVersion":1,"environment":"development","dataRoot":"%s","execution":{"backend":"docker","provider":"zai","model":"m"}}\n' "$W/$1/data" > "$W/$1/config.json"; echo "$W/$1/data"; }
|
||||
run_case() { local name="$1" cfg="$2"; shift 2
|
||||
for tree in base cand mutwt; do
|
||||
local rc=0 out
|
||||
out="$(cd $S/$tree && env -u NODE_TEST_CONTEXT MOSAIC_CONFIG="$cfg" node scripts/mosaic-task.mjs "$@" 2>"$W/$name.$tree.err")" || rc=$?
|
||||
printf ' %s: exit %s\n' "$tree" "$rc"; [ -z "$out" ] || printf '%s\n' "$out" | sed 's/^/ out| /'
|
||||
[ -s "$W/$name.$tree.err" ] && head -1 "$W/$name.$tree.err" | sed 's/^/ err| /'
|
||||
done; }
|
||||
hdr() { printf '\n== %s\n' "$*"; }
|
||||
|
||||
hdr "P1 show ../x with no config file (mutwt carries D01 when run by this script)"
|
||||
run_case p1 "$W/no-such-config.json" show ../x
|
||||
|
||||
D="$(root p2)"; mkdir -p "$D/runs/$A"; printf '%s\n' "$RESULT" > "$D/runs/$A/result.json"; chmod 000 "$D/runs/$A"
|
||||
hdr "P2 show of a run directory with mode 000"; run_case p2 "$W/p2/config.json" show "$A"
|
||||
chmod 755 "$D/runs/$A"
|
||||
|
||||
D="$(root p3)"; mkdir -p "$D/runs/r-.bad" "$D/runs/r-"; printf '%s\n' "$RESULT" > "$D/runs/r-.bad/result.json"
|
||||
hdr "P3 list with r- names that aren't valid run ids"; run_case p3 "$W/p3/config.json" list
|
||||
|
||||
D="$(root p4)"; mkdir -p "$D/runs/$A"; printf '%s\n' "$RESULT" > "$D/runs/$A/result.json"; ln -s . "$D/runs/r-self"; mv "$D/runs" "$D/real-runs"; ln -s real-runs "$D/runs"
|
||||
hdr "P4 runs/ is a link inside the data root"; run_case p4 "$W/p4/config.json" list
|
||||
|
||||
D="$(root p5)"; ln -s "$W/p5/outside" "$D/state"
|
||||
hdr "P5 state/ linked out of the data root, no active.json or log there"
|
||||
(cd $S/cand && node -e '
|
||||
import("./packages/runs/src/index.mjs").then((m) => {
|
||||
for (const f of ["readActivePointer", "readActivationLog"]) {
|
||||
try { console.log(" " + f + ": " + JSON.stringify(m[f](process.argv[1]))); } catch (e) { console.log(" " + f + ": " + e.name + " " + e.exitCode + " " + e.message); }
|
||||
}
|
||||
});' "$D")
|
||||
printf '{"pointerVersion":1,"release":"x","imageTag":"y","activatedAt":"z"}\n' > "$W/p5/outside/active.json"
|
||||
hdr "P5b same, with active.json outside"
|
||||
(cd $S/cand && node -e '
|
||||
import("./packages/runs/src/index.mjs").then((m) => {
|
||||
try { console.log(" readActivePointer: " + JSON.stringify(m.readActivePointer(process.argv[1]))); } catch (e) { console.log(" readActivePointer: " + e.name + " " + e.exitCode + " " + e.message.replace(process.argv[1], "<D>")); }
|
||||
});' "$D")
|
||||
|
||||
D="$(root p6)"; mkdir -p "$D/state"
|
||||
printf '%s\n' '{"at":"a","event":"activate","release":"0.0.1","imageTag":"t1","from":"0.0.0"}' '{"at":"b","event":"rollback","release":"0.0.0","imageTag":5}' > "$D/state/activation-log.jsonl"
|
||||
hdr "P6 log lines release.sh rollback would still use (an extra key, a non-string imageTag)"
|
||||
(cd $S/cand && node -e '
|
||||
import("./packages/runs/src/index.mjs").then((m) => console.log(" " + JSON.stringify(m.readActivationLog(process.argv[1]))));' "$D")
|
||||
echo; echo DONE
|
||||
@@ -0,0 +1,131 @@
|
||||
# Row 56, runs and releases reader, round 1 review (Darkwing)
|
||||
|
||||
Issue #1545, packet comment 27109, request comment 27113, queue revs
|
||||
343-347. Brief: `docs/plans/2026-10-10_design-implementation.md`, section
|
||||
"Runs and releases reader module". Base `dc96f87b`. Candidate manifest
|
||||
sha256 `ed3c5392ae43e1c3541c6bab6a2f25826514124f5f68536e5cc330a39cc5d7a3`,
|
||||
`build.patch` sha256 `dd7b469b…`. Both match comment 27109, and the
|
||||
packet manifest checks clean.
|
||||
|
||||
Verdict: **approve**. The module reads and never writes, nothing it
|
||||
returns comes from outside the data root, and `list` and `show` print the
|
||||
same bytes as before on Rocko's seeded data root. Every suite is green.
|
||||
Nothing blocks. The notes below are test gaps and README wording, plus
|
||||
one disagreement with the packet: mutant M31 is not equivalent.
|
||||
|
||||
## Method
|
||||
|
||||
- I read the whole patch: the five `src/` files, the three test files,
|
||||
the README, the `mosaic-task.mjs` change, and Rocko's `seed.sh`,
|
||||
`byte-check.sh` and `delta-check.sh`. I compared the pointer and log
|
||||
readers against `scripts/release.sh` (`append_log`, the `activate`
|
||||
printf, `rollback`'s log scan and `status`).
|
||||
- Detached worktrees at `dc96f87b`: `base` unchanged, `cand` with
|
||||
`git apply --index build.patch`, then `sha256sum -c` on the manifest,
|
||||
15 OK. A third worktree, built the same way, for mutants. It checks 15
|
||||
OK after the runs (`r1/mut/manifest-after.txt`).
|
||||
- `grep` for any write, mkdir, rename, unlink, chmod, open or symlink call
|
||||
in `packages/runs/src`: none. The patch touches no Q14-frozen path.
|
||||
- Rocko's `byte-check.sh` and `delta-check.sh`, base against candidate,
|
||||
with work directories in my scratch rather than `/tmp`.
|
||||
- 22 mutants of my own (`r1/mut/mutate.py`, exact text that must match
|
||||
once), each run against the `packages/runs` suite.
|
||||
- Six probes for cases the packet doesn't cover (`r1/probes.sh`, output
|
||||
in `r1/mut/probes.txt`), run against base, candidate and a candidate
|
||||
carrying mutant D01.
|
||||
|
||||
Node v26.8.1, `TMPDIR=~/darkwing-scratch/r56a/tmp`. `gate.sh` with
|
||||
`DOCKER_HOST=unix:///nonexistent.sock` ran 16:55:29Z to 16:57:50Z.
|
||||
Mutants ran 16:57:19Z to 16:57:40Z in the other worktree, then probes,
|
||||
then `test-release` with Docker, which finished at 16:58:33Z.
|
||||
|
||||
## Suites
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| packages/runs (node) | 37/0 |
|
||||
| test-auth | 15/0 |
|
||||
| test-conductor | 17/0 |
|
||||
| test-config | 24/0 |
|
||||
| test-discord | 66/0 |
|
||||
| test-extension-package | 18/0 |
|
||||
| test-foundation | 44/0 |
|
||||
| test-queue | 27/0 |
|
||||
| test-release | 4/0 without Docker, 14/0 with it (`test-release-docker.txt`) |
|
||||
| test-task, Docker unreachable | 26/0, four skip lines |
|
||||
| byte-check.sh | 16 cases identical, data root unchanged |
|
||||
| delta-check.sh | same output as Rocko's apart from the scratch path in one stack trace |
|
||||
|
||||
I didn't run test-task with real Docker, because it makes live model
|
||||
calls. Rocko's gate ran it at 98/0, and the change to `mosaic-task.mjs` is
|
||||
limited to `list` and `show`, which the package's CLI tests and the byte
|
||||
check cover.
|
||||
|
||||
## Mutants
|
||||
|
||||
17 of 22 killed (`r1/mut/summary.txt`). The five survivors:
|
||||
|
||||
| Mutant | Change | Observable? |
|
||||
|---|---|---|
|
||||
| D01 | the CLI's `isRunId` check before `loadConfig` removed (Rocko's M31) | yes, probe P1 |
|
||||
| D10 | `isInside` treats a path equal to the data root as outside | only when a link resolves to the data root itself |
|
||||
| D17 | `listRunRecords` reads through `readRunDocument`, which checks the id | yes, probe P3: `list` exits 4 on a run named `r-` |
|
||||
| D20 | `listRunIds` keeps `isRunId` names only | yes, probe P3: `list` hides `r-` and `r-.bad` |
|
||||
| D22 | a log entry without `imageTag` accepted | yes, by reading the log |
|
||||
|
||||
## Probes
|
||||
|
||||
| Probe | Base | Candidate |
|
||||
|---|---|---|
|
||||
| P1 `show ../x` with no config file | exit 4, `invalid run id` | the same. With D01: exit 3, `configuration problem` |
|
||||
| P2 `show` of a run directory with mode 000 | prints two lines, then a stack trace, exit 1 | `cannot read ...: EACCES`, exit 4 |
|
||||
| P3 `list` with runs named `r-` and `r-.bad` | lists both | the same |
|
||||
| P4 `runs/` a link inside the data root, plus a run linked to `.` | lists both | the same |
|
||||
| P5 `state/` linked out, no files there | not in base | pointer `null`, log empty. With `active.json` there: refuses, exit 4 |
|
||||
| P6 log lines with an extra key, and a numeric `imageTag` | not in base | both counted in `malformed` |
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. M31 isn't equivalent. The `isRunId` check in `showRun` is the only id
|
||||
check that runs before `loadConfig`, so it decides the exit code and
|
||||
message when the config is bad (P1: 4 against 3). The candidate keeps
|
||||
the base order, so nothing regresses, but a test of `show ../x` with
|
||||
`MOSAIC_CONFIG` pointing at a missing file would pin it. I agree that
|
||||
M04 can't be reached on POSIX.
|
||||
2. No test lists a run whose name starts with `r-` but isn't a valid id,
|
||||
so D17 and D20 survive. `runs.mjs` says such names are still listed,
|
||||
and base does list them. One `listRunRecords` case with `r-` or
|
||||
`r-.bad` would kill both.
|
||||
3. `logEntry` accepts an entry with no `imageTag` (D22). The malformed
|
||||
cases test a wrong type, a null `note` and an extra key, but not a
|
||||
missing key.
|
||||
4. README wording, three places:
|
||||
- "`runs/` or `state/` resolving outside refuses" holds for `runs/`.
|
||||
For `state/`, the module resolves only the file, so a `state/` link
|
||||
out with no files behind it reads as no release and an empty log
|
||||
(P5). Nothing outside is read, so the invariant holds and only the
|
||||
sentence is wrong.
|
||||
- "the way `release.sh rollback` skips them": rollback skips only lines
|
||||
that don't parse. P6's two lines would still be used by rollback and
|
||||
are counted malformed here. `append_log` writes only the five keys,
|
||||
so real logs don't hit this. The stricter reader is fine. The
|
||||
comparison in the README and in `state.mjs` isn't accurate.
|
||||
- The delta table has no row for P2. `show` of a run directory it
|
||||
can't read used to crash after printing two lines. It now refuses
|
||||
with exit 4. That's an improvement and the package test covers it,
|
||||
but `delta-check.sh` doesn't record it.
|
||||
5. D10 needs no test. It matters only for a link that resolves to the
|
||||
data root itself, which is inside by any reading.
|
||||
6. The `padEnd` crash in `list` that Rocko reported is still there and
|
||||
still predates this row. Sage files it as a follow-up.
|
||||
|
||||
## Files
|
||||
|
||||
- `r1/candidate-manifest.sha256`: copy of Rocko's.
|
||||
- `r1/gate.sh`, `r1/out/`: suite runs, `summary.txt`, byte and delta
|
||||
checks, test-release with Docker.
|
||||
- `r1/mut/`: mutant definitions, runner, diffs, outputs, `summary.txt`
|
||||
and the manifest check after the runs.
|
||||
- `r1/probes.sh`, `r1/mut/probes.txt`: the six probes. In that run the
|
||||
third tree (`mutwt`) carries D01, and the D17 and D20 runs of P3 follow
|
||||
at the end.
|
||||
Reference in New Issue
Block a user