mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
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.
150 lines
6.2 KiB
TypeScript
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);
|
|
});
|
|
});
|