Files
openclaw/src/agents/sandbox/fs-paths.test.ts
2026-02-17 13:36:48 +09:00

106 lines
3.6 KiB
TypeScript

import path from "node:path";
import { describe, expect, it } from "vitest";
import {
buildSandboxFsMounts,
parseSandboxBindMount,
resolveSandboxFsPathWithMounts,
} from "./fs-paths.js";
import { createSandboxTestContext } from "./test-fixtures.js";
import type { SandboxContext } from "./types.js";
function createSandbox(overrides?: Partial<SandboxContext>): SandboxContext {
return createSandboxTestContext({ overrides });
}
describe("parseSandboxBindMount", () => {
it("parses bind mode and writeability", () => {
expect(parseSandboxBindMount("/tmp/a:/workspace-a:ro")).toEqual({
hostRoot: path.resolve("/tmp/a"),
containerRoot: "/workspace-a",
writable: false,
});
expect(parseSandboxBindMount("/tmp/b:/workspace-b:rw")).toEqual({
hostRoot: path.resolve("/tmp/b"),
containerRoot: "/workspace-b",
writable: true,
});
});
it("parses Windows drive-letter host paths", () => {
expect(parseSandboxBindMount("C:\\Users\\kai\\workspace:/workspace:ro")).toEqual({
hostRoot: path.resolve("C:\\Users\\kai\\workspace"),
containerRoot: "/workspace",
writable: false,
});
expect(parseSandboxBindMount("D:/data:/workspace-data:rw")).toEqual({
hostRoot: path.resolve("D:/data"),
containerRoot: "/workspace-data",
writable: true,
});
});
it("parses UNC-style host paths", () => {
expect(parseSandboxBindMount("//server/share:/workspace:ro")).toEqual({
hostRoot: path.resolve("//server/share"),
containerRoot: "/workspace",
writable: false,
});
});
});
describe("resolveSandboxFsPathWithMounts", () => {
it("maps mounted container absolute paths to host paths", () => {
const sandbox = createSandbox({
docker: {
...createSandbox().docker,
binds: ["/tmp/workspace-two:/workspace-two:ro"],
},
});
const mounts = buildSandboxFsMounts(sandbox);
const resolved = resolveSandboxFsPathWithMounts({
filePath: "/workspace-two/docs/AGENTS.md",
cwd: sandbox.workspaceDir,
defaultWorkspaceRoot: sandbox.workspaceDir,
defaultContainerRoot: sandbox.containerWorkdir,
mounts,
});
expect(resolved.hostPath).toBe(
path.join(path.resolve("/tmp/workspace-two"), "docs", "AGENTS.md"),
);
expect(resolved.containerPath).toBe("/workspace-two/docs/AGENTS.md");
expect(resolved.relativePath).toBe("/workspace-two/docs/AGENTS.md");
expect(resolved.writable).toBe(false);
});
it("keeps workspace-relative display paths for default workspace files", () => {
const sandbox = createSandbox();
const mounts = buildSandboxFsMounts(sandbox);
const resolved = resolveSandboxFsPathWithMounts({
filePath: "src/index.ts",
cwd: sandbox.workspaceDir,
defaultWorkspaceRoot: sandbox.workspaceDir,
defaultContainerRoot: sandbox.containerWorkdir,
mounts,
});
expect(resolved.hostPath).toBe(path.join(path.resolve("/tmp/workspace"), "src", "index.ts"));
expect(resolved.containerPath).toBe("/workspace/src/index.ts");
expect(resolved.relativePath).toBe("src/index.ts");
expect(resolved.writable).toBe(true);
});
it("preserves legacy sandbox-root error for outside paths", () => {
const sandbox = createSandbox();
const mounts = buildSandboxFsMounts(sandbox);
expect(() =>
resolveSandboxFsPathWithMounts({
filePath: "/etc/passwd",
cwd: sandbox.workspaceDir,
defaultWorkspaceRoot: sandbox.workspaceDir,
defaultContainerRoot: sandbox.containerWorkdir,
mounts,
}),
).toThrow(/Path escapes sandbox root/);
});
});