fix: add git hook setup and stable config hash sorting
This commit is contained in:
97
src/git-hooks.test.ts
Normal file
97
src/git-hooks.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
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 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user