Skip to content

Commit 30cea5a

Browse files
test: make fs-safe hardlink tests compatible with Windows
1 parent c4a3efc commit 30cea5a

1 file changed

Lines changed: 34 additions & 20 deletions

File tree

src/infra/fs-safe.test.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,8 @@
11
// Tests safe filesystem wrappers and protected file-handle behavior.
22
import type { FileHandle } from "node:fs/promises";
33
import fs from "node:fs/promises";
4-
import fsSync from "node:fs";
5-
import os from "node:os";
64
import path from "node:path";
75
import { __setFsSafeTestHooksForTest } from "@openclaw/fs-safe/test-hooks";
8-
9-
const canCreateHardlinks = (() => {
10-
const probeDir = fsSync.mkdtempSync(path.join(os.tmpdir(), "openclaw-fs-hardlink-probe-"));
11-
const targetFile = path.join(probeDir, "target.txt");
12-
const linkFile = path.join(probeDir, "link.txt");
13-
try {
14-
fsSync.writeFileSync(targetFile, "target", "utf8");
15-
fsSync.linkSync(targetFile, linkFile);
16-
return true;
17-
} catch {
18-
return false;
19-
} finally {
20-
fsSync.rmSync(probeDir, { recursive: true, force: true });
21-
}
22-
})();
236
import { afterEach, describe, expect, it, vi } from "vitest";
247
import { withEnv, withEnvAsync } from "../test-utils/env.js";
258
import {
@@ -36,13 +19,35 @@ import {
3619
} from "./fs-safe.js";
3720

3821
const tempDirs = createTrackedTempDirs();
22+
let hardlinkSupport: Promise<boolean> | undefined;
3923

4024
afterEach(async () => {
4125
__setFsSafeTestHooksForTest(undefined);
4226
vi.restoreAllMocks();
4327
await tempDirs.cleanup();
4428
});
4529

30+
async function canCreateHardlinks(): Promise<boolean> {
31+
hardlinkSupport ??= (async () => {
32+
let probeDir: string | undefined;
33+
try {
34+
probeDir = await tempDirs.make("openclaw-fs-hardlink-probe-");
35+
const targetFile = path.join(probeDir, "target.txt");
36+
const linkFile = path.join(probeDir, "link.txt");
37+
await fs.writeFile(targetFile, "target", "utf8");
38+
await fs.link(targetFile, linkFile);
39+
return true;
40+
} catch {
41+
return false;
42+
} finally {
43+
if (probeDir !== undefined) {
44+
await fs.rm(probeDir, { recursive: true, force: true });
45+
}
46+
}
47+
})();
48+
return await hardlinkSupport;
49+
}
50+
4651
async function expectRejectCode(promise: Promise<unknown>, expected: string | RegExp) {
4752
const err = await promise.catch((caught: unknown) => caught);
4853
if (err === undefined) {
@@ -365,7 +370,10 @@ describe("fs-safe", () => {
365370
});
366371
});
367372

368-
it.skipIf(!canCreateHardlinks)("blocks hardlink aliases under root", async () => {
373+
it("blocks hardlink aliases under root", async () => {
374+
if (!(await canCreateHardlinks())) {
375+
return;
376+
}
369377
const root = await tempDirs.make("openclaw-fs-safe-root-");
370378
const hardlinkPath = path.join(root, "link.txt");
371379
await withOutsideHardlinkAlias({
@@ -492,7 +500,10 @@ describe("fs-safe", () => {
492500
await expectRejectCode((await openRoot(root)).write("../escape.txt", "x"), "outside-workspace");
493501
});
494502

495-
it.skipIf(!canCreateHardlinks)("rejects writing through hardlink aliases", async () => {
503+
it("rejects writing through hardlink aliases", async () => {
504+
if (!(await canCreateHardlinks())) {
505+
return;
506+
}
496507
const root = await tempDirs.make("openclaw-fs-safe-root-");
497508
const hardlinkPath = path.join(root, "alias.txt");
498509
await withOutsideHardlinkAlias({
@@ -504,7 +515,10 @@ describe("fs-safe", () => {
504515
});
505516
});
506517

507-
it.skipIf(!canCreateHardlinks)("rejects appending through hardlink aliases", async () => {
518+
it("rejects appending through hardlink aliases", async () => {
519+
if (!(await canCreateHardlinks())) {
520+
return;
521+
}
508522
const root = await tempDirs.make("openclaw-fs-safe-root-");
509523
const hardlinkPath = path.join(root, "alias.txt");
510524
await withOutsideHardlinkAlias({

0 commit comments

Comments
 (0)