Files
hyperframes/packages/cli/src/commands/upgrade.test.ts
T

59 lines
2.0 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
/**
* Pins the security-relevant contract of the `--yes` install path: the detected
* manager binary is spawned via execFileSync with `shell: false` and the exact
* argv from installInvocation — no shell, so a version can never be re-parsed
* as shell syntax. installInvocation's argv correctness is covered separately
* in installerDetection.test.ts; this locks how it's executed.
*/
describe("runDetectedInstall", () => {
afterEach(() => {
vi.doUnmock("node:child_process");
vi.resetModules();
});
it("spawns the manager binary with shell:false and inherited stdio", async () => {
const execSpy = vi.fn();
vi.resetModules();
vi.doMock("node:child_process", () => ({ execFileSync: execSpy }));
const { runDetectedInstall } = await import("./upgrade.js");
runDetectedInstall(
{ bin: "bun", args: ["add", "-g", "hyperframes@1.2.3"] },
"bun add -g hyperframes@1.2.3",
"1.2.3",
);
expect(execSpy).toHaveBeenCalledTimes(1);
expect(execSpy).toHaveBeenCalledWith("bun", ["add", "-g", "hyperframes@1.2.3"], {
stdio: "inherit",
shell: false,
});
});
it("sets a non-zero exit code when the install fails, without throwing", async () => {
const execSpy = vi.fn(() => {
throw new Error("install boom");
});
vi.resetModules();
vi.doMock("node:child_process", () => ({ execFileSync: execSpy }));
const { runDetectedInstall } = await import("./upgrade.js");
try {
expect(() =>
runDetectedInstall(
{ bin: "npm", args: ["install", "-g", "hyperframes@1.2.3"] },
"npm install -g hyperframes@1.2.3",
"1.2.3",
),
).not.toThrow();
const { consumeCommandResult } = await import("../utils/commandResult.js");
expect(consumeCommandResult().exitCode).toBe(1);
} finally {
const { consumeCommandResult } = await import("../utils/commandResult.js");
consumeCommandResult();
}
});
});