mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
838 lines
25 KiB
TypeScript
838 lines
25 KiB
TypeScript
import { ExecException } from "child_process"
|
|
import * as vscode from "vscode"
|
|
import * as fs from "fs"
|
|
import * as path from "path"
|
|
|
|
import {
|
|
checkGitInstalled,
|
|
searchCommits,
|
|
getCommitInfo,
|
|
getWorkingState,
|
|
getGitRepositoryInfo,
|
|
sanitizeGitUrl,
|
|
extractRepositoryName,
|
|
getWorkspaceGitInfo,
|
|
GitRepositoryInfo,
|
|
convertGitUrlToHttps,
|
|
} 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()
|
|
})
|
|
})
|