Files
clawdbot/src/git-hooks.test.ts
2026-01-19 02:02:17 +00:00

98 lines
3.1 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import {
filterOxfmtTargets,
filterOutPartialTargets,
findPartiallyStagedFiles,
splitNullDelimited,
} from "../scripts/format-staged.js";
import { setupGitHooks } from "../scripts/setup-git-hooks.js";
function makeTempDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-hooks-"));
}
describe("format-staged helpers", () => {
it("splits null-delimited output", () => {
expect(splitNullDelimited("a\0b\0")).toEqual(["a", "b"]);
expect(splitNullDelimited("")).toEqual([]);
});
it("filters oxfmt targets", () => {
const targets = filterOxfmtTargets([
"src/app.ts",
"src/app.md",
"test/foo.tsx",
"scripts/dev.ts",
"test\\bar.js",
]);
expect(targets).toEqual(["src/app.ts", "test/foo.tsx", "test/bar.js"]);
});
it("detects partially staged files", () => {
const partial = findPartiallyStagedFiles(
["src/a.ts", "test/b.tsx"],
["src/a.ts", "docs/readme.md"],
);
expect(partial).toEqual(["src/a.ts"]);
});
it("filters out partial targets", () => {
const filtered = filterOutPartialTargets(
["src/a.ts", "test/b.tsx", "test/c.ts"],
["test/b.tsx"],
);
expect(filtered).toEqual(["src/a.ts", "test/c.ts"]);
});
});
describe("setupGitHooks", () => {
it("returns git-missing when git is unavailable", () => {
const runGit = vi.fn(() => ({ status: 1, stdout: "" }));
const result = setupGitHooks({ repoRoot: "/tmp", runGit });
expect(result).toEqual({ ok: false, reason: "git-missing" });
expect(runGit).toHaveBeenCalled();
});
it("returns not-repo when not inside a work tree", () => {
const runGit = vi.fn((args) => {
if (args[0] === "--version") return { status: 0, stdout: "git version" };
if (args[0] === "rev-parse") return { status: 0, stdout: "false" };
return { status: 1, stdout: "" };
});
const result = setupGitHooks({ repoRoot: "/tmp", runGit });
expect(result).toEqual({ ok: false, reason: "not-repo" });
});
it("configures hooks path when inside a repo", () => {
const repoRoot = makeTempDir();
const hooksDir = path.join(repoRoot, "git-hooks");
fs.mkdirSync(hooksDir, { recursive: true });
const hookPath = path.join(hooksDir, "pre-commit");
fs.writeFileSync(hookPath, "#!/bin/sh\n", "utf-8");
fs.chmodSync(hookPath, 0o644);
const runGit = vi.fn((args) => {
if (args[0] === "--version") return { status: 0, stdout: "git version" };
if (args[0] === "rev-parse") return { status: 0, stdout: "true" };
if (args[0] === "config") return { status: 0, stdout: "" };
return { status: 1, stdout: "" };
});
const result = setupGitHooks({ repoRoot, runGit });
expect(result).toEqual({ ok: true });
expect(runGit.mock.calls.some(([args]) => args[0] === "config")).toBe(true);
if (process.platform !== "win32") {
const mode = fs.statSync(hookPath).mode & 0o777;
expect(mode & 0o100).toBeTruthy();
}
fs.rmSync(repoRoot, { recursive: true, force: true });
});
});