Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/web/__tests__/spawn.test.ts

Raw
import { 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("");
	});
});