Compare commits

...

1 Commits

Author SHA1 Message Date
Sarah Fortune
fa32353c7e Skip tests for operations that arent supported on windows 2026-05-06 13:10:33 -07:00
7 changed files with 75 additions and 13 deletions

View File

@ -788,7 +788,7 @@ describe("temporary workspace and symlink parent helpers", () => {
});
describe("file stores and private stores", () => {
it("writes, streams, copies, reads, removes, and prunes file-store entries", async () => {
it.skipIf(process.platform === "win32")("writes, streams, copies, reads, removes, and prunes file-store entries", async () => {
const root = await tempRoot("fs-safe-store-");
const sourceRoot = await tempRoot("fs-safe-store-source-");
const source = path.join(sourceRoot, "source.txt");
@ -828,7 +828,7 @@ describe("file stores and private stores", () => {
await expect(fs.stat(old)).rejects.toMatchObject({ code: "ENOENT" });
});
it("covers private file store mode", async () => {
it.skipIf(process.platform === "win32")("covers private file store mode", async () => {
const root = await tempRoot("fs-safe-private-store-");
const store = fileStore({ rootDir: root, private: true });

View File

@ -6,6 +6,9 @@ import { afterEach, describe, expect, it } from "vitest";
import { configureFsSafePython, FsSafeError, root as openRoot } from "../src/index.js";
import { openLocalFileSafely, readLocalFileSafely } from "../src/root.js";
import { __setFsSafeTestHooksForTest } from "../src/test-hooks.js";
import { expectedFsSafeCode } from "./helpers/security.js";
const skipOnWindows = process.platform === "win32";
const tempDirs: string[] = [];
@ -23,7 +26,7 @@ afterEach(async () => {
});
describe("@openclaw/fs-safe", () => {
it("reuses a root capability across filesystem operations", async () => {
it.skipIf(skipOnWindows)("reuses a root capability across filesystem operations", async () => {
const rootPath = await tempRoot("fs-root-object-");
const root = await openRoot(rootPath);
@ -62,7 +65,7 @@ describe("@openclaw/fs-safe", () => {
});
});
it("can disable the Python helper and keep root operations available", async () => {
it.skipIf(skipOnWindows)("can disable the Python helper and keep root operations available", async () => {
configureFsSafePython({ mode: "off" });
const rootPath = await tempRoot("fs-safe-python-off-");
const sourceRoot = await tempRoot("fs-safe-python-off-source-");
@ -125,7 +128,7 @@ describe("@openclaw/fs-safe", () => {
);
});
it("writes, reads, stats, and lists files within a root", async () => {
it.skipIf(skipOnWindows)("writes, reads, stats, and lists files within a root", async () => {
const root = await openRoot(await tempRoot("fs-safe-basic-"));
await root.mkdir("nested");
@ -234,7 +237,7 @@ describe("@openclaw/fs-safe", () => {
await expect(root.read("link/secret.txt")).rejects.toMatchObject({
code: "outside-workspace",
});
await expect(root.list("link")).rejects.toMatchObject({ code: "path-alias" });
await expect(root.list("link")).rejects.toMatchObject({ code: expectedFsSafeCode("path-alias") });
});
it("rejects symlink leaves for stat and read", async () => {
@ -244,11 +247,11 @@ describe("@openclaw/fs-safe", () => {
await writeFile(path.join(outside, "secret.txt"), "secret");
await symlink(path.join(outside, "secret.txt"), path.join(rootPath, "secret-link"), "file");
await expect(root.stat("secret-link")).rejects.toMatchObject({ code: "path-alias" });
await expect(root.stat("secret-link")).rejects.toMatchObject({ code: expectedFsSafeCode("path-alias") });
await expect(root.read("secret-link")).rejects.toMatchObject({ code: "symlink" });
});
it("renames paths within the same root and rejects symlink sources", async () => {
it.skipIf(skipOnWindows)("renames paths within the same root and rejects symlink sources", async () => {
const rootPath = await tempRoot("fs-safe-rename-");
const root = await openRoot(rootPath);
const outside = await tempRoot("fs-safe-outside-");
@ -264,7 +267,7 @@ describe("@openclaw/fs-safe", () => {
});
});
it("requires explicit overwrite for moves that replace a target", async () => {
it.skipIf(skipOnWindows)("requires explicit overwrite for moves that replace a target", async () => {
const rootPath = await tempRoot("fs-safe-rename-overwrite-");
const root = await openRoot(rootPath);
await root.write("from.txt", "source");
@ -279,7 +282,7 @@ describe("@openclaw/fs-safe", () => {
await expect(readFile(path.join(rootPath, "to.txt"), "utf8")).resolves.toBe("source");
});
it("enforces copyIn maxBytes while streaming", async () => {
it.skipIf(skipOnWindows)("enforces copyIn maxBytes while streaming", async () => {
const rootPath = await tempRoot("fs-safe-copy-limit-");
const sourceRoot = await tempRoot("fs-safe-copy-source-");
const sourcePath = path.join(sourceRoot, "source.txt");
@ -354,7 +357,7 @@ describe("@openclaw/fs-safe", () => {
await expect(readFile(outsideFile, "utf8")).resolves.toBe("kept");
await expect(root.stat("link")).rejects.toMatchObject({
code: "not-found",
code: expectedFsSafeCode("not-found"),
});
});

View File

@ -100,7 +100,13 @@ export async function makeTempLayout(
export function expectFsSafeCode(error: unknown, codes: readonly string[]): void {
expect(error).toBeInstanceOf(FsSafeError);
expect(codes).toContain((error as FsSafeError).code);
const accepted =
process.platform === "win32" ? [...codes, "unsupported-platform"] : codes;
expect(accepted).toContain((error as FsSafeError).code);
}
export function expectedFsSafeCode(code: string): string {
return process.platform === "win32" ? "unsupported-platform" : code;
}
export async function expectNoOutsideWrite(

View File

@ -125,7 +125,7 @@ describe("private temp workspaces", () => {
});
describe("file store", () => {
it("writes, reads, copies, and prunes files under the store root", async () => {
it.skipIf(process.platform === "win32")("writes, reads, copies, and prunes files under the store root", async () => {
const store = fileStore({ rootDir: root, maxBytes: 1024 });
await store.write("media/a.txt", "hello");
await expect(store.readBytes("media/a.txt")).resolves.toEqual(Buffer.from("hello"));

View File

@ -180,6 +180,18 @@ describe("Python helper configuration", () => {
describe("persistent Python helper worker", () => {
it("reuses one worker and unreferences it while idle", async () => {
if (process.platform === "win32") {
configureFsSafePython({ mode: "auto", pythonPath: "/tmp/fake-python" });
await expect(
runPinnedPythonOperation<{ ok: boolean }>({
operation: "stat",
rootPath: "/tmp/root",
payload: { relativePath: "a.txt" },
}),
).rejects.toMatchObject({ code: "unsupported-platform" });
return;
}
const child = makeRespondingChild();
spawnMock.mockReturnValue(child);
configureFsSafePython({ mode: "auto", pythonPath: "/tmp/fake-python" });
@ -209,6 +221,21 @@ describe("persistent Python helper worker", () => {
const rootDir = await tempRoot("fs-safe-python-policy-");
await fs.writeFile(path.join(rootDir, "file.txt"), "ok");
if (process.platform === "win32") {
spawnMock.mockImplementation(makeFailingChild);
configureFsSafePython({ mode: "auto", pythonPath: "/tmp/missing-python" });
const autoRoot = await root(rootDir);
await expect(autoRoot.stat("file.txt")).rejects.toMatchObject({
code: "unsupported-platform",
});
configureFsSafePython({ mode: "require", pythonPath: "/tmp/missing-python" });
await expect((await root(rootDir)).stat("file.txt")).rejects.toMatchObject({
code: "unsupported-platform",
});
return;
}
spawnMock.mockImplementation(makeFailingChild);
configureFsSafePython({ mode: "auto", pythonPath: "/tmp/missing-python" });
const autoRoot = await root(rootDir);

View File

@ -42,6 +42,21 @@ describe("pinned write fallback coverage", () => {
const { runPinnedWriteHelper } = await import("../src/pinned-write.js");
const root = await tempRoot("fs-safe-pinned-write-fallback-");
if (process.platform === "win32") {
await expect(
runPinnedWriteHelper({
rootPath: root,
relativeParentPath: "nested",
basename: "created.txt",
mkdir: true,
mode: 0o600,
overwrite: false,
input: { kind: "buffer", data: "created", encoding: "utf8" },
}),
).rejects.toMatchObject({ code: "unsupported-platform" });
return;
}
const created = await runPinnedWriteHelper({
rootPath: root,
relativeParentPath: "nested",

View File

@ -168,6 +168,17 @@ describe("write, move, and delete boundary bypass attempts", () => {
await fsp.symlink(layout.outsideFile, path.join(layout.root, "dest-link.txt"), "file");
const safeRoot = await openRoot(layout.root);
if (process.platform === "win32") {
await expect(safeRoot.move("source-link.txt", "moved.txt")).rejects.toMatchObject({
code: "unsupported-platform",
});
await expect(safeRoot.move("from.txt", "dest-link.txt", { overwrite: true })).rejects.toMatchObject({
code: "unsupported-platform",
});
await expectNoOutsideWrite(layout);
return;
}
await expect(safeRoot.move("source-link.txt", "moved.txt")).rejects.toBeTruthy();
await safeRoot.move("from.txt", "dest-link.txt", { overwrite: true });
await expectNoOutsideWrite(layout);