| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- /**
- * Tests for git utilities
- */
- import { describe, it, expect, vi, beforeEach } from "vitest"
- import { getGitInfo, getGitBranch, branchExists, generateBranchName, isGitWorktree } from "../git.js"
- import simpleGit from "simple-git"
- // Mock simple-git
- vi.mock("simple-git")
- describe("git utilities", () => {
- beforeEach(() => {
- vi.clearAllMocks()
- })
- describe("getGitInfo", () => {
- it("should return default info for empty cwd", async () => {
- const result = await getGitInfo("")
- expect(result).toEqual({
- branch: null,
- isClean: true,
- isRepo: false,
- })
- })
- it("should return default info for non-git directory", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(false),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitInfo("/some/path")
- expect(result).toEqual({
- branch: null,
- isClean: true,
- isRepo: false,
- })
- })
- it("should return git info for clean repository", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- revparse: vi.fn().mockResolvedValue("main\n"),
- status: vi.fn().mockResolvedValue({ files: [] }),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitInfo("/git/repo")
- expect(result).toEqual({
- branch: "main",
- isClean: true,
- isRepo: true,
- })
- })
- it("should return git info for dirty repository", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- revparse: vi.fn().mockResolvedValue("feature-branch\n"),
- status: vi.fn().mockResolvedValue({
- files: [{ path: "file.txt", working_dir: "M" }],
- }),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitInfo("/git/repo")
- expect(result).toEqual({
- branch: "feature-branch",
- isClean: false,
- isRepo: true,
- })
- })
- it("should handle errors gracefully", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockRejectedValue(new Error("Git error")),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitInfo("/git/repo")
- expect(result).toEqual({
- branch: null,
- isClean: true,
- isRepo: false,
- })
- })
- })
- describe("getGitBranch", () => {
- it("should return null for empty cwd", async () => {
- const result = await getGitBranch("")
- expect(result).toBeNull()
- })
- it("should return null for non-git directory", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(false),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitBranch("/some/path")
- expect(result).toBeNull()
- })
- it("should return branch name", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- revparse: vi.fn().mockResolvedValue("develop\n"),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitBranch("/git/repo")
- expect(result).toBe("develop")
- })
- it("should handle errors gracefully", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockRejectedValue(new Error("Git error")),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await getGitBranch("/git/repo")
- expect(result).toBeNull()
- })
- })
- describe("branchExists", () => {
- it("should return false for empty cwd", async () => {
- const result = await branchExists("", "main")
- expect(result).toBe(false)
- })
- it("should return false for empty branchName", async () => {
- const result = await branchExists("/git/repo", "")
- expect(result).toBe(false)
- })
- it("should return false for non-git directory", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(false),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await branchExists("/some/path", "main")
- expect(result).toBe(false)
- })
- it("should return true when local branch exists", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- branch: vi.fn().mockResolvedValue({
- all: ["main", "develop", "feature-branch"],
- }),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await branchExists("/git/repo", "feature-branch")
- expect(result).toBe(true)
- })
- it("should return true when remote branch exists", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- branch: vi.fn().mockResolvedValue({
- all: ["main", "remotes/origin/feature-branch"],
- }),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await branchExists("/git/repo", "feature-branch")
- expect(result).toBe(true)
- })
- it("should return false when branch does not exist", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- branch: vi.fn().mockResolvedValue({
- all: ["main", "develop"],
- }),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await branchExists("/git/repo", "nonexistent")
- expect(result).toBe(false)
- })
- it("should handle errors gracefully", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockRejectedValue(new Error("Git error")),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await branchExists("/git/repo", "main")
- expect(result).toBe(false)
- })
- })
- describe("generateBranchName", () => {
- it("should generate branch name with lowercase and hyphens", () => {
- const result = generateBranchName("Fix Bug in Auth")
- expect(result).toMatch(/^fix-bug-in-auth-\d+$/)
- })
- it("should replace special characters with hyphens", () => {
- const result = generateBranchName("Feature: Add @user support!")
- expect(result).toMatch(/^feature-add-user-support-\d+$/)
- })
- it("should remove leading and trailing hyphens", () => {
- const result = generateBranchName("---test---")
- expect(result).toMatch(/^test-\d+$/)
- })
- it("should collapse multiple hyphens into one", () => {
- const result = generateBranchName("fix multiple spaces")
- expect(result).toMatch(/^fix-multiple-spaces-\d+$/)
- })
- it("should truncate to 50 characters", () => {
- const longPrompt = "a".repeat(100)
- const result = generateBranchName(longPrompt)
- const withoutTimestamp = result.split("-").slice(0, -1).join("-")
- expect(withoutTimestamp.length).toBeLessThanOrEqual(50)
- })
- it("should add timestamp for uniqueness", async () => {
- const prompt = "test feature"
- const result1 = generateBranchName(prompt)
- await new Promise((resolve) => setTimeout(resolve, 5))
- const result2 = generateBranchName(prompt)
- expect(result1).not.toBe(result2)
- expect(result1).toMatch(/^test-feature-\d+$/)
- expect(result2).toMatch(/^test-feature-\d+$/)
- })
- it("should handle empty string", () => {
- const result = generateBranchName("")
- expect(result).toMatch(/^kilo-\d+$/)
- })
- it("should handle only special characters", () => {
- const result = generateBranchName("!@#$%^&*()")
- expect(result).toMatch(/^kilo-\d+$/)
- })
- it("should handle unicode characters", () => {
- const result = generateBranchName("Add 日本語 support")
- expect(result).toMatch(/^add-support-\d+$/)
- })
- it("should handle mixed case properly", () => {
- const result = generateBranchName("FixBugInAuthSystem")
- expect(result).toMatch(/^fixbuginauthsystem-\d+$/)
- })
- })
- describe("isGitWorktree", () => {
- it("should return false for empty cwd", async () => {
- const result = await isGitWorktree("")
- expect(result).toBe(false)
- })
- it("should return false for non-git directory", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(false),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await isGitWorktree("/some/path")
- expect(result).toBe(false)
- })
- it("should return false for regular git repository", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- revparse: vi.fn().mockResolvedValue(".git\n"),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await isGitWorktree("/git/repo")
- expect(result).toBe(false)
- })
- it("should return true for git worktree", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockResolvedValue(true),
- revparse: vi.fn().mockResolvedValue("/path/to/.git/worktrees/feature-branch\n"),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await isGitWorktree("/git/worktree")
- expect(result).toBe(true)
- })
- it("should handle errors gracefully", async () => {
- const mockGit = {
- checkIsRepo: vi.fn().mockRejectedValue(new Error("Git error")),
- }
- vi.mocked(simpleGit).mockReturnValue(mockGit as any)
- const result = await isGitWorktree("/git/repo")
- expect(result).toBe(false)
- })
- })
- })
|