Files
openclaw/src/plugin-sdk/windows-spawn.test.ts
Peter Steinberger 7b366e16b0 fix: Windows CLI backends fail through npm shims (#101378)
* fix(process): resolve Windows npm CLI shims directly

Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>

* docs(changelog): note Windows CLI shim fix

* docs(process): explain Windows shim boundary

* chore: keep release changelog owner-only

* test(process): isolate Windows shim resolution

* fix(process): classify Windows forwarding shims safely

---------

Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>
2026-07-07 06:57:16 +01:00

133 lines
4.2 KiB
TypeScript

/**
* Tests Windows spawn compatibility helpers.
*/
import { writeFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { createPluginSdkTestHarness } from "./test-helpers.js";
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "./windows-spawn.js";
const { createTempDir } = createPluginSdkTestHarness({
cleanup: {
maxRetries: 8,
retryDelay: 8,
},
});
describe("resolveWindowsSpawnProgram", () => {
it("rejects node command strings that include inline entrypoint arguments on Windows", () => {
expect(() =>
resolveWindowsSpawnProgram({
command: "node C:\\Users\\me\\.openclaw\\npm\\node_modules\\@openai\\codex\\bin\\codex.js",
platform: "win32",
env: {},
execPath: "C:\\node\\node.exe",
}),
).toThrow("Windows spawn command must be an executable path only");
});
it("allows executable paths with spaces on Windows", () => {
const resolved = resolveWindowsSpawnProgram({
command: "C:\\Program Files\\OpenAI Codex\\codex.exe",
platform: "win32",
env: {},
execPath: "C:\\node\\node.exe",
});
expect(resolved).toEqual({
command: "C:\\Program Files\\OpenAI Codex\\codex.exe",
leadingArgv: [],
resolution: "direct",
windowsHide: undefined,
});
});
it("fails closed by default for unresolved windows wrappers", async () => {
const dir = await createTempDir("openclaw-windows-spawn-test-");
const shimPath = path.join(dir, "wrapper.cmd");
await writeFile(shimPath, "@ECHO off\r\necho wrapper\r\n", "utf8");
expect(() =>
resolveWindowsSpawnProgram({
command: shimPath,
platform: "win32",
env: { PATH: dir, PATHEXT: ".CMD;.EXE;.BAT" },
execPath: "C:\\node\\node.exe",
}),
).toThrow(/without shell execution/);
});
it("only returns shell fallback when explicitly opted in", async () => {
const dir = await createTempDir("openclaw-windows-spawn-test-");
const shimPath = path.join(dir, "wrapper.cmd");
await writeFile(shimPath, "@ECHO off\r\necho wrapper\r\n", "utf8");
const resolved = resolveWindowsSpawnProgram({
command: shimPath,
platform: "win32",
env: { PATH: dir, PATHEXT: ".CMD;.EXE;.BAT" },
execPath: "C:\\node\\node.exe",
allowShellFallback: true,
});
const invocation = materializeWindowsSpawnProgram(resolved, ["--cwd", "C:\\safe & calc.exe"]);
expect(invocation).toEqual({
command: shimPath,
argv: ["--cwd", "C:\\safe & calc.exe"],
resolution: "shell-fallback",
shell: true,
windowsHide: undefined,
});
});
it("preserves custom batch-wrapper behavior instead of bypassing its target", async () => {
const dir = await createTempDir("openclaw-windows-spawn-test-");
const targetPath = path.join(dir, "tool.exe");
const wrapperPath = path.join(dir, "wrapper.cmd");
await writeFile(targetPath, "", "utf8");
await writeFile(
wrapperPath,
'@ECHO off\r\nSET WRAPPER_FLAG=1\r\n"%~dp0\\tool.exe" %*\r\n',
"utf8",
);
const resolved = resolveWindowsSpawnProgram({
command: wrapperPath,
platform: "win32",
env: { PATH: dir, PATHEXT: ".CMD;.EXE;.BAT" },
execPath: "C:\\node\\node.exe",
allowShellFallback: true,
});
expect(resolved).toEqual({
command: wrapperPath,
leadingArgv: [],
resolution: "shell-fallback",
shell: true,
});
});
it("does not reinterpret a forwarded batch wrapper as a Node script", async () => {
const dir = await createTempDir("openclaw-windows-spawn-test-");
const targetPath = path.join(dir, "inner.cmd");
const wrapperPath = path.join(dir, "wrapper.cmd");
await writeFile(targetPath, "@ECHO off\r\necho inner\r\n", "utf8");
await writeFile(wrapperPath, '@ECHO off\r\n"%~dp0\\inner.cmd" %*\r\n', "utf8");
const resolved = resolveWindowsSpawnProgram({
command: wrapperPath,
platform: "win32",
env: { PATH: dir, PATHEXT: ".CMD;.EXE;.BAT" },
execPath: "C:\\node\\node.exe",
allowShellFallback: true,
});
expect(resolved).toEqual({
command: wrapperPath,
leadingArgv: [],
resolution: "shell-fallback",
shell: true,
});
});
});