import { execFileSync } from "node:child_process"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createPiSession, type PiSession, } from "./fixture/create-pi-session.ts"; import { sandboxEnv } from "./fixture/env.ts"; import { writeWindowsExecutable } from "./fixture/windows-executable.ts"; let session: PiSession | undefined; let root: string | undefined; afterEach(async () => { await session?.dispose(); session = undefined; vi.unstubAllEnvs(); if (root) rmSync(root, { recursive: true, force: true }); root = undefined; }); describe("test environment isolation", () => { it("replaces inherited Windows app-data locations before loading Pi", async () => { vi.stubEnv("LOCALAPPDATA", "C:\\outside-sandbox\\local"); vi.stubEnv("APPDATA", "C:\\outside-sandbox\\roaming"); session = await createPiSession({}); expect(session.env["LOCALAPPDATA"]).toBe( path.join(session.cwd, ".test-state"), ); expect(session.env["APPDATA"]).toBe(path.join(session.cwd, ".config")); expect(process.env["LOCALAPPDATA"]).toBe(session.env["LOCALAPPDATA"]); expect(process.env["APPDATA"]).toBe(session.env["APPDATA"]); await session.dispose(); expect(process.env["LOCALAPPDATA"]).toBe("C:\\outside-sandbox\\local"); expect(process.env["APPDATA"]).toBe("C:\\outside-sandbox\\roaming"); }); it("keeps custom XDG sandbox roots aligned with native Windows consumers", async () => { root = mkdtempSync(path.join(tmpdir(), "pi-ext-fixture-env-")); const state = path.join(root, "state"); const config = path.join(root, "config"); session = await createPiSession({ cwd: root, env: { XDG_STATE_HOME: state, XDG_CONFIG_HOME: config }, }); expect(session.env["LOCALAPPDATA"]).toBe(state); expect(session.env["APPDATA"]).toBe(config); }); it("sandboxEnv overrides inherited app-data keys with owned paths", () => { root = mkdtempSync(path.join(tmpdir(), "pi-ext-fixture-env-")); const env = sandboxEnv(root, { inheritEnv: ["LOCALAPPDATA", "APPDATA"] }); expect(env["LOCALAPPDATA"]).toBe(env["XDG_STATE_HOME"]); expect(env["APPDATA"]).toBe(env["XDG_CONFIG_HOME"]); for (const key of ["LOCALAPPDATA", "APPDATA"]) { const relative = path.relative(root, env[key]!); expect(path.isAbsolute(relative)).toBe(false); expect(relative.startsWith("..")).toBe(false); } }); it.runIf(process.platform === "win32")( "creates a native executable accepting arbitrary CLI flags", () => { root = mkdtempSync(path.join(tmpdir(), "pi-ext-native-command-")); const executable = writeWindowsExecutable( root, "probe", "native command\n", ); expect( execFileSync(executable, ["--no-config-file", "-c", "ignored"], { encoding: "utf8", timeout: 5_000, windowsHide: true, }), ).toBe("native command\n"); expect( execFileSync("probe", ["--version"], { encoding: "utf8", timeout: 5_000, windowsHide: true, env: { ...process.env, PATH: `${root}${path.delimiter}${process.env["PATH"]}`, }, }), ).toBe("native command\n"); }, ); });