98 lines
3.1 KiB
TypeScript
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 });
|
|
});
|
|
});
|