import { ExecException } from "child_process" import * as fs from "fs" import * as path from "path" import { checkGitInstalled, searchCommits, getCommitInfo, getWorkingState, getGitRepositoryInfo, sanitizeGitUrl, extractRepositoryName, getWorkspaceGitInfo, convertGitUrlToHttps, getGitStatus, } from "../git" import { truncateOutput } from "../../integrations/misc/extract-text" type ExecFunction = ( command: string, options: { cwd?: string }, callback: (error: ExecException | null, result?: { stdout: string; stderr: string }) => void, ) => void type PromisifiedExec = (command: string, options?: { cwd?: string }) => Promise<{ stdout: string; stderr: string }> // Mock child_process.exec vitest.mock("child_process", () => ({ exec: vitest.fn(), })) // Mock fs.promises vitest.mock("fs", () => ({ promises: { access: vitest.fn(), readFile: vitest.fn(), }, })) // Create a mock for vscode const mockWorkspaceFolders = vitest.fn() vitest.mock("vscode", () => ({ workspace: { get workspaceFolders() { return mockWorkspaceFolders() }, }, })) // Mock util.promisify to return our own mock function vitest.mock("util", () => ({ promisify: vitest.fn((fn: ExecFunction): PromisifiedExec => { return async (command: string, options?: { cwd?: string }) => { // Call the original mock to maintain the mock implementation return new Promise((resolve, reject) => { fn( command, options || {}, (error: ExecException | null, result?: { stdout: string; stderr: string }) => { if (error) { reject(error) } else { resolve(result!) } }, ) }) } }), })) // Mock extract-text vitest.mock("../../integrations/misc/extract-text", () => ({ truncateOutput: vitest.fn((text) => text), })) import { exec } from "child_process" describe("git utils", () => { const cwd = "/test/path" beforeEach(() => { vitest.clearAllMocks() }) describe("checkGitInstalled", () => { it("should return true when git --version succeeds", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(null, { stdout: "git version 2.39.2", stderr: "" }) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await checkGitInstalled() expect(result).toBe(true) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) }) it("should return false when git --version fails", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(new Error("git not found")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await checkGitInstalled() expect(result).toBe(false) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) }) it("should handle unexpected errors gracefully", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { // Simulate an unexpected error callback(new Error("Unexpected system error")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await checkGitInstalled() expect(result).toBe(false) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) }) }) describe("searchCommits", () => { const mockCommitData = [ "abc123def456", "abc123", "fix: test commit", "John Doe", "2024-01-06", "def456abc789", "def456", "feat: new feature", "Jane Smith", "2024-01-05", ].join("\n") it("should return commits when git is installed and repo exists", async () => { // Set up mock responses const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], [ 'git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="test" --regexp-ignore-case', { stdout: mockCommitData, stderr: "" }, ], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { // Find matching response for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error(`Unexpected command: ${command}`)) }) const result = await searchCommits("test", cwd) // First verify the result is correct expect(result).toHaveLength(2) expect(result[0]).toEqual({ hash: "abc123def456", shortHash: "abc123", subject: "fix: test commit", author: "John Doe", date: "2024-01-06", }) // Then verify all commands were called correctly expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git rev-parse --git-dir", { cwd }, expect.any(Function)) expect(vitest.mocked(exec)).toHaveBeenCalledWith( 'git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="test" --regexp-ignore-case', { cwd }, expect.any(Function), ) }) it("should return empty array when git is not installed", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(new Error("git not found")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await searchCommits("test", cwd) expect(result).toEqual([]) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) }) it("should return empty array when not in a git repository", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", null], // null indicates error should be called ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { const response = responses.get(command) if (response === null) { callback(new Error("not a git repository")) return {} as any } else if (response) { callback(null, response) return {} as any } else { callback(new Error("Unexpected command")) return {} as any } }) const result = await searchCommits("test", cwd) expect(result).toEqual([]) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) expect(vitest.mocked(exec)).toHaveBeenCalledWith("git rev-parse --git-dir", { cwd }, expect.any(Function)) }) it("should handle hash search when grep search returns no results", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], [ 'git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="abc123" --regexp-ignore-case', { stdout: "", stderr: "" }, ], [ 'git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --author-date-order abc123', { stdout: mockCommitData, stderr: "" }, ], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await searchCommits("abc123", cwd) expect(result).toHaveLength(2) expect(result[0]).toEqual({ hash: "abc123def456", shortHash: "abc123", subject: "fix: test commit", author: "John Doe", date: "2024-01-06", }) }) }) describe("getCommitInfo", () => { const mockCommitInfo = [ "abc123def456", "abc123", "fix: test commit", "John Doe", "2024-01-06", "Detailed description", ].join("\n") const mockStats = "1 file changed, 2 insertions(+), 1 deletion(-)" const mockDiff = "@@ -1,1 +1,2 @@\n-old line\n+new line" it("should return formatted commit info", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], [ 'git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch abc123', { stdout: mockCommitInfo, stderr: "" }, ], ['git show --stat --format="" abc123', { stdout: mockStats, stderr: "" }], ['git show --format="" abc123', { stdout: mockDiff, stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command.startsWith(cmd)) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getCommitInfo("abc123", cwd) expect(result).toContain("Commit: abc123") expect(result).toContain("Author: John Doe") expect(result).toContain("Files Changed:") expect(result).toContain("Full Changes:") expect(vitest.mocked(truncateOutput)).toHaveBeenCalled() }) it("should return error message when git is not installed", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(new Error("git not found")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await getCommitInfo("abc123", cwd) expect(result).toBe("Git is not installed") }) it("should return error message when not in a git repository", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", null], // null indicates error should be called ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { const response = responses.get(command) if (response === null) { callback(new Error("not a git repository")) return {} as any } else if (response) { callback(null, response) return {} as any } else { callback(new Error("Unexpected command")) return {} as any } }) const result = await getCommitInfo("abc123", cwd) expect(result).toBe("Not a git repository") }) }) describe("getWorkingState", () => { const mockStatus = " M src/file1.ts\n?? src/file2.ts" const mockDiff = "@@ -1,1 +1,2 @@\n-old line\n+new line" it("should return working directory changes", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --short", { stdout: mockStatus, stderr: "" }], ["git diff HEAD", { stdout: mockDiff, stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getWorkingState(cwd) expect(result).toContain("Working directory changes:") expect(result).toContain("src/file1.ts") expect(result).toContain("src/file2.ts") expect(vitest.mocked(truncateOutput)).toHaveBeenCalled() }) it("should return message when working directory is clean", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --short", { stdout: "", stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getWorkingState(cwd) expect(result).toBe("No changes in working directory") }) it("should return error message when git is not installed", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(new Error("git not found")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await getWorkingState(cwd) expect(result).toBe("Git is not installed") }) it("should return error message when not in a git repository", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", null], // null indicates error should be called ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { const response = responses.get(command) if (response === null) { callback(new Error("not a git repository")) return {} as any } else if (response) { callback(null, response) return {} as any } else { callback(new Error("Unexpected command")) return {} as any } }) const result = await getWorkingState(cwd) expect(result).toBe("Not a git repository") }) }) }) describe("getGitRepositoryInfo", () => { const workspaceRoot = "/test/workspace" const gitDir = path.join(workspaceRoot, ".git") const configPath = path.join(gitDir, "config") const headPath = path.join(gitDir, "HEAD") beforeEach(() => { vitest.clearAllMocks() }) it("should return empty object when not a git repository", async () => { // Mock fs.access to throw error (directory doesn't exist) vitest.mocked(fs.promises.access).mockRejectedValueOnce(new Error("ENOENT")) const result = await getGitRepositoryInfo(workspaceRoot) expect(result).toEqual({}) expect(fs.promises.access).toHaveBeenCalledWith(gitDir) }) it("should extract repository info from git config", async () => { // Clear previous mocks vitest.clearAllMocks() // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory vitest.mocked(fs.promises.access).mockResolvedValue(undefined) // Mock git config file content const mockConfig = ` [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = https://github.com/RooCodeInc/Roo-Code.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] remote = origin merge = refs/heads/main ` // Mock HEAD file content const mockHead = "ref: refs/heads/main" // Setup the readFile mock to return different values based on the path gitSpy.mockImplementation((path: any, encoding: any) => { if (path === configPath) { return Promise.resolve(mockConfig) } else if (path === headPath) { return Promise.resolve(mockHead) } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getGitRepositoryInfo(workspaceRoot) expect(result).toEqual({ repositoryUrl: "https://github.com/RooCodeInc/Roo-Code.git", repositoryName: "RooCodeInc/Roo-Code", defaultBranch: "main", }) // Verify config file was read expect(gitSpy).toHaveBeenCalledWith(configPath, "utf8") // The implementation might not always read the HEAD file if it already found the branch in config // So we don't assert that it was called }) it("should handle missing repository URL in config", async () => { // Clear previous mocks vitest.clearAllMocks() // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory vitest.mocked(fs.promises.access).mockResolvedValue(undefined) // Mock git config file without URL const mockConfig = ` [core] repositoryformatversion = 0 filemode = true bare = false ` // Mock HEAD file content const mockHead = "ref: refs/heads/main" // Setup the readFile mock to return different values based on the path gitSpy.mockImplementation((path: any, encoding: any) => { if (path === configPath) { return Promise.resolve(mockConfig) } else if (path === headPath) { return Promise.resolve(mockHead) } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getGitRepositoryInfo(workspaceRoot) expect(result).toEqual({ defaultBranch: "main", }) }) it("should handle errors when reading git config", async () => { // Clear previous mocks vitest.clearAllMocks() // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory vitest.mocked(fs.promises.access).mockResolvedValue(undefined) // Setup the readFile mock to return different values based on the path gitSpy.mockImplementation((path: any, encoding: any) => { if (path === configPath) { return Promise.reject(new Error("Failed to read config")) } else if (path === headPath) { return Promise.resolve("ref: refs/heads/main") } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getGitRepositoryInfo(workspaceRoot) expect(result).toEqual({ defaultBranch: "main", }) }) it("should handle errors when reading HEAD file", async () => { // Clear previous mocks vitest.clearAllMocks() // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory vitest.mocked(fs.promises.access).mockResolvedValue(undefined) // Setup the readFile mock to return different values based on the path gitSpy.mockImplementation((path: any, encoding: any) => { if (path === configPath) { return Promise.resolve(` [remote "origin"] url = https://github.com/RooCodeInc/Roo-Code.git `) } else if (path === headPath) { return Promise.reject(new Error("Failed to read HEAD")) } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getGitRepositoryInfo(workspaceRoot) expect(result).toEqual({ repositoryUrl: "https://github.com/RooCodeInc/Roo-Code.git", repositoryName: "RooCodeInc/Roo-Code", }) }) it("should convert SSH URLs to HTTPS format", async () => { // Clear previous mocks vitest.clearAllMocks() // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory vitest.mocked(fs.promises.access).mockResolvedValue(undefined) // Mock git config file with SSH URL const mockConfig = ` [core] repositoryformatversion = 0 filemode = true bare = false [remote "origin"] url = git@github.com:RooCodeInc/Roo-Code.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] remote = origin merge = refs/heads/main ` // Mock HEAD file content const mockHead = "ref: refs/heads/main" // Setup the readFile mock to return different values based on the path gitSpy.mockImplementation((path: any, encoding: any) => { if (path === configPath) { return Promise.resolve(mockConfig) } else if (path === headPath) { return Promise.resolve(mockHead) } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getGitRepositoryInfo(workspaceRoot) // Verify that the SSH URL was converted to HTTPS expect(result).toEqual({ repositoryUrl: "https://github.com/RooCodeInc/Roo-Code.git", repositoryName: "RooCodeInc/Roo-Code", defaultBranch: "main", }) }) }) describe("convertGitUrlToHttps", () => { it("should leave HTTPS URLs unchanged", () => { const url = "https://github.com/RooCodeInc/Roo-Code.git" const converted = convertGitUrlToHttps(url) expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should convert SSH URLs to HTTPS format", () => { const url = "git@github.com:RooCodeInc/Roo-Code.git" const converted = convertGitUrlToHttps(url) expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should convert SSH URLs with ssh:// prefix to HTTPS format", () => { const url = "ssh://git@github.com/RooCodeInc/Roo-Code.git" const converted = convertGitUrlToHttps(url) expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should handle URLs without git@ prefix", () => { const url = "ssh://github.com/RooCodeInc/Roo-Code.git" const converted = convertGitUrlToHttps(url) expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should handle invalid URLs gracefully", () => { const url = "not-a-valid-url" const converted = convertGitUrlToHttps(url) expect(converted).toBe("not-a-valid-url") }) }) describe("sanitizeGitUrl", () => { it("should sanitize HTTPS URLs with credentials", () => { const url = "https://username:password@github.com/RooCodeInc/Roo-Code.git" const sanitized = sanitizeGitUrl(url) expect(sanitized).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should leave SSH URLs unchanged", () => { const url = "git@github.com:RooCodeInc/Roo-Code.git" const sanitized = sanitizeGitUrl(url) expect(sanitized).toBe("git@github.com:RooCodeInc/Roo-Code.git") }) it("should leave SSH URLs with ssh:// prefix unchanged", () => { const url = "ssh://git@github.com/RooCodeInc/Roo-Code.git" const sanitized = sanitizeGitUrl(url) expect(sanitized).toBe("ssh://git@github.com/RooCodeInc/Roo-Code.git") }) it("should remove tokens from other URL formats", () => { const url = "https://oauth2:ghp_abcdef1234567890abcdef1234567890abcdef@github.com/RooCodeInc/Roo-Code.git" const sanitized = sanitizeGitUrl(url) expect(sanitized).toBe("https://github.com/RooCodeInc/Roo-Code.git") }) it("should handle invalid URLs gracefully", () => { const url = "not-a-valid-url" const sanitized = sanitizeGitUrl(url) expect(sanitized).toBe("not-a-valid-url") }) }) describe("extractRepositoryName", () => { it("should extract repository name from HTTPS URL", () => { const url = "https://github.com/RooCodeInc/Roo-Code.git" const repoName = extractRepositoryName(url) expect(repoName).toBe("RooCodeInc/Roo-Code") }) it("should extract repository name from HTTPS URL without .git suffix", () => { const url = "https://github.com/RooCodeInc/Roo-Code" const repoName = extractRepositoryName(url) expect(repoName).toBe("RooCodeInc/Roo-Code") }) it("should extract repository name from SSH URL", () => { const url = "git@github.com:RooCodeInc/Roo-Code.git" const repoName = extractRepositoryName(url) expect(repoName).toBe("RooCodeInc/Roo-Code") }) it("should extract repository name from SSH URL with ssh:// prefix", () => { const url = "ssh://git@github.com/RooCodeInc/Roo-Code.git" const repoName = extractRepositoryName(url) expect(repoName).toBe("RooCodeInc/Roo-Code") }) it("should return empty string for unrecognized URL formats", () => { const url = "not-a-valid-git-url" const repoName = extractRepositoryName(url) expect(repoName).toBe("") }) it("should handle URLs with credentials", () => { const url = "https://username:password@github.com/RooCodeInc/Roo-Code.git" const repoName = extractRepositoryName(url) expect(repoName).toBe("RooCodeInc/Roo-Code") }) }) describe("getWorkspaceGitInfo", () => { const workspaceRoot = "/test/workspace" beforeEach(() => { vitest.clearAllMocks() }) it("should return empty object when no workspace folders", async () => { // Mock workspace with no folders mockWorkspaceFolders.mockReturnValue(undefined) const result = await getWorkspaceGitInfo() expect(result).toEqual({}) }) it("should return git info for the first workspace folder", async () => { // Clear previous mocks vitest.clearAllMocks() // Mock workspace with one folder mockWorkspaceFolders.mockReturnValue([{ uri: { fsPath: workspaceRoot }, name: "workspace", index: 0 }]) // Create a spy to track the implementation const gitSpy = vitest.spyOn(fs.promises, "access") const readFileSpy = vitest.spyOn(fs.promises, "readFile") // Mock successful access to .git directory gitSpy.mockResolvedValue(undefined) // Mock git config file content const mockConfig = ` [remote "origin"] url = https://github.com/RooCodeInc/Roo-Code.git [branch "main"] remote = origin merge = refs/heads/main ` // Setup the readFile mock to return config content readFileSpy.mockImplementation((path: any, encoding: any) => { if (path.includes("config")) { return Promise.resolve(mockConfig) } return Promise.reject(new Error(`Unexpected path: ${path}`)) }) const result = await getWorkspaceGitInfo() expect(result).toEqual({ repositoryUrl: "https://github.com/RooCodeInc/Roo-Code.git", repositoryName: "RooCodeInc/Roo-Code", defaultBranch: "main", }) // Verify the fs operations were called with the correct workspace path expect(gitSpy).toHaveBeenCalled() expect(readFileSpy).toHaveBeenCalled() }) }) describe("getGitStatus", () => { const cwd = "/test/path" beforeEach(() => { vitest.clearAllMocks() }) it("should return git status output with default maxFiles", async () => { const mockOutput = `## main...origin/main [ahead 2, behind 1] M src/staged-file.ts M src/unstaged-file.ts MM src/both-modified.ts ?? src/untracked-file.ts` const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd, 20) expect(result).toBe(mockOutput) }) it("should show only branch info when maxFiles is 0", async () => { const mockOutput = `## main...origin/main M src/file1.ts ?? src/file2.ts` const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd, 0) expect(result).toBe("## main...origin/main") expect(result).not.toContain("M src/file1.ts") expect(result).not.toContain("?? src/file2.ts") }) it("should return null when git is not installed", async () => { vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { if (command === "git --version") { callback(new Error("git not found")) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd) expect(result).toBeNull() }) it("should return null when not in a git repository", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", null], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { const response = responses.get(command) if (response === null) { callback(new Error("not a git repository")) return {} as any } else if (response) { callback(null, response) return {} as any } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd) expect(result).toBeNull() }) it("should respect maxFiles parameter", async () => { const lines = Array.from({ length: 30 }, (_, i) => { if (i === 0) return "## main" return `M file${i}.ts` }) const mockOutput = lines.join("\n") const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd, 10) expect(result).toContain("## main") expect(result).toContain("M file1.ts") expect(result).toContain("... 19 more files") expect(result).not.toContain("file15.ts") }) it("should return null when status is empty", async () => { const responses = new Map([ ["git --version", { stdout: "git version 2.39.2", stderr: "" }], ["git rev-parse --git-dir", { stdout: ".git", stderr: "" }], ["git status --porcelain=v1 --branch", { stdout: "", stderr: "" }], ]) vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { for (const [cmd, response] of responses) { if (command === cmd) { callback(null, response) return {} as any } } callback(new Error("Unexpected command")) return {} as any }) const result = await getGitStatus(cwd) expect(result).toBeNull() }) })