mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(qa-lab): bound Node binary lookup (#109428)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Qa Lab tests cover node exec plugin behavior.
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { runExecMock } = vi.hoisted(() => ({ runExecMock: vi.fn() }));
|
||||
|
||||
@@ -13,6 +13,10 @@ describe("resolveQaNodeExecPath", () => {
|
||||
runExecMock.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("reuses the current exec path when already running under Node", async () => {
|
||||
await expect(
|
||||
resolveQaNodeExecPath({
|
||||
@@ -66,6 +70,7 @@ describe("resolveQaNodeExecPath", () => {
|
||||
expect(runExecMock).toHaveBeenCalledWith("which", ["node"], {
|
||||
baseEnv: env,
|
||||
logOutput: false,
|
||||
timeoutMs: 5_000,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,6 +87,7 @@ describe("resolveQaNodeExecPath", () => {
|
||||
expect(options).toEqual({
|
||||
encoding: "utf8",
|
||||
env: { SystemRoot: String.raw`D:\Windows` },
|
||||
timeoutMs: 5_000,
|
||||
});
|
||||
return {
|
||||
stdout: String.raw`D:\nodejs\node.exe` + "\r\n",
|
||||
@@ -92,6 +98,33 @@ describe("resolveQaNodeExecPath", () => {
|
||||
).resolves.toBe(String.raw`D:\nodejs\node.exe`);
|
||||
});
|
||||
|
||||
it("fails after the lookup timeout when the PATH probe stalls", async () => {
|
||||
vi.useFakeTimers();
|
||||
runExecMock.mockImplementationOnce(
|
||||
(_file: string, _args: string[], options: { timeoutMs: number }): Promise<never> =>
|
||||
new Promise((_resolve, reject) => {
|
||||
setTimeout(() => reject(new Error("timed out")), options.timeoutMs);
|
||||
}),
|
||||
);
|
||||
|
||||
const lookup = resolveQaNodeExecPath({
|
||||
execPath: "/opt/homebrew/bin/bun",
|
||||
platform: "darwin",
|
||||
versions: { ...process.versions, bun: "1.2.3" },
|
||||
});
|
||||
const rejection = lookup.catch((error: unknown) => error);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(5_000);
|
||||
await expect(rejection).resolves.toEqual(
|
||||
expect.objectContaining({ message: expect.stringContaining("Node not found in PATH") }),
|
||||
);
|
||||
expect(runExecMock).toHaveBeenCalledWith("which", ["node"], {
|
||||
baseEnv: undefined,
|
||||
logOutput: false,
|
||||
timeoutMs: 5_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("throws a clear error when node is unavailable", async () => {
|
||||
await expect(
|
||||
resolveQaNodeExecPath({
|
||||
|
||||
@@ -9,11 +9,18 @@ type ExecFileAsync = (
|
||||
options: {
|
||||
encoding: "utf8";
|
||||
env?: NodeJS.ProcessEnv;
|
||||
timeoutMs: number;
|
||||
},
|
||||
) => Promise<{ stdout: string; stderr: string }>;
|
||||
|
||||
const NODE_BINARY_LOOKUP_TIMEOUT_MS = 5_000;
|
||||
|
||||
const execFileAsync: ExecFileAsync = async (file, args, options) =>
|
||||
await runExec(file, [...args], { baseEnv: options.env, logOutput: false });
|
||||
await runExec(file, [...args], {
|
||||
baseEnv: options.env,
|
||||
logOutput: false,
|
||||
timeoutMs: options.timeoutMs,
|
||||
});
|
||||
|
||||
function isNodeExecPath(execPath: string, platform: NodeJS.Platform): boolean {
|
||||
const pathModule = platform === "win32" ? path.win32 : path.posix;
|
||||
@@ -48,6 +55,7 @@ export async function resolveQaNodeExecPath(params?: {
|
||||
({ stdout } = await execFileImpl(locator, ["node"], {
|
||||
encoding: "utf8",
|
||||
env: params?.env,
|
||||
timeoutMs: NODE_BINARY_LOOKUP_TIMEOUT_MS,
|
||||
}));
|
||||
} catch {
|
||||
throw new Error(
|
||||
|
||||
Reference in New Issue
Block a user