fix: make test suite portable across platforms (#56)

* fix: make test suite portable across platforms

Replace hardcoded macOS path with fileURLToPath for cross-platform
compatibility. Add .cmd wrapper creation and platform-aware PATH
separator in test fixtures so the fake codex binary is discoverable
on Windows. Add shell and windowsHide options to the test helper
run() function to match production behavior.

Test results on Windows improve from 12/64 pass to 59/64 pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip shell for absolute-path commands to avoid Windows space-in-path breakage

When `process.execPath` resolves to a path with spaces (e.g.,
`C:\Program Files\nodejs\node.exe`), `shell: true` causes cmd.exe
to split the path at the space. Guard with `path.isAbsolute()` so
only bare command names (which need `.cmd` shim resolution) use the
shell.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Dominik Kundel <dkundel@openai.com>
This commit is contained in:
ZETA 2026-04-01 03:40:08 +08:00 committed by GitHub
parent 1a79ae57ec
commit a1266348d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 17 additions and 4 deletions

View File

@ -4,7 +4,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import { fileURLToPath } from "node:url";
const ROOT = path.resolve(fileURLToPath(new URL("..", import.meta.url)));
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const PLUGIN_ROOT = path.join(ROOT, "plugins", "codex");
function read(relativePath) {

View File

@ -1,4 +1,6 @@
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { writeExecutable } from "./helpers.mjs";
@ -507,11 +509,19 @@ rl.on("line", (line) => {
});
`;
writeExecutable(scriptPath, source);
// On Windows, npm global binaries are invoked via .cmd wrappers.
// Create a codex.cmd so the fake binary is discoverable by spawn with shell: true.
if (process.platform === "win32") {
const cmdWrapper = `@echo off\r\nnode "%~dp0codex" %*\r\n`;
fs.writeFileSync(path.join(binDir, "codex.cmd"), cmdWrapper, { encoding: "utf8" });
}
}
export function buildEnv(binDir) {
const sep = process.platform === "win32" ? ";" : ":";
return {
...process.env,
PATH: `${binDir}:${process.env.PATH}`
PATH: `${binDir}${sep}${process.env.PATH}`
};
}

View File

@ -1,6 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { spawnSync } from "node:child_process";
export function makeTempDir(prefix = "codex-plugin-test-") {
@ -16,7 +17,9 @@ export function run(command, args, options = {}) {
cwd: options.cwd,
env: options.env,
encoding: "utf8",
input: options.input
input: options.input,
shell: process.platform === "win32" && !path.isAbsolute(command),
windowsHide: true
});
}

View File

@ -10,7 +10,7 @@ import { initGitRepo, makeTempDir, run } from "./helpers.mjs";
import { loadBrokerSession } from "../plugins/codex/scripts/lib/broker-lifecycle.mjs";
import { resolveStateDir } from "../plugins/codex/scripts/lib/state.mjs";
const ROOT = path.resolve(fileURLToPath(new URL("..", import.meta.url)));
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const PLUGIN_ROOT = path.join(ROOT, "plugins", "codex");
const SCRIPT = path.join(PLUGIN_ROOT, "scripts", "codex-companion.mjs");
const STOP_HOOK = path.join(PLUGIN_ROOT, "scripts", "stop-review-gate-hook.mjs");