File size: 7,138 Bytes
e249c6d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | import fs from "node:fs/promises";
import path from "node:path";
import { configureFsSafeNative, getFsSafeNativeConfig } from "@openclaw/fs-safe/config";
import { afterEach, assert, beforeEach, describe, expect, it, vi } from "vitest";
import { getApfsCloneId } from "../../../test/helpers/apfs.js";
import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js";
import * as runner from "../../process/exec-runner.js";
import { drainGlobalSingletonLifecycleState } from "../../shared/global-singleton.js";
import { detectWorktreeFilesystemBackend } from "./filesystem-backend.js";
import { nativeWorktreeFilesystem } from "./filesystem-native.js";
describe.skipIf(process.platform !== "darwin")("isolated native worktree operations", () => {
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
const options = { commitGuard: () => {} };
beforeEach(() => {
for (const name of [
"FS_SAFE_NATIVE_MODE",
"OPENCLAW_FS_SAFE_NATIVE_MODE",
"FS_SAFE_PYTHON_MODE",
"OPENCLAW_FS_SAFE_PYTHON_MODE",
]) {
vi.stubEnv(name, undefined);
}
configureFsSafeNative({ mode: "off" });
});
afterEach(async () => {
await drainGlobalSingletonLifecycleState();
vi.restoreAllMocks();
vi.unstubAllEnvs();
});
it("clones and reads provenance with the host's native helper still off", async () => {
const root = tempDirs.make("openclaw-native-policy-");
const backend = await detectWorktreeFilesystemBackend(root, options);
assert(backend);
expect(backend?.id).toBe("apfs");
const source = path.join(root, "source");
const destination = path.join(root, "destination");
await backend.createTemplate(source, options);
await fs.writeFile(path.join(source, "payload"), Buffer.alloc(1024 * 1024, 0x5a));
await backend.cloneTemplate(source, destination, options);
const [original, cloned] = await nativeWorktreeFilesystem.readMetadata(
[path.join(source, "payload"), path.join(destination, "payload")],
options,
);
expect(original?.cloneId).toBe(getApfsCloneId(path.join(source, "payload")));
expect(cloned?.cloneId).toBe(original?.cloneId);
expect(cloned?.ino).not.toBe(original?.ino);
expect(await fs.readFile(path.join(destination, "payload"))).toEqual(
await fs.readFile(path.join(source, "payload")),
);
expect(getFsSafeNativeConfig().mode).toBe("off");
});
it.each(["FS_SAFE_NATIVE_MODE", "OPENCLAW_FS_SAFE_NATIVE_MODE"])(
"honors explicit %s=off in read and write children",
async (name) => {
vi.stubEnv(name, "off");
const root = tempDirs.make("openclaw-native-disabled-");
const source = path.join(root, "source");
const destination = path.join(root, "destination");
await fs.mkdir(source);
await fs.writeFile(path.join(source, "payload"), "source");
expect(await detectWorktreeFilesystemBackend(root, options)).toBeNull();
await expect(
nativeWorktreeFilesystem.copy(source, destination, options),
).rejects.toMatchObject({
code: "helper-unavailable",
});
await expect(fs.access(destination)).rejects.toMatchObject({ code: "ENOENT" });
expect(getFsSafeNativeConfig().mode).toBe("off");
},
);
it.each(["copy", "createSource"])(
"revalidates allocation authority before child %s input",
async (operation) => {
const root = tempDirs.make("openclaw-native-admission-");
const source = path.join(root, "source");
const destination = path.join(root, "destination");
await fs.mkdir(source);
await fs.writeFile(path.join(source, "payload"), "source");
const run = runner.runCommandBuffersWithTimeout;
let authorized = true;
let childPid: number | undefined;
vi.spyOn(runner, "runCommandBuffersWithTimeout").mockImplementation(
(argv, commandOptions) => {
if (typeof commandOptions === "number") {
throw new Error("Native writes need guarded input");
}
return run(argv, {
...commandOptions,
beforeInput(pid) {
childPid = pid;
authorized = false;
commandOptions.beforeInput?.(pid);
},
});
},
);
const writeOptions = {
commitGuard() {
if (!authorized) {
throw new Error("allocation lease lost");
}
},
};
await expect(
operation === "copy"
? nativeWorktreeFilesystem.copy(source, destination, writeOptions)
: nativeWorktreeFilesystem.createSource(destination, writeOptions),
).rejects.toThrow("allocation lease lost");
expect(childPid).toBeGreaterThan(0);
await expect(fs.access(destination)).rejects.toMatchObject({ code: "ENOENT" });
},
);
it("joins an admitted write child after cancellation before returning to cleanup", async () => {
const root = tempDirs.make("openclaw-native-settlement-");
const ready = path.join(root, "ready");
const canceled = path.join(root, "canceled");
const release = path.join(root, "release");
const finished = path.join(root, "finished");
const run = runner.runCommandBuffersWithTimeout;
// A controlled write child makes settlement observable independently of disk speed.
const script = `
import fs from 'node:fs';
import path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import { serialize } from 'node:v8';
const root = process.argv[1];
process.on('SIGTERM', () => fs.writeFileSync(path.join(root, 'canceled'), 'yes'));
JSON.parse(fs.readFileSync(0, 'utf8'));
fs.writeFileSync(path.join(root, 'ready'), 'yes');
while (!fs.existsSync(path.join(root, 'release'))) await setTimeout(10);
fs.writeFileSync(path.join(root, 'finished'), 'yes');
process.stdout.write(serialize({ type: 'written' }));
`;
vi.spyOn(runner, "runCommandBuffersWithTimeout").mockImplementation((_argv, commandOptions) =>
run([process.execPath, "--input-type=module", "--eval", script, root], commandOptions),
);
const controller = new AbortController();
let settled = false;
const pending = nativeWorktreeFilesystem
.copy(path.join(root, "source"), path.join(root, "destination"), {
...options,
signal: controller.signal,
})
.then(
() => undefined,
(error: unknown) => error,
)
.finally(() => {
settled = true;
});
try {
await expect.poll(() => fs.readFile(ready, "utf8").catch(() => "")).toBe("yes");
controller.abort(new Error("allocation canceled"));
await expect.poll(() => fs.readFile(canceled, "utf8").catch(() => "")).toBe("yes");
expect(settled).toBe(false);
await expect(fs.access(finished)).rejects.toMatchObject({ code: "ENOENT" });
} finally {
await fs.writeFile(release, "yes");
await pending;
}
expect(await pending).toMatchObject({ message: "allocation canceled" });
expect(await fs.readFile(finished, "utf8")).toBe("yes");
});
});
|