diff --git a/src/shared/config-eval.test.ts b/src/shared/config-eval.test.ts index 48ddb9e3298..7891c17142c 100644 --- a/src/shared/config-eval.test.ts +++ b/src/shared/config-eval.test.ts @@ -1,12 +1,40 @@ -import { describe, expect, it } from "vitest"; +import fs from "node:fs"; +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { evaluateRuntimeEligibility, evaluateRuntimeRequires, + hasBinary, isConfigPathTruthyWithDefaults, isTruthy, resolveConfigPath, + resolveRuntimePlatform, } from "./config-eval.js"; +const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform"); +const originalPath = process.env.PATH; +const originalPathExt = process.env.PATHEXT; + +function setPlatform(platform: NodeJS.Platform): void { + Object.defineProperty(process, "platform", { + value: platform, + configurable: true, + }); +} + +afterEach(() => { + vi.restoreAllMocks(); + process.env.PATH = originalPath; + if (originalPathExt === undefined) { + delete process.env.PATHEXT; + } else { + process.env.PATHEXT = originalPathExt; + } + if (originalPlatformDescriptor) { + Object.defineProperty(process, "platform", originalPlatformDescriptor); + } +}); + describe("config-eval helpers", () => { it("normalizes truthy values across primitive types", () => { expect(isTruthy(undefined)).toBe(false); @@ -51,6 +79,53 @@ describe("config-eval helpers", () => { ).toBe(true); expect(isConfigPathTruthyWithDefaults(config, "browser.other", {})).toBe(false); }); + + it("returns the active runtime platform", () => { + setPlatform("darwin"); + expect(resolveRuntimePlatform()).toBe("darwin"); + }); + + it("caches binary lookups until PATH changes", () => { + process.env.PATH = ["/missing/bin", "/found/bin"].join(path.delimiter); + const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation((candidate) => { + if (String(candidate) === path.join("/found/bin", "tool")) { + return undefined; + } + throw new Error("missing"); + }); + + expect(hasBinary("tool")).toBe(true); + expect(hasBinary("tool")).toBe(true); + expect(accessSpy).toHaveBeenCalledTimes(2); + + process.env.PATH = "/other/bin"; + accessSpy.mockClear(); + accessSpy.mockImplementation(() => { + throw new Error("missing"); + }); + + expect(hasBinary("tool")).toBe(false); + expect(accessSpy).toHaveBeenCalledTimes(1); + }); + + it("checks PATHEXT candidates on Windows", () => { + setPlatform("win32"); + process.env.PATH = "/tools"; + process.env.PATHEXT = ".EXE;.CMD"; + const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation((candidate) => { + if (String(candidate) === "/tools/tool.CMD") { + return undefined; + } + throw new Error("missing"); + }); + + expect(hasBinary("tool")).toBe(true); + expect(accessSpy.mock.calls.map(([candidate]) => String(candidate))).toEqual([ + "/tools/tool", + "/tools/tool.EXE", + "/tools/tool.CMD", + ]); + }); }); describe("evaluateRuntimeRequires", () => {