openclaw/test/scripts/pr-worktree-state.test.ts
Peter Steinberger 994e9bb445
test: select Node for maintainer tooling fixtures (#162550)
Run Node-only maintainer tooling fixtures through the existing Node-selection helpers while keeping Vitest on its selected runtime. Preserve package-integrity, update interruption, isolation, and declaration assertions, and restore Bun-hosted compensation coverage.

Proof: all 21 consumers pass on Node 24 and Bun (1043 passed, four existing skips each); changed-file checks and import-cycle checks pass; independent P2 and exact-head ClawSweeper reviews found no actionable defects.

Land through the authorized native exception for CI run 36837194384 attempt 1: the untouched Windows session-creation path-alias failure independently reproduces on main; nine downstream cancelled jobs remain unrun coverage.
2026-10-01 02:20:15 -07:00

150 lines
6.2 KiB
TypeScript

import {
copyFileSync,
existsSync,
linkSync,
mkdirSync,
readdirSync,
readFileSync,
renameSync,
symlinkSync,
unlinkSync,
writeFileSync,
} from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import { requireNodeTool } from "../helpers/node-toolchain.js";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
import {
createProvisionOwnerFixture,
expectProvisionLeaseReleased,
expectProvisionSeed,
} from "./pr-worktree-owner.test-support.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
const describePosix = process.platform === "win32" ? describe.skip : describe;
describePosix("repository-owned PR provisioning state", () => {
it.runIf(process.platform === "linux")(
"releases its completed operation after exited loader descendants await reaping",
() => {
const f = createProvisionOwnerFixture(tempDirs.make("openclaw-pr-exited-loaders-"));
delete f.env.OPENCLAW_CONFIG_PATH;
const result = f.run("entry", "", { holdExitedDescendants: true });
expect(result.status, result.stdout + result.stderr).toBe(0);
expect(result.stdout).toMatch(/successfully reaped: [1-9]/u);
f.isolation.assertProvisioner();
expectProvisionSeed(f);
expectProvisionLeaseReleased(f);
expect(f.git(f.canonical, "for-each-ref", "refs/openclaw/pr-operation-locks")).toBe("");
expect(existsSync(join(f.home, ".openclaw"))).toBe(false);
},
);
it.each([
{ mode: "native", override: false },
{ mode: "native", override: true },
{ mode: "managed", override: false },
{ mode: "managed", override: true },
] as const)(
"isolates $mode state from HOME (operator override=$override)",
({ mode, override }) => {
const f = createProvisionOwnerFixture(tempDirs.make("openclaw-pr-state-"), mode);
// On supported filesystems, this also exercises the template registry writer.
writeFileSync(f.env.OPENCLAW_CONFIG_PATH!, "{}\n");
const operator = join(f.root, "operator-state");
if (override) {
mkdirSync(operator);
writeFileSync(join(operator, "operator-evidence"), "leave untouched");
f.env.OPENCLAW_STATE_DIR = operator;
} else {
delete f.env.OPENCLAW_CONFIG_PATH;
}
// The native entrypoints carry explicit argv binding, not NODE_OPTIONS.
if (override) {
f.env.NODE_OPTIONS = "--no-warnings";
} else {
delete f.env.NODE_OPTIONS;
}
const result = f.run();
expect(result.status, result.stderr).toBe(0);
f.isolation.assertProvisioner();
expectProvisionSeed(f);
expect(existsSync(join(f.home, ".openclaw"))).toBe(false);
expect(existsSync(join(f.canonical, ".local", "pr-state", "state", "openclaw.sqlite"))).toBe(
true,
);
expectProvisionLeaseReleased(f);
expect(f.git(f.canonical, "for-each-ref", "refs/openclaw/pr-operation-locks")).toBe("");
if (override) {
expect(readdirSync(operator)).toEqual(["operator-evidence"]);
expect(readFileSync(join(operator, "operator-evidence"), "utf8")).toBe("leave untouched");
} else {
expect(existsSync(operator)).toBe(false);
}
},
);
it("refuses a fresh provisioner whose handoff argv binding was stripped", () => {
const f = createProvisionOwnerFixture(tempDirs.make("openclaw-pr-stripped-binding-"));
const quote = (value: string) => `'${value.replace(/'/gu, `'\\''`)}'`;
// Keep the pre-open observer but remove only the child's resolver import.
// The parent still has a valid binding: its witness cannot qualify this PID.
writeFileSync(
join(f.isolation.bin, "node"),
`#!/bin/sh\nexec ${quote(requireNodeTool("node"))} ${quote(f.isolation.nodeArgs[1]!)} "$@"\n`,
);
const result = f.run();
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("Missing private provisioner handoff preload");
expect(readFileSync(f.isolation.observations, "utf8")).toBe("");
expect(existsSync(f.isolation.binding.databasePath)).toBe(false);
expect(existsSync(f.isolation.stateDatabase)).toBe(false);
});
it.each(["missing", "replaced"] as const)(
"refuses a %s binding directory before opening either store",
(fault) => {
const f = createProvisionOwnerFixture(tempDirs.make("openclaw-pr-binding-identity-"));
const preload = fileURLToPath(f.isolation.binding.nodeOption.slice("--import=".length));
if (fault === "missing") {
unlinkSync(preload);
} else {
const retained = join(f.root, "replaced-handoff");
renameSync(f.isolation.binding.directory, retained);
mkdirSync(f.isolation.binding.directory, { mode: 0o700 });
copyFileSync(join(retained, "handoff-resolver-preload.mjs"), preload);
}
const result = f.run();
expect(result.status).not.toBe(0);
expect(result.stderr).toMatch(
fault === "missing" ? /ERR_MODULE_NOT_FOUND/ : /directory identity changed/,
);
expect(readFileSync(f.isolation.observations, "utf8")).toBe("");
expect(existsSync(f.isolation.binding.databasePath)).toBe(false);
expect(existsSync(f.isolation.stateDatabase)).toBe(false);
},
);
it.each([
{ kind: "symlink", suffix: "" },
{ kind: "hardlink", suffix: "" },
{ kind: "symlink", suffix: "-wal" },
{ kind: "hardlink", suffix: "-wal" },
] as const)("refuses a handoff $kind $suffix before store access", ({ kind, suffix }) => {
const f = createProvisionOwnerFixture(tempDirs.make("openclaw-pr-binding-alias-"));
const target = join(f.root, "private-negative-control");
writeFileSync(target, "do not open", { mode: 0o600 });
const alias = f.isolation.binding.databasePath + suffix;
if (kind === "symlink") {
symlinkSync(target, alias);
} else {
linkSync(target, alias);
}
const result = f.run();
expect(result.status).not.toBe(0);
expect(result.stderr).toMatch(/database (alias|hardlink) is forbidden/);
expect(readFileSync(target, "utf8")).toBe("do not open");
expect(readFileSync(f.isolation.observations, "utf8")).toBe("");
expect(existsSync(f.isolation.stateDatabase)).toBe(false);
});
});