Files
pi-config/extensions/rpiv-pi/extensions/rpiv-core/git-context.test.ts

110 lines
3.9 KiB
TypeScript

import { createMockPi, stubGitExec } from "@juicesharp/rpiv-test-utils";
import { beforeEach, describe, expect, it } from "vitest";
import { clearGitContextCache, getGitContext, resetInjectedMarker, takeGitContextIfChanged } from "./git-context.js";
beforeEach(() => {
clearGitContextCache();
resetInjectedMarker();
});
describe("getGitContext", () => {
it("parses branch + commit + user from three exec calls", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc1234", user: "alice" }) as never,
});
const ctx = await getGitContext(pi);
expect(ctx).toEqual({ branch: "main", commit: "abc1234", user: "alice" });
});
it("remaps literal HEAD to 'detached'", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "HEAD", commit: "abc", user: "alice" }) as never,
});
const ctx = await getGitContext(pi);
expect(ctx?.branch).toBe("detached");
});
it("returns null when both branch and commit are empty (not a repo)", async () => {
const { pi } = createMockPi({ exec: stubGitExec({}) as never });
expect(await getGitContext(pi)).toBeNull();
});
it("falls back to process.env.USER when git config user.name errors", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc", userError: new Error("no config") }) as never,
});
process.env.USER = "env-alice";
const ctx = await getGitContext(pi);
expect(ctx?.user).toBe("env-alice");
});
it("falls back to 'unknown' when neither git nor env has user", async () => {
const origUser = process.env.USER;
delete process.env.USER;
try {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc", userError: new Error("x") }) as never,
});
const ctx = await getGitContext(pi);
expect(ctx?.user).toBe("unknown");
} finally {
if (origUser) process.env.USER = origUser;
}
});
it("memoises: subsequent calls do not re-exec", async () => {
const exec = stubGitExec({ branch: "main", commit: "abc", user: "alice" });
const { pi } = createMockPi({ exec: exec as never });
await getGitContext(pi);
await getGitContext(pi);
expect(exec).toHaveBeenCalledTimes(3); // 3 initial exec calls, no second-round
});
it("clearGitContextCache forces re-read", async () => {
const exec = stubGitExec({ branch: "main", commit: "abc", user: "alice" });
const { pi } = createMockPi({ exec: exec as never });
await getGitContext(pi);
clearGitContextCache();
await getGitContext(pi);
expect(exec).toHaveBeenCalledTimes(6);
});
});
describe("takeGitContextIfChanged", () => {
it("returns the context-line on first call", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc", user: "alice" }) as never,
});
const r = await takeGitContextIfChanged(pi);
expect(r).toContain("- Branch: main");
expect(r).toContain("- Commit: abc");
expect(r).toContain("- User: alice");
});
it("returns null on second call when signature unchanged", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc", user: "alice" }) as never,
});
await takeGitContextIfChanged(pi);
expect(await takeGitContextIfChanged(pi)).toBeNull();
});
it("re-emits after clearGitContextCache + resetInjectedMarker + signature change", async () => {
const { pi } = createMockPi({
exec: stubGitExec({ branch: "main", commit: "abc", user: "alice" }) as never,
});
await takeGitContextIfChanged(pi);
clearGitContextCache();
resetInjectedMarker();
const { pi: pi2 } = createMockPi({
exec: stubGitExec({ branch: "feature", commit: "def", user: "alice" }) as never,
});
expect(await takeGitContextIfChanged(pi2)).not.toBeNull();
});
it("returns null when not in a git repo", async () => {
const { pi } = createMockPi({ exec: stubGitExec({}) as never });
expect(await takeGitContextIfChanged(pi)).toBeNull();
});
});