repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/git-safe/__tests__/git-policy.test.ts
Rawimport { spawnSync } from "node:child_process";
import {
chmodSync,
copyFileSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { applyGitPolicy, createGitPolicyRuntime } from "../git-policy";
const testDirs: string[] = [];
afterEach(() => {
for (const dir of testDirs.splice(0))
rmSync(dir, { recursive: true, force: true });
});
describe("git-safe agent Git policy", () => {
it("generates and reuses a wrapper with fixed executables", async () => {
const bin = mkdtempSync(path.join(tmpdir(), "pi-git-policy-bin-"));
testDirs.push(bin);
const git = path.join(
bin,
process.platform === "win32" ? "git.exe" : "git",
);
if (process.platform === "win32") copyFileSync(process.execPath, git);
else {
writeFileSync(git, "#!/bin/sh\nexit 0\n", "utf8");
chmodSync(git, 0o755);
}
const first = await createGitPolicyRuntime({ PATH: bin }, process.platform);
const second = await createGitPolicyRuntime(
{ PATH: bin },
process.platform,
);
const wrapper = readFileSync(
path.join(first.wrapperDir, "git-wrapper.cjs"),
"utf8",
);
expect(second).toBe(first);
expect(wrapper).toContain(JSON.stringify(first.realGit));
expect(wrapper).not.toContain("__PI_GIT_SAFE_REAL_GIT__");
});
it.runIf(process.platform === "win32")(
"launches when callers disable MSYS argument conversion",
async () => {
const bin = mkdtempSync(path.join(tmpdir(), "pi-git-policy-bin-"));
testDirs.push(bin);
copyFileSync(process.execPath, path.join(bin, "git.exe"));
const runtime = await createGitPolicyRuntime({ PATH: bin }, "win32");
const launcher = path
.join(runtime.wrapperDir, "git")
.replaceAll("\\", "/")
.replace(
/^([A-Za-z]):\//u,
(_match, drive: string) => `/${drive.toLowerCase()}/`,
);
const result = spawnSync(
"bash",
["-lc", `MSYS2_ARG_CONV_EXCL='*' '${launcher}' --version`],
{ encoding: "utf8" },
);
expect(result.status, result.stderr).toBe(0);
expect(result.stdout.trim()).toBe(process.version);
rmSync(path.join(runtime.wrapperDir, "git-wrapper.cjs"));
const unavailable = spawnSync(
"bash",
["-lc", `'${launcher}' --version`],
{
encoding: "utf8",
},
);
expect(unavailable.status).toBe(127);
expect(unavailable.stderr.trim()).toBe(
"git: command unavailable; ask the user to restart Pi",
);
},
);
it("routes Git, command, and env through the executable wrapper", () => {
const runtime = { wrapperDir: "/tmp/git policy", realGit: "/usr/bin/git" };
const wrapped = applyGitPolicy("git status", runtime);
expect(wrapped).toContain("'/tmp/git policy'");
expect(wrapped).toContain("'/tmp/git policy/git'");
expect(wrapped).not.toContain("PI_GIT_SAFE_REAL_GIT");
expect(wrapped).not.toContain("PI_GIT_SAFE_NODE");
expect(applyGitPolicy(wrapped, runtime)).not.toBe(wrapped);
});
it("converts Windows paths for Git Bash", () => {
const wrapped = applyGitPolicy("git status", {
wrapperDir: "C:\\Users\\me\\AppData\\Local\\Temp\\git-safe",
realGit: "C:\\Program Files\\Git\\cmd\\git.exe",
});
expect(wrapped).toContain(
"export PATH='/c/Users/me/AppData/Local/Temp/git-safe':\"$PATH\"",
);
expect(wrapped).toContain(
"'/c/Users/me/AppData/Local/Temp/git-safe/git' \"$@\"",
);
});
it("does not trust an agent-supplied policy marker", () => {
const command = "# pi-git-safe-policy-v1\ngit status";
const wrapped = applyGitPolicy(command, {
wrapperDir: "/tmp/wrapper",
realGit: "/usr/bin/git",
});
expect(wrapped).not.toBe(command);
expect(wrapped.endsWith(command)).toBe(true);
});
});