repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/web/__tests__/spawn.test.ts
Rawimport { randomUUID } from "node:crypto";
import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { checkToolAvailability, resolveCommand, spawnToText } from "../spawn";
const originalPath = process.env.PATH;
const originalPathAlt = process.env.Path;
afterEach(() => {
if (originalPath === undefined) delete process.env.PATH;
else process.env.PATH = originalPath;
if (originalPathAlt === undefined) delete process.env.Path;
else process.env.Path = originalPathAlt;
});
async function makeFakeTool(dir: string, name: string) {
if (process.platform === "win32") {
const toolPath = path.join(dir, `${name}.cmd`);
await writeFile(toolPath, "@echo off\r\nexit /b 0\r\n");
return toolPath;
}
const toolPath = path.join(dir, name);
await writeFile(toolPath, "#!/bin/sh\nexit 0\n", { mode: 0o755 });
return toolPath;
}
describe("checkToolAvailability", () => {
it("finds executables on PATH without requiring Unix which", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "pi-web-path-"));
const name = `fake-tool-${randomUUID()}`;
await makeFakeTool(dir, name);
process.env.PATH = dir;
await expect(checkToolAvailability(name, "missing")).resolves.toBeNull();
});
it("does not keep stale negative cache entries after PATH changes", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "pi-web-path-"));
const emptyDir = await mkdtemp(path.join(tmpdir(), "pi-web-empty-path-"));
const name = `fake-tool-${randomUUID()}`;
process.env.PATH = emptyDir;
await expect(checkToolAvailability(name, "missing")).resolves.toBe(
"missing",
);
await makeFakeTool(dir, name);
process.env.PATH = dir;
await expect(checkToolAvailability(name, "missing")).resolves.toBeNull();
});
it("resolves explicit command paths but rejects directories", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "pi-web-explicit-path-"));
const name = `fake-tool-${randomUUID()}`;
const executable = await makeFakeTool(dir, name);
expect(await resolveCommand(path.join(dir, name))).toBe(executable);
expect(await resolveCommand(dir)).toBeNull();
});
it("runs PATH-resolved tools without a shell-specific lookup utility", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "pi web path "));
const name = `fake-tool-${randomUUID()}`;
await makeFakeTool(dir, name);
process.env.PATH = dir;
await expect(spawnToText(name, ["arg with spaces"])).resolves.toBe("");
});
});