pi-worktrees/.pi/extensions/pi-worktree.test.ts
David Kong fc19b36cd8 fix: address all code review findings for issue-3
Critical fixes:
- Normalize path comparison with path.resolve() (Task 001)
- Add validation guards for malformed lines in parser (Task 002)

Major fixes:
- Extract tool definitions to factory functions (Task 003)
- Use WorktreeListResult interface with enriched metadata (Tasks 004, 010)
- Add unit tests for parseWorktreeList — 12 tests covering edge cases (Task 005)
- Return warning message when zero worktrees found (Task 006)

Minor fixes:
- Apply consistent JSDoc to all functions including factories (Task 007)
- Replace em-dash with ASCII separator for encoding safety (Task 008)
- Add section comments between tool factories and command registration (Task 009)

Suggestions implemented:
- Export parseWorktreeList in separate testable module (Task 011)
2026-07-24 23:01:40 +10:00

117 lines
3.9 KiB
TypeScript

/**
* Unit tests for parseWorktreeList parser.
*
* Tests cover normal output, edge cases, malformed input, and path normalization.
*/
import { describe, it, expect } from "bun:test";
import { parseWorktreeList } from "./pi-worktree-parser";
describe("parseWorktreeList", () => {
describe("normal output", () => {
it("parses a single worktree entry with branch in brackets", () => {
const rawOutput = "/home/user/project abc1234 [main]";
const result = parseWorktreeList(rawOutput, "/home/user/project");
expect(result).toHaveLength(1);
expect(result[0].path).toBe("/home/user/project");
expect(result[0].commit).toBe("abc1234");
expect(result[0].branch).toBe("main");
expect(result[0].isCurrent).toBe(true);
});
it("parses multiple worktree entries", () => {
const rawOutput = `/home/user/project abc1234 [main]
/home/user/feature-x def5678 [feature-x]`;
const result = parseWorktreeList(rawOutput, "/home/user/project");
expect(result).toHaveLength(2);
expect(result[0].branch).toBe("main");
expect(result[0].isCurrent).toBe(true);
expect(result[1].branch).toBe("feature-x");
expect(result[1].isCurrent).toBe(false);
});
it("handles detached HEAD entries", () => {
const rawOutput = "/home/user/detached fedcba9 HEAD";
const result = parseWorktreeList(rawOutput, "/other/path");
expect(result).toHaveLength(1);
expect(result[0].branch).toBe("(detached)");
expect(result[0].commit).toBe("fedcba9");
});
});
describe("path normalization (Task 001)", () => {
it("marks isCurrent=true when paths match with trailing slash difference", () => {
const rawOutput = "/home/user/project abc1234 [main]";
const result = parseWorktreeList(rawOutput, "/home/user/project/");
expect(result[0].isCurrent).toBe(true);
});
it("marks isCurrent=true when paths match with different casing in components", () => {
const rawOutput = "/home/user/Project abc1234 [main]";
// On Linux, path.resolve preserves case — this tests normalization of slashes
const result = parseWorktreeList(rawOutput, "/home/user/Project/");
expect(result[0].isCurrent).toBe(true);
});
});
describe("malformed input validation (Task 002)", () => {
it("skips lines with fewer than 3 tokens", () => {
const rawOutput = `/home/user/project abc1234 [main]
/incomplete/path`;
const result = parseWorktreeList(rawOutput, "/other");
expect(result).toHaveLength(1);
expect(result[0].path).toBe("/home/user/project");
});
it("skips lines with only 2 tokens (missing branch)", () => {
const rawOutput = `/home/user/project abc1234 [main]
/incomplete/path def5678`;
const result = parseWorktreeList(rawOutput, "/other");
expect(result).toHaveLength(1);
});
it("returns empty array for completely malformed input", () => {
const rawOutput = `just some random text`;
const result = parseWorktreeList(rawOutput, "/any/path");
expect(result).toHaveLength(0);
});
});
describe("empty input (Task 006)", () => {
it("returns empty array for empty string", () => {
const result = parseWorktreeList("", "/some/path");
expect(result).toHaveLength(0);
});
it("returns empty array for whitespace-only input", () => {
const result = parseWorktreeList(" \n \n ", "/some/path");
expect(result).toHaveLength(0);
});
});
describe("branch names with special characters", () => {
it("handles branch names with hyphens and slashes", () => {
const rawOutput = "/home/user/feature abc1234 [feature/my-branch-name]";
const result = parseWorktreeList(rawOutput, "/other");
expect(result).toHaveLength(1);
expect(result[0].branch).toBe("feature/my-branch-name");
});
it("handles branch names with underscores", () => {
const rawOutput = "/home/user/feature abc1234 [issue_42_fix]";
const result = parseWorktreeList(rawOutput, "/other");
expect(result).toHaveLength(1);
expect(result[0].branch).toBe("issue_42_fix");
});
});
});