fix: fix misc windows issues (#323)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { edit } from "../../tools/impl/Edit";
|
||||
import { TestDirectory } from "../helpers/testFs";
|
||||
|
||||
@@ -111,4 +111,43 @@ describe("Edit tool", () => {
|
||||
} as unknown as Parameters<typeof edit>[0]),
|
||||
).rejects.toThrow(/missing required parameter.*new_string/);
|
||||
});
|
||||
|
||||
test("handles CRLF line endings (Windows compatibility)", async () => {
|
||||
testDir = new TestDirectory();
|
||||
// Create file with CRLF line endings (Windows style)
|
||||
const file = testDir.createFile("crlf.txt", "");
|
||||
writeFileSync(file, "line1\r\nline2\r\nline3\r\n", "utf-8");
|
||||
|
||||
// Edit with LF line endings (what the model typically sends)
|
||||
const result = await edit({
|
||||
file_path: file,
|
||||
old_string: "line1\nline2",
|
||||
new_string: "changed1\nchanged2",
|
||||
});
|
||||
|
||||
// Should successfully find and replace despite CRLF vs LF mismatch
|
||||
expect(result.replacements).toBe(1);
|
||||
const content = readFileSync(file, "utf-8");
|
||||
expect(content).toContain("changed1");
|
||||
expect(content).toContain("changed2");
|
||||
});
|
||||
|
||||
test("handles mixed line endings", async () => {
|
||||
testDir = new TestDirectory();
|
||||
const file = testDir.createFile("mixed.txt", "");
|
||||
// File with CRLF
|
||||
writeFileSync(file, "function foo() {\r\n return 1;\r\n}\r\n", "utf-8");
|
||||
|
||||
// Model sends LF
|
||||
const result = await edit({
|
||||
file_path: file,
|
||||
old_string: "function foo() {\n return 1;\n}",
|
||||
new_string: "function bar() {\n return 2;\n}",
|
||||
});
|
||||
|
||||
expect(result.replacements).toBe(1);
|
||||
const content = readFileSync(file, "utf-8");
|
||||
expect(content).toContain("function bar()");
|
||||
expect(content).toContain("return 2");
|
||||
});
|
||||
});
|
||||
|
||||
81
src/tests/tools/shell-launchers.test.ts
Normal file
81
src/tests/tools/shell-launchers.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { buildShellLaunchers } from "../../tools/impl/shellLaunchers";
|
||||
|
||||
describe("Shell Launchers", () => {
|
||||
test("builds launchers for a command", () => {
|
||||
const launchers = buildShellLaunchers("echo hello");
|
||||
expect(launchers.length).toBeGreaterThan(0);
|
||||
expect(launchers[0]).toBeDefined();
|
||||
});
|
||||
|
||||
test("returns empty array for empty command", () => {
|
||||
const launchers = buildShellLaunchers("");
|
||||
expect(launchers).toEqual([]);
|
||||
});
|
||||
|
||||
test("returns empty array for whitespace-only command", () => {
|
||||
const launchers = buildShellLaunchers(" ");
|
||||
expect(launchers).toEqual([]);
|
||||
});
|
||||
|
||||
if (process.platform === "win32") {
|
||||
describe("Windows-specific", () => {
|
||||
test("PowerShell is tried before cmd.exe", () => {
|
||||
const launchers = buildShellLaunchers("echo test");
|
||||
|
||||
// Find indices of PowerShell and cmd.exe
|
||||
const powershellIndex = launchers.findIndex(
|
||||
(l) =>
|
||||
l[0]?.toLowerCase().includes("powershell") ||
|
||||
l[0]?.toLowerCase() === "pwsh",
|
||||
);
|
||||
const cmdIndex = launchers.findIndex(
|
||||
(l) =>
|
||||
l[0]?.toLowerCase().includes("cmd") ||
|
||||
l[0]?.toLowerCase() === process.env.ComSpec?.toLowerCase(),
|
||||
);
|
||||
|
||||
expect(powershellIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(cmdIndex).toBeGreaterThanOrEqual(0);
|
||||
// PowerShell should come before cmd.exe
|
||||
expect(powershellIndex).toBeLessThan(cmdIndex);
|
||||
});
|
||||
|
||||
test("includes PowerShell with -NoProfile flag", () => {
|
||||
const launchers = buildShellLaunchers("echo test");
|
||||
const powershellLauncher = launchers.find((l) =>
|
||||
l[0]?.toLowerCase().includes("powershell"),
|
||||
);
|
||||
|
||||
expect(powershellLauncher).toBeDefined();
|
||||
expect(powershellLauncher).toContain("-NoProfile");
|
||||
expect(powershellLauncher).toContain("-Command");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
describe("Unix-specific", () => {
|
||||
test("includes bash with -lc flag", () => {
|
||||
const launchers = buildShellLaunchers("echo test");
|
||||
const bashLauncher = launchers.find(
|
||||
(l) => l[0]?.includes("bash") && l[1] === "-lc",
|
||||
);
|
||||
|
||||
expect(bashLauncher).toBeDefined();
|
||||
});
|
||||
|
||||
test("prefers user SHELL environment", () => {
|
||||
const originalShell = process.env.SHELL;
|
||||
process.env.SHELL = "/bin/zsh";
|
||||
|
||||
try {
|
||||
const launchers = buildShellLaunchers("echo test");
|
||||
// User's shell should be first
|
||||
expect(launchers[0]?.[0]).toBe("/bin/zsh");
|
||||
} finally {
|
||||
if (originalShell === undefined) delete process.env.SHELL;
|
||||
else process.env.SHELL = originalShell;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user