diff --git a/packages/cli/src/utils/npxCommand.test.ts b/packages/cli/src/utils/npxCommand.test.ts index af41b3a01b..95253f9bc1 100644 --- a/packages/cli/src/utils/npxCommand.test.ts +++ b/packages/cli/src/utils/npxCommand.test.ts @@ -1,4 +1,7 @@ import { execFileSync } from "node:child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { delimiter, join } from "node:path"; import { describe, expect, it } from "vitest"; import { buildNpmCommand, buildNpxCommand } from "./npxCommand.js"; @@ -14,18 +17,28 @@ describe("buildNpxCommand", () => { }); }); - // Real npx cold-start on Windows CI routinely exceeds vitest's 5s default, - // making this smoke test flaky. Give it generous headroom (it still asserts - // a real version string, so it isn't reduced to a tautology by mocking). - it("executes the host npx version check through the resolved command", () => { - const npx = buildNpxCommand(["--version"]); - const version = execFileSync(npx.command, npx.args, { - encoding: "utf8", - timeout: 30_000, - }).trim(); - - expect(version).toMatch(/^\d+\.\d+\.\d+/); - }, 60_000); + // Runs the built command for real against a stub `npx` placed first on PATH, so the + // cmd.exe -> npx.cmd resolution is exercised without the host's npx or the network. + it("resolves and runs the npx shim through the built command", () => { + const dir = mkdtempSync(join(tmpdir(), "hf-npx-stub-")); + try { + if (process.platform === "win32") { + writeFileSync(join(dir, "npx.cmd"), "@echo 9.9.9\r\n"); + } else { + writeFileSync(join(dir, "npx"), "#!/bin/sh\necho 9.9.9\n", { mode: 0o755 }); + } + const pathKey = Object.keys(process.env).find((k) => k.toLowerCase() === "path") ?? "PATH"; + const npx = buildNpxCommand(["--version"]); + const out = execFileSync(npx.command, npx.args, { + encoding: "utf8", + timeout: 15_000, + env: { ...process.env, [pathKey]: `${dir}${delimiter}${process.env[pathKey] ?? ""}` }, + }); + expect(out.trim()).toBe("9.9.9"); + } finally { + rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); + } + }); }); describe("buildNpmCommand", () => {