mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.
One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.
Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.
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");
|
|
});
|