syntaxai/tdd.md · main · src / a31_git_parse.test.ts

a31_git_parse.test.ts 94 lines · 3113 bytes raw
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", "[email protected]", ts, "syntaxai", "[email protected]", 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");
});