mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
* test(infra): retry recursive temp-dir removal on main (main twin of #11968)
`main` has been red since b342c1a361 on the vitest and integration gates:
✖ tests/unit/autoCombo/provider-family-combos.test.ts > auto/<family>
✖ chat pipeline applies Codex OAuth fingerprint and priority tier inside combos
Both call resetStorage() from beforeEach, which does an fs.rmSync(TEST_DATA_DIR,
{recursive: true, force: true}) with no retry, and intermittently loses the race
with a not-yet-released SQLite handle (ENOTEMPTY).
release/v3.8.51 fixed this in #11968 with a mechanical codemod adding
maxRetries/retryDelay to every recursive rm/rmSync/rmdirSync under tests/, but
that PR landed only on the release branch. Because main only receives work at
the release squash, it stayed broken for the whole cycle — and repo-wide gates
then turn every open PR into main red on checks unrelated to their diff.
This is the --base main twin: re-runs the same codemod that already shipped on
the release branch (scripts/ad-hoc/codemod-rm-maxretries.mjs), so the two
branches converge on identical test-teardown semantics. Test-only; no product
logic is touched.
The remaining three failures reported on #12133 (unit full suite exceeding its
4800s ceiling, package-artifact exceeding 1200s, and the boot-smoke that is
skipped as a consequence) are runner-contention timeouts, not code defects —
validate-release-green.mjs runs those heavy gates concurrently on one shared
hosted runner. There is no fix to port for those.
* chore(scripts): carry the rm-maxretries codemod onto main alongside its output
The codemod that generated the previous commit lives in the repo on
release/v3.8.51 (added by #11968) but was never on main. Bringing it over keeps
the tool next to the change it produced, so the transformation stays
reproducible and auditable from either branch.
380 lines
14 KiB
TypeScript
380 lines
14 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { createRequire, syncBuiltinESMExports } from "node:module";
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const childProcess = require("node:child_process");
|
|
const modulePath = path.join(process.cwd(), "src/shared/services/cliRuntime.ts");
|
|
|
|
const originalSpawn = childProcess.spawn;
|
|
const originalExecFileSync = childProcess.execFileSync;
|
|
const originalEnv = { ...process.env };
|
|
|
|
const tempDirs = new Set();
|
|
|
|
async function importFresh(label) {
|
|
return import(`${pathToFileURL(modulePath).href}?case=${label}-${Date.now()}-${Math.random()}`);
|
|
}
|
|
|
|
function restoreEnv() {
|
|
for (const key of Object.keys(process.env)) {
|
|
if (!(key in originalEnv)) delete process.env[key];
|
|
}
|
|
Object.assign(process.env, originalEnv);
|
|
}
|
|
|
|
function createTempDir(prefix) {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
tempDirs.add(dir);
|
|
return dir;
|
|
}
|
|
|
|
function writeScript(dir, name, content, executable = true) {
|
|
const filePath = path.join(dir, name);
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
fs.writeFileSync(filePath, content);
|
|
if (process.platform !== "win32") {
|
|
fs.chmodSync(filePath, executable ? 0o755 : 0o644);
|
|
}
|
|
return filePath;
|
|
}
|
|
|
|
test.afterEach(() => {
|
|
childProcess.spawn = originalSpawn;
|
|
childProcess.execFileSync = originalExecFileSync;
|
|
syncBuiltinESMExports();
|
|
restoreEnv();
|
|
|
|
for (const dir of tempDirs) {
|
|
fs.rmSync(dir as any, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
}
|
|
tempDirs.clear();
|
|
});
|
|
|
|
test("CLI config helpers enforce safe config homes and expose per-tool config paths", async () => {
|
|
const cliRuntime = await importFresh("config-helpers");
|
|
const homeDir = os.homedir();
|
|
const safeOverride = path.join(homeDir, "tmp-cli-config-home");
|
|
|
|
process.env.CLI_ALLOW_CONFIG_WRITES = "off";
|
|
assert.equal(cliRuntime.isCliConfigWriteAllowed(), false);
|
|
assert.match(cliRuntime.ensureCliConfigWriteAllowed(), /CLI_ALLOW_CONFIG_WRITES=false/);
|
|
|
|
process.env.CLI_CONFIG_HOME = safeOverride;
|
|
assert.equal(cliRuntime.getCliConfigHome(), safeOverride);
|
|
|
|
process.env.CLI_CONFIG_HOME = "relative/path";
|
|
assert.equal(cliRuntime.getCliConfigHome(), homeDir);
|
|
|
|
process.env.CLI_CONFIG_HOME = "/tmp/outside-home";
|
|
assert.equal(cliRuntime.getCliConfigHome(), homeDir);
|
|
|
|
process.env.CLI_CONFIG_HOME = safeOverride;
|
|
assert.deepEqual(cliRuntime.getCliConfigPaths("codex"), {
|
|
config: path.join(safeOverride, ".codex", "config.toml"),
|
|
auth: path.join(safeOverride, ".codex", "auth.json"),
|
|
});
|
|
assert.equal(
|
|
cliRuntime.getCliPrimaryConfigPath("codex"),
|
|
path.join(safeOverride, ".codex", "config.toml")
|
|
);
|
|
assert.equal(cliRuntime.getCliConfigPaths("unknown"), null);
|
|
|
|
process.env.XDG_CONFIG_HOME = path.join(homeDir, ".config-test");
|
|
// #3330: OpenCode uses XDG (`~/.config` / $XDG_CONFIG_HOME) on every platform,
|
|
// including Windows — no %APPDATA% special-case.
|
|
const expectedOpencodeRoot = process.env.XDG_CONFIG_HOME;
|
|
assert.deepEqual(cliRuntime.getCliConfigPaths("opencode"), {
|
|
config: path.join(expectedOpencodeRoot, "opencode", "opencode.json"),
|
|
});
|
|
});
|
|
|
|
test("getCliRuntimeStatus rejects unsafe env overrides and reports validated runtime mode", async () => {
|
|
process.env.CLI_MODE = "container";
|
|
process.env.CLI_CLAUDE_BIN = "relative/claude";
|
|
|
|
const cliRuntime = await importFresh("unsafe-env-command");
|
|
const status = await cliRuntime.getCliRuntimeStatus("claude");
|
|
|
|
assert.equal(status.installed, false);
|
|
assert.equal(status.runnable, false);
|
|
assert.equal(status.reason, "unsafe_path");
|
|
assert.equal(status.runtimeMode, "container");
|
|
assert.equal(status.requiresBinary, true);
|
|
});
|
|
|
|
test("getCliRuntimeStatus reports not_executable for absolute env override files without execute permission", async () => {
|
|
const tempDir = createTempDir("omniroute-cli-notexec-");
|
|
const scriptName = process.platform === "win32" ? "codex.cmd" : "codex";
|
|
const scriptPath = writeScript(
|
|
tempDir,
|
|
scriptName,
|
|
process.platform === "win32"
|
|
? "@echo off\r\necho codex 1.0.0\r\nREM padding padding padding\r\n"
|
|
: "#!/bin/sh\necho codex 1.0.0\n# padding padding padding\n",
|
|
false
|
|
);
|
|
|
|
process.env.CLI_CODEX_BIN = scriptPath;
|
|
const cliRuntime = await importFresh("not-executable");
|
|
const status = await cliRuntime.getCliRuntimeStatus("codex");
|
|
|
|
assert.equal(status.installed, true);
|
|
if (process.platform === "win32") {
|
|
assert.equal(status.runnable, true);
|
|
assert.equal(status.reason, null);
|
|
} else {
|
|
assert.equal(status.runnable, false);
|
|
assert.equal(status.reason, "not_executable");
|
|
}
|
|
assert.equal(status.commandPath, scriptPath);
|
|
});
|
|
|
|
test("getCliRuntimeStatus reports healthcheck_failed when a binary exists but does not answer version probes", async () => {
|
|
const tempDir = createTempDir("omniroute-cli-healthcheck-");
|
|
const scriptName = process.platform === "win32" ? "qodercli.cmd" : "qodercli";
|
|
const scriptPath = writeScript(
|
|
tempDir,
|
|
scriptName,
|
|
process.platform === "win32"
|
|
? "@echo off\r\nexit /b 1\r\nREM padding padding padding\r\n"
|
|
: "#!/bin/sh\nexit 1\n# padding padding padding\n"
|
|
);
|
|
|
|
process.env.CLI_QODER_BIN = scriptPath;
|
|
process.env.CLI_MODE = "invalid-mode";
|
|
const cliRuntime = await importFresh("healthcheck-failed");
|
|
const status = await cliRuntime.getCliRuntimeStatus("qoder");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, false);
|
|
assert.equal(status.reason, "healthcheck_failed");
|
|
assert.equal(status.runtimeMode, "auto");
|
|
});
|
|
|
|
test("getCliRuntimeStatus healthchecks Windows .exe paths with spaces without shell", async () => {
|
|
if (process.platform !== "win32") return;
|
|
|
|
const tempDir = path.join(createTempDir("omniroute-cli-space-"), "dir with space");
|
|
const scriptPath = writeScript(
|
|
tempDir,
|
|
"claude.exe",
|
|
"fake executable content padding padding padding"
|
|
);
|
|
const spawnCalls = [];
|
|
|
|
process.env.CLI_CLAUDE_BIN = scriptPath;
|
|
childProcess.spawn = (command, args, options) => {
|
|
spawnCalls.push({ command, args, options });
|
|
const child = new (require("node:events").EventEmitter)();
|
|
child.stdout = new (require("node:events").EventEmitter)();
|
|
child.stderr = new (require("node:events").EventEmitter)();
|
|
child.kill = () => true;
|
|
setImmediate(() => {
|
|
child.stdout.emit("data", "2.1.157 (Claude Code)\n");
|
|
child.emit("close", 0);
|
|
});
|
|
return child;
|
|
};
|
|
syncBuiltinESMExports();
|
|
|
|
const cliRuntime = await importFresh("windows-exe-space-no-shell");
|
|
const status = await cliRuntime.getCliRuntimeStatus("claude");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, true);
|
|
assert.equal(status.reason, null);
|
|
assert.equal(status.commandPath, scriptPath);
|
|
assert.equal(spawnCalls[0].command, scriptPath);
|
|
assert.deepEqual(spawnCalls[0].args, ["--version"]);
|
|
assert.equal(spawnCalls[0].options.shell, undefined);
|
|
});
|
|
|
|
test("getCliRuntimeStatus still healthchecks Windows .cmd wrappers through shell", async () => {
|
|
if (process.platform !== "win32") return;
|
|
|
|
const tempDir = createTempDir("omniroute-cli-cmd-shell-");
|
|
const scriptPath = writeScript(
|
|
tempDir,
|
|
"codex.cmd",
|
|
"@echo off\r\necho codex 1.2.3\r\nREM padding padding padding\r\n"
|
|
);
|
|
const spawnCalls = [];
|
|
|
|
process.env.CLI_CODEX_BIN = scriptPath;
|
|
childProcess.spawn = (command, args, options) => {
|
|
spawnCalls.push({ command, args, options });
|
|
const child = new (require("node:events").EventEmitter)();
|
|
child.stdout = new (require("node:events").EventEmitter)();
|
|
child.stderr = new (require("node:events").EventEmitter)();
|
|
child.kill = () => true;
|
|
setImmediate(() => {
|
|
child.stdout.emit("data", "codex 1.2.3\n");
|
|
child.emit("close", 0);
|
|
});
|
|
return child;
|
|
};
|
|
syncBuiltinESMExports();
|
|
|
|
const cliRuntime = await importFresh("windows-cmd-shell");
|
|
const status = await cliRuntime.getCliRuntimeStatus("codex");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, true);
|
|
assert.equal(status.reason, null);
|
|
// The command is passed to spawn unquoted — Node quotes it for cmd.exe when
|
|
// shell:true. We must NOT manually interpolate quotes (hard rule #13).
|
|
assert.equal(spawnCalls[0].command, scriptPath);
|
|
assert.deepEqual(spawnCalls[0].args, ["--version"]);
|
|
assert.equal(spawnCalls[0].options.shell, true);
|
|
});
|
|
|
|
test("shouldUseShellForCommand never uses the shell on non-Windows platforms", async () => {
|
|
if (process.platform === "win32") return;
|
|
const cliRuntime = await importFresh("should-use-shell-posix");
|
|
for (const cmd of ["/usr/bin/claude", "/opt/My App/claude.exe", "tool.cmd", "x.bat"]) {
|
|
assert.equal(
|
|
cliRuntime.shouldUseShellForCommand(cmd),
|
|
false,
|
|
`expected no shell on POSIX for: ${cmd}`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("getCliRuntimeStatus discovers binaries from CLI_EXTRA_PATHS during PATH lookup", async () => {
|
|
const tempDir = createTempDir("omniroute-cli-extra-path-");
|
|
const scriptName = process.platform === "win32" ? "qodercli.cmd" : "qodercli";
|
|
writeScript(
|
|
tempDir,
|
|
scriptName,
|
|
process.platform === "win32"
|
|
? "@echo off\r\necho qodercli 1.2.3\r\nREM padding padding padding\r\n"
|
|
: "#!/bin/sh\necho qodercli 1.2.3\n# padding padding padding\n"
|
|
);
|
|
|
|
process.env.CLI_EXTRA_PATHS = tempDir;
|
|
process.env.PATH = process.platform === "win32" ? process.env.PATH || "" : "/bin:/usr/bin";
|
|
|
|
const cliRuntime = await importFresh("extra-paths");
|
|
const status = await cliRuntime.getCliRuntimeStatus("qoder");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, true);
|
|
assert.equal(status.reason, null);
|
|
assert.equal(
|
|
path.basename(String(status.commandPath)).toLowerCase(),
|
|
process.platform === "win32" ? "qodercli.cmd" : "qodercli"
|
|
);
|
|
});
|
|
|
|
test("getCliRuntimeStatus resolves known binaries from npm global prefix discovered via npm config", async () => {
|
|
const prefixDir = createTempDir("omniroute-cli-prefix-");
|
|
const scriptName = process.platform === "win32" ? "qodercli.cmd" : "qodercli";
|
|
const scriptPath = writeScript(
|
|
path.join(prefixDir, process.platform === "win32" ? "" : "bin"),
|
|
scriptName,
|
|
process.platform === "win32"
|
|
? "@echo off\r\necho qodercli 1.2.3\r\nREM padding padding padding\r\n"
|
|
: "#!/bin/sh\necho qodercli 1.2.3\n# padding padding padding\n"
|
|
);
|
|
|
|
delete process.env.npm_config_prefix;
|
|
process.env.PATH = process.platform === "win32" ? process.env.PATH || "" : "/bin:/usr/bin";
|
|
childProcess.execFileSync = (command, args) => {
|
|
assert.equal(command, "npm");
|
|
assert.deepEqual(args, ["config", "get", "prefix"]);
|
|
return `${prefixDir}\n`;
|
|
};
|
|
syncBuiltinESMExports();
|
|
|
|
const cliRuntime = await importFresh("npm-prefix-known-path");
|
|
const status = await cliRuntime.getCliRuntimeStatus("qoder");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, true);
|
|
assert.equal(status.reason, null);
|
|
assert.equal(status.commandPath, scriptPath);
|
|
});
|
|
|
|
test("getCliRuntimeStatus ignores suspicious known-path binaries and symlink escapes", async () => {
|
|
const prefixDir = createTempDir("omniroute-cli-suspicious-");
|
|
const binDir = path.join(prefixDir, process.platform === "win32" ? "" : "bin");
|
|
const scriptName = process.platform === "win32" ? "qodercli.exe" : "qodercli";
|
|
fs.mkdirSync(binDir, { recursive: true });
|
|
fs.writeFileSync(path.join(binDir, scriptName), "");
|
|
|
|
process.env.npm_config_prefix = prefixDir;
|
|
process.env.PATH = process.platform === "win32" ? process.env.PATH || "" : "/bin:/usr/bin";
|
|
|
|
const cliRuntime = await importFresh("suspicious-size");
|
|
const suspiciousStatus = await cliRuntime.getCliRuntimeStatus("qoder");
|
|
|
|
assert.equal(suspiciousStatus.installed, false);
|
|
assert.equal(suspiciousStatus.reason, "suspicious_size");
|
|
|
|
if (process.platform !== "win32") {
|
|
const escapePrefix = createTempDir("omniroute-cli-escape-");
|
|
const escapeBinDir = path.join(escapePrefix, "bin");
|
|
const outsideDir = createTempDir("omniroute-cli-outside-");
|
|
const outsideTarget = writeScript(
|
|
outsideDir,
|
|
"qodercli",
|
|
"#!/bin/sh\necho qodercli 9.9.9\n# padding padding padding\n"
|
|
);
|
|
|
|
fs.mkdirSync(escapeBinDir, { recursive: true });
|
|
fs.symlinkSync(outsideTarget, path.join(escapeBinDir, "qodercli"));
|
|
process.env.npm_config_prefix = escapePrefix;
|
|
|
|
// #7753: every candidate checkKnownPath() ever receives is constructed by
|
|
// getKnownToolPaths() from a trusted root (here: npm_config_prefix/bin) — so a
|
|
// symlink placed exactly at that generated candidate path (the version-manager
|
|
// shim pattern used by nvm-windows/nvm/asdf/pyenv: trusted shim location,
|
|
// private per-version store target) is legitimate and must now be ACCEPTED even
|
|
// though its resolved target lives outside every EXPECTED_PARENT_PATHS entry.
|
|
// A genuinely unsafe symlink (one whose ORIGINAL location is also untrusted, not
|
|
// reachable through this known-path candidate generator) is covered separately
|
|
// by tests/unit/cliRuntime-symlink-escape-7753.test.ts via the exported
|
|
// checkKnownPath().
|
|
const escapedRuntime = await importFresh("symlink-escape");
|
|
const escapedStatus = await escapedRuntime.getCliRuntimeStatus("qoder");
|
|
|
|
assert.equal(escapedStatus.installed, true);
|
|
assert.equal(escapedStatus.reason, null);
|
|
}
|
|
});
|
|
|
|
test("getCliRuntimeStatus tolerates spawn errors during healthcheck and marks the tool as not runnable", async () => {
|
|
const tempDir = createTempDir("omniroute-cli-spawn-error-");
|
|
const scriptName = process.platform === "win32" ? "cline.cmd" : "cline";
|
|
const scriptPath = writeScript(
|
|
tempDir,
|
|
scriptName,
|
|
process.platform === "win32"
|
|
? "@echo off\r\necho cline\r\nREM padding padding padding\r\n"
|
|
: "#!/bin/sh\necho cline\n# padding padding padding\n"
|
|
);
|
|
|
|
process.env.CLI_CLINE_BIN = scriptPath;
|
|
childProcess.spawn = () => {
|
|
const child = new (require("node:events").EventEmitter)();
|
|
child.stdout = new (require("node:events").EventEmitter)();
|
|
child.stderr = new (require("node:events").EventEmitter)();
|
|
child.kill = () => true;
|
|
setImmediate(() => child.emit("error", new Error("spawn blocked")));
|
|
return child;
|
|
};
|
|
syncBuiltinESMExports();
|
|
|
|
const cliRuntime = await importFresh("spawn-error");
|
|
const status = await cliRuntime.getCliRuntimeStatus("cline");
|
|
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.runnable, false);
|
|
assert.equal(status.reason, "healthcheck_failed");
|
|
});
|