test: dedupe workspace path-resolution scenarios

This commit is contained in:
Peter Steinberger
2026-02-22 12:25:57 +00:00
parent c61c9e121a
commit 60a0291bf8
2 changed files with 38 additions and 114 deletions

View File

@@ -7,80 +7,40 @@ import { createOpenClawCodingTools } from "./pi-tools.js";
import { expectReadWriteEditTools } from "./test-helpers/pi-tools-fs-helpers.js";
describe("createOpenClawCodingTools", () => {
it("uses workspaceDir for Read tool path resolution", async () => {
it("uses workspaceDir for read/write/edit path resolution", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-ws-"));
try {
// Create a test file in the "workspace"
const testFile = "test-workspace-file.txt";
const testContent = "workspace path resolution test";
await fs.writeFile(path.join(tmpDir, testFile), testContent, "utf8");
// Create tools with explicit workspaceDir
const tools = createOpenClawCodingTools({ workspaceDir: tmpDir });
const readTool = tools.find((tool) => tool.name === "read");
expect(readTool).toBeDefined();
const { readTool, writeTool, editTool } = expectReadWriteEditTools(tools);
// Read using relative path - should resolve against workspaceDir
const result = await readTool?.execute("tool-ws-1", {
path: testFile,
const readPath = "test-workspace-file.txt";
const readContent = "workspace path resolution test";
await fs.writeFile(path.join(tmpDir, readPath), readContent, "utf8");
const readResult = await readTool?.execute("tool-ws-1", {
path: readPath,
});
const textBlocks = result?.content?.filter((block) => block.type === "text") as
const textBlocks = readResult?.content?.filter((block) => block.type === "text") as
| Array<{ text?: string }>
| undefined;
const combinedText = textBlocks?.map((block) => block.text ?? "").join("\n");
expect(combinedText).toContain(testContent);
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
});
it("uses workspaceDir for Write tool path resolution", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-ws-"));
try {
const testFile = "test-write-file.txt";
const testContent = "written via workspace path";
expect(combinedText).toContain(readContent);
// Create tools with explicit workspaceDir
const tools = createOpenClawCodingTools({ workspaceDir: tmpDir });
const writeTool = tools.find((tool) => tool.name === "write");
expect(writeTool).toBeDefined();
// Write using relative path - should resolve against workspaceDir
const writePath = "test-write-file.txt";
const writeContent = "written via workspace path";
await writeTool?.execute("tool-ws-2", {
path: testFile,
content: testContent,
path: writePath,
content: writeContent,
});
expect(await fs.readFile(path.join(tmpDir, writePath), "utf8")).toBe(writeContent);
// Verify file was written to workspaceDir
const written = await fs.readFile(path.join(tmpDir, testFile), "utf8");
expect(written).toBe(testContent);
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
});
it("uses workspaceDir for Edit tool path resolution", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-ws-"));
try {
const testFile = "test-edit-file.txt";
const originalContent = "hello world";
const expectedContent = "hello universe";
await fs.writeFile(path.join(tmpDir, testFile), originalContent, "utf8");
// Create tools with explicit workspaceDir
const tools = createOpenClawCodingTools({ workspaceDir: tmpDir });
const editTool = tools.find((tool) => tool.name === "edit");
expect(editTool).toBeDefined();
// Edit using relative path - should resolve against workspaceDir
const editPath = "test-edit-file.txt";
await fs.writeFile(path.join(tmpDir, editPath), "hello world", "utf8");
await editTool?.execute("tool-ws-3", {
path: testFile,
path: editPath,
oldText: "world",
newText: "universe",
});
// Verify file was edited in workspaceDir
const edited = await fs.readFile(path.join(tmpDir, testFile), "utf8");
expect(edited).toBe(expectedContent);
expect(await fs.readFile(path.join(tmpDir, editPath), "utf8")).toBe("hello universe");
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}

View File

@@ -21,74 +21,38 @@ async function withTempDir<T>(prefix: string, fn: (dir: string) => Promise<T>) {
}
describe("workspace path resolution", () => {
it("reads relative paths against workspaceDir even after cwd changes", async () => {
it("resolves relative read/write/edit paths against workspaceDir even after cwd changes", async () => {
await withTempDir("openclaw-ws-", async (workspaceDir) => {
await withTempDir("openclaw-cwd-", async (otherDir) => {
const testFile = "read.txt";
const contents = "workspace read ok";
await fs.writeFile(path.join(workspaceDir, testFile), contents, "utf8");
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(otherDir);
try {
const tools = createOpenClawCodingTools({ workspaceDir });
const readTool = tools.find((tool) => tool.name === "read");
expect(readTool).toBeDefined();
const { readTool, writeTool, editTool } = expectReadWriteEditTools(tools);
const result = await readTool?.execute("ws-read", { path: testFile });
expect(getTextContent(result)).toContain(contents);
} finally {
cwdSpy.mockRestore();
}
});
});
});
const readFile = "read.txt";
await fs.writeFile(path.join(workspaceDir, readFile), "workspace read ok", "utf8");
const readResult = await readTool.execute("ws-read", { path: readFile });
expect(getTextContent(readResult)).toContain("workspace read ok");
it("writes relative paths against workspaceDir even after cwd changes", async () => {
await withTempDir("openclaw-ws-", async (workspaceDir) => {
await withTempDir("openclaw-cwd-", async (otherDir) => {
const testFile = "write.txt";
const contents = "workspace write ok";
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(otherDir);
try {
const tools = createOpenClawCodingTools({ workspaceDir });
const writeTool = tools.find((tool) => tool.name === "write");
expect(writeTool).toBeDefined();
await writeTool?.execute("ws-write", {
path: testFile,
content: contents,
const writeFile = "write.txt";
await writeTool.execute("ws-write", {
path: writeFile,
content: "workspace write ok",
});
expect(await fs.readFile(path.join(workspaceDir, writeFile), "utf8")).toBe(
"workspace write ok",
);
const written = await fs.readFile(path.join(workspaceDir, testFile), "utf8");
expect(written).toBe(contents);
} finally {
cwdSpy.mockRestore();
}
});
});
});
it("edits relative paths against workspaceDir even after cwd changes", async () => {
await withTempDir("openclaw-ws-", async (workspaceDir) => {
await withTempDir("openclaw-cwd-", async (otherDir) => {
const testFile = "edit.txt";
await fs.writeFile(path.join(workspaceDir, testFile), "hello world", "utf8");
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(otherDir);
try {
const tools = createOpenClawCodingTools({ workspaceDir });
const editTool = tools.find((tool) => tool.name === "edit");
expect(editTool).toBeDefined();
await editTool?.execute("ws-edit", {
path: testFile,
const editFile = "edit.txt";
await fs.writeFile(path.join(workspaceDir, editFile), "hello world", "utf8");
await editTool.execute("ws-edit", {
path: editFile,
oldText: "world",
newText: "openclaw",
});
const updated = await fs.readFile(path.join(workspaceDir, testFile), "utf8");
expect(updated).toBe("hello openclaw");
expect(await fs.readFile(path.join(workspaceDir, editFile), "utf8")).toBe(
"hello openclaw",
);
} finally {
cwdSpy.mockRestore();
}