import { test, expect } from "bun:test"; import { parseGitCommits, parseLsTreeLine, GIT_COMMIT_FORMAT, } from "./a31_git_parse.ts"; const FS = "\x1f"; const RS = "\x1e"; const fakeCommit = ( sha: string, parents: string, msg: string, ts = "2026-05-10T13:00:00+01:00", ): string => [sha, parents, "syntaxai", "syntaxai@example.com", ts, "syntaxai", "syntaxai@example.com", ts, msg].join(FS) + RS; test("parses a single commit with one parent and short message", () => { const raw = fakeCommit("abc123", "def456", "edit content/sama/skill.md\n"); const commits = parseGitCommits(raw); expect(commits).toHaveLength(1); const c = commits[0]!; expect(c.sha).toBe("abc123"); expect(c.parents).toEqual(["def456"]); expect(c.authorName).toBe("syntaxai"); expect(c.message).toBe("edit content/sama/skill.md"); }); test("parses multiple commits separated by RS", () => { const raw = fakeCommit("aaa", "bbb", "first") + fakeCommit("bbb", "ccc", "second") + fakeCommit("ccc", "", "root commit"); const commits = parseGitCommits(raw); expect(commits.map((c) => c.sha)).toEqual(["aaa", "bbb", "ccc"]); expect(commits[2]!.parents).toEqual([]); }); test("preserves multi-line commit message body", () => { const msg = "subject line\n\nbody line one\nbody line two\n"; const raw = fakeCommit("xyz", "par", msg); const c = parseGitCommits(raw)[0]!; expect(c.message).toBe("subject line\n\nbody line one\nbody line two"); }); test("merge commit has multiple parents", () => { const raw = fakeCommit("merge1", "p1 p2 p3", "merge"); const c = parseGitCommits(raw)[0]!; expect(c.parents).toEqual(["p1", "p2", "p3"]); }); test("empty input yields empty array", () => { expect(parseGitCommits("")).toEqual([]); }); test("malformed record throws", () => { expect(() => parseGitCommits("not enough fields here" + RS)).toThrow(); }); test("GIT_COMMIT_FORMAT round-trips through %x1e/%x1f hex escapes", () => { // The format string passes \x1e and \x1f as %x1e / %x1f to git's // printf-style placeholder language. This guards against accidental // edits that break the round-trip. expect(GIT_COMMIT_FORMAT).toContain("%x1f"); expect(GIT_COMMIT_FORMAT).toEndWith("%x1e"); }); test("parseLsTreeLine accepts a regular blob row", () => { const r = parseLsTreeLine("100644 blob abc123def456\tcontent/sama/skill.md"); expect(r).toEqual({ mode: "100644", type: "blob", sha: "abc123def456", path: "content/sama/skill.md", }); }); test("parseLsTreeLine accepts a tree row", () => { const r = parseLsTreeLine("040000 tree treesha\tcontent"); expect(r?.type).toBe("tree"); }); test("parseLsTreeLine returns null for blank or malformed input", () => { expect(parseLsTreeLine("")).toBeNull(); expect(parseLsTreeLine("not even tab separated")).toBeNull(); expect(parseLsTreeLine("100644 weirdtype sha\tpath")).toBeNull(); }); test("parseLsTreeLine preserves paths with embedded spaces", () => { const r = parseLsTreeLine("100644 blob abc\tcontent/with space/file.md"); expect(r?.path).toBe("content/with space/file.md"); });