Files
mesh-catalog/modules/lab/test/runs.test.ts
T
jochen acf8a76646
mesh/merge-gate head acf8a76646de merged into trunk ebf7f3773dc7 — pass: builds lab → shanks; no bus step; every machine composes with the change as it did…
mesh/repo-check head acf8a76646de merged into trunk ebf7f3773dc7 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/delivery-group group fix/693-a-run-that-proved-nothing-is-not-passed delivered: every member is delivered
lab: read the suite's verdict only after its recorded line, NOT REACHED in any case (issue 693, review 254)
A test's own output, printed before the suite reports, could carry a line
shaped like the suite's NOT PASSED or skipped line and stand for its verdict.
2026-10-14 03:28:20 +02:00

489 lines
29 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from "node:test";
import assert from "node:assert/strict";
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { installPlan, installStep, NPM_REPOSITORIES, outcomeOf, readStatus, REPOSITORIES, script } from "../tools/runs.ts";
// Defends novox/hq issue 624: lab_run ran `npm ci` at the root of every checkout it installs, and
// mesh-tools keeps its package in node-tools/ with no lock file at its root, so every run failed at its
// setup, in 9 seconds, before anything was built.
function aCheckout(files: Record<string, string>): string {
const root = mkdtempSync(join(tmpdir(), "lab-checkout-"));
for (const [path, content] of Object.entries(files)) {
mkdirSync(join(root, path, ".."), { recursive: true });
writeFileSync(join(root, path), content);
}
return root;
}
test("a checkout with a lock file at its root is installed there", () => {
const root = aCheckout({ "package.json": "{}", "package-lock.json": "{}", "node-tools/package.json": "{}" });
assert.deepEqual(installPlan(root), { install: [root], unlocked: [] });
rmSync(root, { recursive: true, force: true });
});
test("a checkout whose package lives one directory down is installed there, as mesh-tools' node-tools", () => {
const root = aCheckout({ "node-tools/package.json": "{}", "node-tools/package-lock.json": "{}", "Dockerfile": "" });
assert.deepEqual(installPlan(root), { install: [join(root, "node-tools")], unlocked: [] });
rmSync(root, { recursive: true, force: true });
});
test("a package.json with no lock file is named, never installed", () => {
const root = aCheckout({ "package.json": "{}" });
assert.deepEqual(installPlan(root), { install: [], unlocked: [root] });
rmSync(root, { recursive: true, force: true });
});
// The shell a run executes follows the same rule: npm is asked where installPlan says, and a checkout
// with nothing installable fails the run loudly, naming it.
function runStep(root: string, build: boolean): { ok: boolean; said: string; asked: string[] } {
const bin = mkdtempSync(join(tmpdir(), "lab-npm-"));
const record = join(bin, "asked");
writeFileSync(join(bin, "npm"), `#!/bin/sh\necho "$(pwd) $*" >> ${JSON.stringify(record)}\n`);
chmodSync(join(bin, "npm"), 0o755);
const ran = spawnSync("bash", ["-c", `set -euo pipefail\n${installStep(root, build)}`], {
encoding: "utf8", env: { ...process.env, PATH: `${bin}:${process.env.PATH}` },
});
let asked: string[] = [];
try {
asked = readFileSync(record, "utf8").trim().split("\n").filter(Boolean);
} catch { /* nothing asked */ }
rmSync(bin, { recursive: true, force: true });
return { ok: ran.status === 0, said: `${ran.stdout}${ran.stderr}`, asked };
}
test("a run installs and builds mesh-tools in node-tools, where its lock file is", () => {
const root = aCheckout({ "node-tools/package.json": "{}", "node-tools/package-lock.json": "{}" });
const r = runStep(root, true);
assert.ok(r.ok, r.said);
assert.deepEqual(r.asked, [
`${join(root, "node-tools")} ci --no-audit --no-fund --loglevel=error`,
`${join(root, "node-tools")} run build --if-present`,
]);
rmSync(root, { recursive: true, force: true });
});
test("a run installs a checkout with its lock file at the root there, and builds only when asked", () => {
const root = aCheckout({ "package.json": "{}", "package-lock.json": "{}" });
const r = runStep(root, false);
assert.ok(r.ok, r.said);
assert.deepEqual(r.asked, [`${root} ci --no-audit --no-fund --loglevel=error`]);
rmSync(root, { recursive: true, force: true });
});
test("a run fails at its setup, naming the checkout, when nothing of it can be installed", () => {
const layouts: Record<string, string>[] = [{ "package.json": "{}" }, { "Dockerfile": "" }, { "sub/package.json": "{}" }];
for (const files of layouts) {
const root = aCheckout(files);
const r = runStep(root, false);
assert.equal(r.ok, false, `installed ${JSON.stringify(files)}`);
assert.match(r.said, /issue 624/);
assert.deepEqual(r.asked, []);
rmSync(root, { recursive: true, force: true });
}
});
// A run checks out mesh-tools-go too (novox/hq issue 616): genesis builds the Go toolchain from the
// repository beside mesh-tools, and a bed that raises genesis serves it. And it tells the beds where the
// checkouts are.
test("a run checks out mesh-tools-go and says where its checkouts are", () => {
assert.ok(REPOSITORIES.includes("mesh-tools-go"));
const s = script("/work", "run-x", "http://forge", ["test/integration/genesis-keeps-its-bus.test.ts"], {});
assert.match(s, /novox\/mesh-tools-go\.git' '\/work\/run-x\/mesh-tools-go'/);
assert.match(s, /export MESH_LAB_REPOS='\/work\/run-x'/);
assert.doesNotMatch(s, /cd '\/work\/run-x'\/\$repo && npm ci/);
for (const repo of NPM_REPOSITORIES) assert.ok(s.includes(`'/work/run-x/${repo}'/package-lock.json`), repo);
});
// Defends novox/hq issue 624, its second half: mesh-tools-go is private, and the lab cloned over HTTP with
// no credential ("could not read Username"). A run clones with the forge credential the mesh binds and
// seals to the lab, through git's credential store, and the secret is in no script, URL or argument.
import { credentialArgs, credentialPath, forgeCredential } from "../tools/runs.ts";
function aBinding(as: string, secret: string): { binding: string; secret: string } {
const dir = mkdtempSync(join(tmpdir(), "lab-binding-"));
writeFileSync(join(dir, "package-registry.json"), JSON.stringify({ provision: "npm-package-registry", at: "10.42.0.1", as,
serves: { scheme: "http", port: 3000 } }));
writeFileSync(join(dir, "package-registry.secret"), `${secret}\n`);
return { binding: join(dir, "package-registry.json"), secret: join(dir, "package-registry.secret") };
}
test("the forge credential is the bound account and its sealed secret, for the forge's own origin", () => {
const b = aBinding("lab-shanks", "s3cret-of-forty-characters");
assert.equal(forgeCredential("http://novox.internal:20000", b.binding, b.secret),
"http://lab-shanks:s3cret-of-forty-characters@novox.internal:20000");
assert.equal(forgeCredential("http://novox.internal:20000/", b.binding, ""), undefined);
assert.equal(forgeCredential("", b.binding, b.secret), undefined);
assert.equal(forgeCredential("http://novox.internal:20000", "/nowhere", b.secret), undefined);
});
test("a run clones through git's credential store, and its script carries no secret", () => {
const b = aBinding("lab-shanks", "s3cret-of-forty-characters");
const credential = forgeCredential("http://novox.internal:20000", b.binding, b.secret);
const path = credentialPath("/work", "run-x");
const s = script("/work", "run-x", "http://novox.internal:20000", ["t.test.ts"], {}, path);
assert.ok(credential);
assert.doesNotMatch(s, /s3cret/);
assert.match(s, /git -c credential\.helper= -c 'credential\.helper=store --file=\/work\/\.run-x\.git-credentials' clone --quiet --depth 50 --branch 'main' 'http:\/\/novox\.internal:20000\/novox\/mesh-tools-go\.git'/);
assert.match(s, /export GIT_TERMINAL_PROMPT=0/);
assert.equal(path.startsWith("/work/run-x/"), false, "the credential is kept out of the run's own directory");
assert.equal(credentialArgs(undefined), "");
});
// Held against git itself: the credential in the store is offered to the forge's origin when it asks, and
// a clone without it fails at once rather than prompting.
test("git presents the stored credential to the forge, and only when one is given", () => {
const dir = mkdtempSync(join(tmpdir(), "lab-git-"));
const store = join(dir, "git-credentials");
writeFileSync(store, "http://lab-shanks:pw@forge.invalid:20000\n", { mode: 0o600 });
const fill = spawnSync("sh", ["-c", `printf 'protocol=http\\nhost=forge.invalid:20000\\n\\n' | git ${credentialArgs(store)}credential fill`],
{ encoding: "utf8", env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });
assert.match(fill.stdout, /username=lab-shanks/);
assert.match(fill.stdout, /password=pw/);
const other = spawnSync("sh", ["-c", `printf 'protocol=http\\nhost=elsewhere.invalid\\n\\n' | git ${credentialArgs(store)}credential fill`],
{ encoding: "utf8", env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });
assert.doesNotMatch(other.stdout, /password=pw/);
rmSync(dir, { recursive: true, force: true });
});
// Review 196 on mesh-catalog #272: the credential file outlived a stop, because SIGTERM to the run's
// process group killed the wrapper before its cleanup. It is now removed right after the clones, on
// every way the script ends, and at the next run's start and in lab_check for what a crash left.
import { cleanCredentials, credentialGuard } from "../tools/runs.ts";
test("a stopped script removes the credential, whatever ends it", () => {
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"] as const) {
const dir = mkdtempSync(join(tmpdir(), "lab-guard-"));
const file = join(dir, ".run-x.git-credentials");
writeFileSync(file, "http://u:p@h\n", { mode: 0o600 });
const ran = spawnSync("bash", ["-c", `${credentialGuard(file)}\nkill -${signal.slice(3)} $$\nsleep 5`], { encoding: "utf8", timeout: 10_000 });
assert.notEqual(ran.status, 0, `${signal}: the script was not stopped`);
assert.equal(existsSync(file), false, `${signal} left the credential on disk`);
rmSync(dir, { recursive: true, force: true });
}
});
test("the script removes the credential as soon as its clones are done", () => {
const s = script("/work", "run-x", "http://forge", ["t.test.ts"], {}, "/work/.run-x.git-credentials");
const lastClone = s.lastIndexOf("clone --quiet");
const removed = s.indexOf("rm -f '/work/.run-x.git-credentials'", lastClone);
const building = s.indexOf("npm ci");
assert.ok(lastClone > 0 && removed > lastClone && removed < building, "not removed between the clones and the build");
assert.match(s, /trap 'rm -f '\\''\/work\/\.run-x\.git-credentials'\\''' EXIT/);
});
test("credentials a crashed run left are removed, but not the running one's", () => {
const work = mkdtempSync(join(tmpdir(), "lab-work-"));
for (const n of [".run-a.git-credentials", ".run-b.git-credentials", "run-a", "other"]) writeFileSync(join(work, n), "x");
const removed = cleanCredentials(work, join(work, ".run-b.git-credentials"));
assert.deepEqual(removed, [join(work, ".run-a.git-credentials")]);
assert.ok(existsSync(join(work, ".run-b.git-credentials")) && existsSync(join(work, "run-a")) && existsSync(join(work, "other")));
rmSync(work, { recursive: true, force: true });
});
// The lab asks the forge for read only (review 196): its credential is for clones of branches nobody has
// reviewed yet, and must not be able to publish the mesh's packages.
test("the lab asks the package registry for read only", () => {
const m = JSON.parse(readFileSync(join(import.meta.dirname, "..", "module.json"), "utf8")) as {
requires: unknown[]; contributes?: Record<string, Record<string, unknown>>;
};
assert.ok(m.requires.includes("npm-package-registry"));
assert.equal(m.contributes?.["npm-package-registry"]?.access, "read");
});
// lab_check failed for ever on "destination path '<work>/check' already exists": it cloned into one fixed
// path and ignored whether removing the last one worked (novox/hq issue 624). Each check now works in a
// fresh directory, and what earlier checks left is cleared or named.
import { checkDir, clearChecks } from "../tools/runs.ts";
test("each check gets a fresh directory of its own", () => {
const work = mkdtempSync(join(tmpdir(), "lab-work-"));
const a = checkDir(work);
const b = checkDir(work);
assert.notEqual(a, b);
assert.ok(a.startsWith(join(work, "check-")) && existsSync(a) && existsSync(b));
rmSync(work, { recursive: true, force: true });
});
// As root nothing is locked against removal, so the unremovable tree cannot be made: skipped there, before
// it makes anything (the merge check runs as root).
test("what earlier checks left is cleared, and what cannot be is named rather than tripped over", {
skip: process.getuid?.() === 0 ? "running as root, which no directory mode stops from removing anything" : false,
}, () => {
const work = mkdtempSync(join(tmpdir(), "lab-work-"));
for (const d of ["check/src", "check-setup/mesh-lab", "check-abc", "run-x", "keep"]) mkdirSync(join(work, d), { recursive: true });
// A tree this account cannot empty: a directory it may not write, with something in it.
mkdirSync(join(work, "check-stuck", "locked"), { recursive: true });
writeFileSync(join(work, "check-stuck", "locked", "file"), "x");
chmodSync(join(work, "check-stuck", "locked"), 0o555);
const said = clearChecks(work);
chmodSync(join(work, "check-stuck", "locked"), 0o755);
for (const gone of ["check", "check-setup", "check-abc"]) assert.equal(existsSync(join(work, gone)), false, gone);
assert.ok(existsSync(join(work, "run-x")) && existsSync(join(work, "keep")));
assert.equal(said.length, 1);
assert.match(said[0] as string, /check-stuck, left by an earlier check, could not be removed/);
rmSync(work, { recursive: true, force: true });
});
// And as root too: what earlier checks left is cleared.
test("leftover checks are cleared, whoever runs the check", () => {
const work = mkdtempSync(join(tmpdir(), "lab-work-"));
for (const d of ["check/src", "check-setup/mesh-lab", "check-abc", "run-x"]) mkdirSync(join(work, d), { recursive: true });
assert.deepEqual(clearChecks(work), []);
for (const gone of ["check", "check-setup", "check-abc"]) assert.equal(existsSync(join(work, gone)), false, gone);
assert.ok(existsSync(join(work, "run-x")));
rmSync(work, { recursive: true, force: true });
});
// The lab's tools read the bound credential, so the runtime is given that file by the tools bundle's env
// (ADR 0192), as gitea and umami are given theirs: the runtime's account may then read exactly it, and no
// other credential changes hands (issue 624).
test("the lab's tools bundle names its bound credential and binding in its env", () => {
const m = JSON.parse(readFileSync(join(import.meta.dirname, "..", "module.json"), "utf8")) as {
secrets: Record<string, string>; binds: Record<string, string>;
build: { artifacts: { name: string; env?: Record<string, string> }[] };
};
const env = m.build.artifacts.find((a) => a.name === "code")?.env ?? {};
assert.equal(env.MESH_LAB_FORGE_SECRET, m.secrets["npm-package-registry"]);
assert.equal(env.MESH_LAB_FORGE_BINDING, m.binds["npm-package-registry"]);
});
// The next setup failure of novox/hq issue 624 (run-2026-10-13T13-15-26-634Z on shanks): npm 12 refused
// mesh-tools' locked @novox/mesh-sdk with EALLOWREMOTE. The lab named the registry by its forge setting's
// address (http://novox.internal:20000/…), the lock file holds the address the forge writes every tarball
// URL at (https://git.novox.be/…), and npm 12 installs a locked tarball only from its configured registry.
// A run names the registry by the address the registry names itself by.
import { npmRegistry } from "../tools/runs.ts";
function aRegistry(answer: () => Response | Promise<Response>): { get: typeof fetch; asked: string[] } {
const asked: string[] = [];
const get = (async (url: string | URL | Request) => {
asked.push(String(url));
return answer();
}) as typeof fetch;
return { get, asked };
}
const packument = (tarball: string) => new Response(JSON.stringify({
name: "@novox/mesh-sdk", "dist-tags": { latest: "0.1.14" },
versions: { "0.1.1": { dist: { tarball: "http://old.example/api/packages/novox/npm/x.tgz" } }, "0.1.14": { dist: { tarball } } },
}), { status: 200, headers: { "content-type": "application/json" } });
test("a run names the package registry by the address the registry gives its tarballs", async () => {
const r = aRegistry(() => packument("https://forge.example.org/api/packages/novox/npm/%40novox%2Fmesh-sdk/-/0.1.14/mesh-sdk-0.1.14.tgz"));
const got = await npmRegistry("http://forge.example.org:3000/", r.get, "https://forge.example.org/api/packages/novox/npm/");
assert.deepEqual(got, { registry: "https://forge.example.org/api/packages/novox/npm/" });
assert.deepEqual(r.asked, ["http://forge.example.org:3000/api/packages/novox/npm/@novox%2fmesh-sdk"]);
// The forge's own scheme is taken too, with its port.
const plain = await npmRegistry("http://forge.example.org:3000",
aRegistry(() => packument("http://forge.example.org:3000/api/packages/novox/npm/%40novox%2Fmesh-sdk/-/0.1.14/mesh-sdk-0.1.14.tgz")).get);
assert.deepEqual(plain, { registry: "http://forge.example.org:3000/api/packages/novox/npm/" });
});
// What the registry answers may come over plain HTTP, so it never decides where packages come from: only
// the forge's public root or its own address, scheme, host and port, under the npm path, with nothing after
// it (review of mesh-catalog #275, ADR 0346).
test("a registry that does not give an address the lab takes leaves the forge's, and the reason is said", async () => {
const sdk = "/api/packages/novox/npm/%40novox%2Fmesh-sdk/-/0.1.14/mesh-sdk-0.1.14.tgz";
for (const [answer, why] of [
[() => new Response("not found", { status: 404 }), /it answered 404/],
[() => new Response("{}", { status: 200 }), /no tarball/],
[() => { throw new Error("connect ECONNREFUSED"); }, /ECONNREFUSED/],
[() => packument(`https://forge.example.org/elsewhere/mesh-sdk-0.1.14.tgz`), /not under \/api\/packages\/novox\/npm\//],
[() => packument(`https://attacker.example.net${sdk}`), /at https:\/\/attacker\.example\.net, which is neither the forge's public root nor its own address/],
[() => packument(`https://attacker.example.net/x?u=https://novox.internal${sdk}`), /query or a fragment/],
[() => packument(`https://novox.internal${sdk}?x=1`), /query or a fragment/],
[() => packument(`https://novox.internal${sdk}#frag`), /query or a fragment/],
[() => packument(`https://attacker.example.net/#https://novox.internal${sdk}`), /query or a fragment/],
[() => packument(`ftp://novox.internal${sdk}`), /at ftp:\/\/novox\.internal, which is neither/],
[() => packument(`file:///api/packages/novox/npm/x.tgz`), /which is neither/],
[() => packument(`https://novox.internal/api/packages/novox/npm/\nregistry=https://attacker.example.net/\nallow-remote=all`), /whitespace or a control character/],
[() => packument(`https://user:pw@novox.internal${sdk}`), /user or password/],
] as [() => Response, RegExp][]) {
const got = await npmRegistry("http://novox.internal:20000", aRegistry(answer).get);
assert.equal(got.registry, "http://novox.internal:20000/api/packages/novox/npm/", String(why));
assert.match(got.problem ?? "", /did not give an address the lab takes/);
assert.match(got.problem ?? "", why);
}
});
test("a run's npm configuration names the registry it was given, and the forge's own address only without one", () => {
const given = script("/work", "run-x", "http://novox.internal:20000", ["t.test.ts"], {}, undefined,
"https://forge.example.org/api/packages/novox/npm/");
assert.match(given, / '@novox:registry=https:\/\/forge\.example\.org\/api\/packages\/novox\/npm\/' > '\/work\/run-x\/\.npmrc'\n/);
assert.doesNotMatch(given, /@novox:registry=http:\/\/novox\.internal/);
const none = script("/work", "run-x", "http://novox.internal:20000", ["t.test.ts"], {});
assert.match(none, /'@novox:registry=http:\/\/novox\.internal:20000\/api\/packages\/novox\/npm\/'/);
});
// The public root the forge serves in the lab's binding (novox/hq ADR 0346) is the one other origin a
// tarball may come from, by scheme, host and port: the review of #275 found the port ignored.
import { publicRootOf } from "../tools/runs.ts";
test("a tarball is taken at the forge's public root, and at no other port or scheme of its host", async () => {
const sdk = "/api/packages/novox/npm/%40novox%2Fmesh-sdk/-/0.1.14/mesh-sdk-0.1.14.tgz";
const root = "https://git.example.org/api/packages/novox/npm/";
const at = (tarball: string, publicRoot?: string) =>
npmRegistry("http://novox.internal:20000", aRegistry(() => packument(tarball)).get, publicRoot);
assert.deepEqual(await at(`https://git.example.org${sdk}`, root), { registry: root });
for (const tarball of [`https://git.example.org:8443${sdk}`, `http://git.example.org${sdk}`, `https://example.org${sdk}`]) {
const got = await at(tarball, root);
assert.equal(got.registry, "http://novox.internal:20000/api/packages/novox/npm/", tarball);
assert.match(got.problem ?? "", /neither the forge's public root nor its own address/, tarball);
}
// Without a public root, the forge's public name is not trusted on its own word.
assert.match((await at(`https://git.example.org${sdk}`)).problem ?? "", /neither/);
// And the forge's own address only at its own port.
assert.match((await at(`http://novox.internal${sdk}`, root)).problem ?? "", /neither/);
});
test("the public root is read from the binding, and only an https root at the npm path is taken", () => {
const dir = mkdtempSync(join(tmpdir(), "lab-root-"));
const binding = (root: unknown) => {
const f = join(dir, `b-${Math.random()}.json`);
writeFileSync(f, JSON.stringify({ provision: "npm-package-registry", at: "home", serves: { scheme: "http", port: 3000,
"npm-path": "/api/packages/novox/npm/", ...(root === undefined ? {} : { "public-root": root }) } }));
return f;
};
assert.deepEqual(publicRootOf(binding("https://git.example.org/api/packages/novox/npm/")),
{ root: "https://git.example.org/api/packages/novox/npm/" });
assert.deepEqual(publicRootOf(binding("https://git.example.org:8443/api/packages/novox/npm/")),
{ root: "https://git.example.org:8443/api/packages/novox/npm/" });
for (const [root, why] of [
[undefined, /serves no public-root/],
["http://git.example.org/api/packages/novox/npm/", /is not https:\/\/<host>/],
["https://git.example.org/elsewhere/", /is not https:\/\/<host>/],
["https://git.example.org/api/packages/novox/npm/?x=1", /is not https:\/\/<host>/],
["https://u:p@git.example.org/api/packages/novox/npm/", /is not https:\/\/<host>/],
["https://git.example.org/api/packages/novox/npm/\nallow-remote=all", /whitespace or a control character/],
["https://${bound:route:name-web}/api/packages/novox/npm/", /not a URL|is not https/],
] as [unknown, RegExp][]) {
const got = publicRootOf(binding(root));
assert.equal(got.root, undefined, String(root));
assert.match(got.problem ?? "", why, String(root));
}
assert.match(publicRootOf(join(dir, "none.json")).problem ?? "", /could not be read/);
assert.match(publicRootOf(undefined).problem ?? "", /no npm-package-registry binding/);
rmSync(dir, { recursive: true, force: true });
});
// A forge setting that is not http(s) is never a trusted origin (review of #280): a `file:` tarball's origin
// is "null", as is a `file:` forge's, and the two must not match.
test("a forge setting that is not http(s) trusts no origin of its own", async () => {
const got = await npmRegistry("file:///srv/forge", aRegistry(() => packument("file:///api/packages/novox/npm/x.tgz")).get);
assert.match(got.problem ?? "", /neither the forge's public root nor its own address/);
const root = "https://git.example.org/api/packages/novox/npm/";
const viaRoot = await npmRegistry("ftp://novox.internal", aRegistry(() =>
packument("ftp://novox.internal/api/packages/novox/npm/x.tgz")).get, root);
assert.match(viaRoot.problem ?? "", /neither/);
});
// Defends novox/hq issue 693: a run whose only test skipped as NOT REACHED read "0 passed, 0 failed", exit 0,
// and lab_status said passed. The log's tail, as run-2026-10-14T00-21-44-014Z on shanks wrote it.
const oldLog = [
"STOPPED at step 16 of 24, base: \t\t\t\tMake sure you have all the referenced content and try again.",
"\ufe63 genesis adopts its bus as the nats module in place: one bus, its streams kept, the node connected (588152.016874ms) # ADR 0333 §5 NOT REACHED: the first run stopped at step 16 of 24, base, before the broker step:",
" \t\t\t\tMake sure you have all the referenced content and try again.",
"NotFound: content digest sha256:e2de96513ba9: not found",
"ℹ tests 1", "ℹ suites 0", "ℹ pass 0", "ℹ fail 0", "ℹ cancelled 0", "ℹ skipped 1", "ℹ todo 0",
"ℹ duration_ms 590254.163993",
"",
"recorded: test/integration/genesis-keeps-its-bus.test.ts — 0 passed, 0 failed, against mesh-lab 7ed7da1",
].join("\n");
// The same run, as the lab's suite says it once it names its skips (mesh-lab, issue 693).
const newLog = [
"ℹ tests 1", "ℹ pass 0", "ℹ fail 0", "ℹ skipped 1",
"",
"recorded: test/integration/genesis-keeps-its-bus.test.ts — 0 passed, 0 failed, 1 skipped, against mesh-lab 7ed7da1",
"NOT PASSED: no test passed: 1 skipped, 1 of them as NOT REACHED",
"skipped: genesis adopts its bus — one bus (test/integration/genesis-keeps-its-bus.test.ts) # ADR 0333 §5 NOT REACHED: the first run stopped at step 16 of 24, base: NotFound: content digest",
].join("\n");
function anEndedRun(log: string, state: "passed" | "failed", exit: number): { work: string; id: string } {
const work = mkdtempSync(join(tmpdir(), "lab-runs-"));
const id = "run-2026-10-14T00-21-44-014Z";
mkdirSync(join(work, id));
writeFileSync(join(work, id, "run.log"), log);
writeFileSync(join(work, id, "status.json"), JSON.stringify({
id, state, exit, started: "2026-10-14T00:21:44.014Z", ended: "2026-10-14T00:33:11.179Z",
tests: ["test/integration/genesis-keeps-its-bus.test.ts"], refs: {},
}));
return { work, id };
}
test("a run whose only test skipped as NOT REACHED, from a lab of before, reads as failed with the reason", () => {
const { work, id } = anEndedRun(oldLog, "passed", 0);
const s = readStatus(work, id);
assert.equal(s?.state, "failed", JSON.stringify(s));
assert.equal(s?.exit, 0);
assert.equal(s?.outcome?.passed, 0);
assert.equal(s?.outcome?.skipped, 1);
assert.equal(s?.outcome?.notPassed, "no test passed: 1 skipped, 1 of them as NOT REACHED");
assert.match(s?.outcome?.skips[0]?.reason ?? "", /^ADR 0333 §5 NOT REACHED: the first run stopped at step 16 of 24, base, before the broker step:$/);
// Kept in the run's record, not only answered.
const kept = JSON.parse(readFileSync(join(work, id, "status.json"), "utf8"));
assert.equal(kept.state, "failed");
assert.equal(kept.outcome.notPassed, "no test passed: 1 skipped, 1 of them as NOT REACHED");
rmSync(work, { recursive: true, force: true });
});
test("the lab's own NOT PASSED and skipped lines are read, a test name's punctuation and all", () => {
const { work, id } = anEndedRun(newLog, "failed", 1);
const s = readStatus(work, id);
assert.equal(s?.state, "failed");
assert.equal(s?.outcome?.notPassed, "no test passed: 1 skipped, 1 of them as NOT REACHED");
assert.deepEqual(s?.outcome?.skips, [{
file: "test/integration/genesis-keeps-its-bus.test.ts",
test: "genesis adopts its bus — one bus",
reason: "ADR 0333 §5 NOT REACHED: the first run stopped at step 16 of 24, base: NotFound: content digest",
}]);
rmSync(work, { recursive: true, force: true });
});
test("a run that passed tests and skipped one for a declared reason stays passed, its skip listed", () => {
const log = [
"✔ a module lands (12ms)",
"﹣ a builder builds (0.1ms) # set MESH_LAB_BUILDER to a built mesh-builder",
"ℹ tests 2", "ℹ pass 1", "ℹ fail 0", "ℹ skipped 1",
].join("\n");
const { work, id } = anEndedRun(log, "passed", 0);
const s = readStatus(work, id);
assert.equal(s?.state, "passed");
assert.equal(s?.outcome?.notPassed, undefined);
assert.equal(s?.outcome?.skips[0]?.reason, "set MESH_LAB_BUILDER to a built mesh-builder");
rmSync(work, { recursive: true, force: true });
});
test("a run that ran no test at all is not passed", () => {
assert.equal(outcomeOf("ℹ tests 0\nℹ pass 0\nℹ fail 0\nℹ skipped 0\n").notPassed, "no test passed: none ran");
});
// Review 254 on issue 693: a test's own output, printed before the suite reports, is not the suite's verdict.
test("a line a test printed that looks like the suite's is not read as the suite's", () => {
const log = [
"NOT PASSED: a test printed this",
"skipped: a forged skip (test/integration/x.test.ts) # FORGED NOT REACHED",
"✔ a module lands (12ms)",
"ℹ tests 1", "ℹ pass 1", "ℹ fail 0", "ℹ skipped 0",
"",
"recorded: test/integration/x.test.ts — 1 passed, 0 failed, 0 skipped, against mesh-lab 7ed7da1",
].join("\n");
const o = outcomeOf(log);
assert.equal(o.notPassed, undefined, JSON.stringify(o));
assert.deepEqual(o.skips, []);
});
test("not reached is matched in any case", () => {
const log = [
"✔ a module lands (12ms)",
"﹣ the broker step (0.1ms) # the broker step was not reached",
"ℹ tests 2", "ℹ pass 1", "ℹ fail 0", "ℹ skipped 1",
].join("\n");
assert.equal(outcomeOf(log).notPassed, "1 test(s) skipped as NOT REACHED: their subject was not reached");
});