From 885e237264cad93a93b547376a6971153140c3b3 Mon Sep 17 00:00:00 2001 From: Jatin Date: Thu, 17 Jul 2025 11:19:56 +0530 Subject: [PATCH 1/3] Add Git tests and API Signed-off-by: Jatin --- internal/git/git.go | 648 +++++++++++++++++- internal/git/git_test.go | 1363 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2010 insertions(+), 1 deletion(-) create mode 100644 internal/git/git_test.go diff --git a/internal/git/git.go b/internal/git/git.go index 6e44f66..90a1d04 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -1,10 +1,157 @@ package git import ( + "fmt" "os/exec" + "path/filepath" ) -// GetStatus executes `git status` and returns its output as a string. +// ExecCommand is a variable that holds the exec.Command function +// This allows it to be mocked in tests +var ExecCommand = exec.Command + +type GitCommands struct{} + +func NewGitCommands() *GitCommands { + return &GitCommands{} +} + +func (g *GitCommands) InitRepository(path string) error { + if path == "" { + path = "." + } + + cmd := ExecCommand("git", "init", path) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to initialize repository: %v\nOutput: %s", err, output) + } + + absPath, _ := filepath.Abs(path) + fmt.Printf("Initialized empty Git repository in %s\n", absPath) + return nil +} + +func (g *GitCommands) CloneRepository(repoURL, directory string) error { + if repoURL == "" { + return fmt.Errorf("repository URL is required") + } + + var cmd *exec.Cmd + if directory != "" { + cmd = exec.Command("git", "clone", repoURL, directory) + } else { + cmd = exec.Command("git", "clone", repoURL) + } + + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to clone repository: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully cloned repository: %s\n", repoURL) + return nil +} + +func (g *GitCommands) ShowStatus() error { + cmd := exec.Command("git", "status", "--porcelain") + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to get status: %v", err) + } + + if len(output) == 0 { + fmt.Println("Working directory clean") + return nil + } + + cmd = exec.Command("git", "status") + output, err = cmd.Output() + if err != nil { + return fmt.Errorf("failed to get detailed status: %v", err) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) ShowLog(options LogOptions) error { + args := []string{"log"} + + if options.Oneline { + args = append(args, "--oneline") + } + if options.Graph { + args = append(args, "--graph") + } + if options.MaxCount > 0 { + args = append(args, fmt.Sprintf("-%d", options.MaxCount)) + } + + cmd := exec.Command("git", args...) + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to get log: %v", err) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) ShowDiff(options DiffOptions) error { + args := []string{"diff"} + + if options.Cached { + args = append(args, "--cached") + } + if options.Stat { + args = append(args, "--stat") + } + if options.Commit1 != "" { + args = append(args, options.Commit1) + } + if options.Commit2 != "" { + args = append(args, options.Commit2) + } + + cmd := exec.Command("git", args...) + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to get diff: %v", err) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) ShowCommit(commitHash string) error { + if commitHash == "" { + commitHash = "HEAD" + } + + cmd := exec.Command("git", "show", commitHash) + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to show commit: %v", err) + } + + fmt.Print(string(output)) + return nil +} + +type LogOptions struct { + Oneline bool + Graph bool + MaxCount int +} + +type DiffOptions struct { + Commit1 string + Commit2 string + Cached bool + Stat bool +} + func GetStatus() (string, error) { cmd := exec.Command("git", "status") output, err := cmd.CombinedOutput() @@ -13,3 +160,502 @@ func GetStatus() (string, error) { } return string(output), nil } + +func (g *GitCommands) AddFiles(paths []string) error { + if len(paths) == 0 { + paths = []string{"."} + } + + args := append([]string{"add"}, paths...) + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to add files: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully added files to staging area\n") + return nil +} + +func (g *GitCommands) ResetFiles(paths []string) error { + if len(paths) == 0 { + return fmt.Errorf("at least one file path is required") + } + + args := append([]string{"reset"}, paths...) + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to unstage files: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully unstaged files\n") + return nil +} + +type CommitOptions struct { + Message string + Amend bool +} + +func (g *GitCommands) Commit(options CommitOptions) error { + if options.Message == "" && !options.Amend { + return fmt.Errorf("commit message is required unless amending") + } + + args := []string{"commit"} + + if options.Amend { + args = append(args, "--amend") + } + + if options.Message != "" { + args = append(args, "-m", options.Message) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to commit changes: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type BranchOptions struct { + Create bool + Delete bool + Name string +} + +func (g *GitCommands) ManageBranch(options BranchOptions) error { + args := []string{"branch"} + + if options.Delete { + if options.Name == "" { + return fmt.Errorf("branch name is required for deletion") + } + args = append(args, "-d", options.Name) + } else if options.Create { + if options.Name == "" { + return fmt.Errorf("branch name is required for creation") + } + args = append(args, options.Name) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("branch operation failed: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) Checkout(branchName string) error { + if branchName == "" { + return fmt.Errorf("branch name is required") + } + + cmd := exec.Command("git", "checkout", branchName) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to checkout branch: %v\nOutput: %s", err, output) + } + + fmt.Printf("Switched to branch '%s'\n", branchName) + return nil +} + +func (g *GitCommands) Switch(branchName string) error { + if branchName == "" { + return fmt.Errorf("branch name is required") + } + + cmd := exec.Command("git", "switch", branchName) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to switch branch: %v\nOutput: %s", err, output) + } + + fmt.Printf("Switched to branch '%s'\n", branchName) + return nil +} + +type MergeOptions struct { + BranchName string + NoFastForward bool + Message string +} + +func (g *GitCommands) Merge(options MergeOptions) error { + if options.BranchName == "" { + return fmt.Errorf("branch name is required") + } + + args := []string{"merge"} + + if options.NoFastForward { + args = append(args, "--no-ff") + } + + if options.Message != "" { + args = append(args, "-m", options.Message) + } + + args = append(args, options.BranchName) + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to merge branch: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type TagOptions struct { + Create bool + Delete bool + Name string + Message string + Commit string +} + +func (g *GitCommands) ManageTag(options TagOptions) error { + args := []string{"tag"} + + if options.Delete { + if options.Name == "" { + return fmt.Errorf("tag name is required for deletion") + } + args = append(args, "-d", options.Name) + } else if options.Create { + if options.Name == "" { + return fmt.Errorf("tag name is required for creation") + } + if options.Message != "" { + args = append(args, "-m", options.Message) + } + args = append(args, options.Name) + if options.Commit != "" { + args = append(args, options.Commit) + } + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("tag operation failed: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type RemoteOptions struct { + Add bool + Remove bool + Name string + URL string + Verbose bool +} + +func (g *GitCommands) ManageRemote(options RemoteOptions) error { + args := []string{"remote"} + + if options.Verbose { + args = append(args, "-v") + } + + if options.Add { + if options.Name == "" || options.URL == "" { + return fmt.Errorf("remote name and URL are required for adding") + } + args = append(args, "add", options.Name, options.URL) + } else if options.Remove { + if options.Name == "" { + return fmt.Errorf("remote name is required for removal") + } + args = append(args, "remove", options.Name) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("remote operation failed: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) Fetch(remote string, branch string) error { + args := []string{"fetch"} + + if remote != "" { + args = append(args, remote) + } + + if branch != "" { + args = append(args, branch) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to fetch: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type PullOptions struct { + Remote string + Branch string + Rebase bool +} + +func (g *GitCommands) Pull(options PullOptions) error { + args := []string{"pull"} + + if options.Rebase { + args = append(args, "--rebase") + } + + if options.Remote != "" { + args = append(args, options.Remote) + } + + if options.Branch != "" { + args = append(args, options.Branch) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to pull: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type PushOptions struct { + Remote string + Branch string + Force bool + SetUpstream bool + Tags bool +} + +func (g *GitCommands) Push(options PushOptions) error { + args := []string{"push"} + + if options.Force { + args = append(args, "--force") + } + + if options.SetUpstream { + args = append(args, "--set-upstream") + } + + if options.Tags { + args = append(args, "--tags") + } + + if options.Remote != "" { + args = append(args, options.Remote) + } + + if options.Branch != "" { + args = append(args, options.Branch) + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to push: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) RemoveFiles(paths []string, cached bool) error { + if len(paths) == 0 { + return fmt.Errorf("at least one file path is required") + } + + args := []string{"rm"} + + if cached { + args = append(args, "--cached") + } + + args = append(args, paths...) + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to remove files: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully removed files\n") + return nil +} + +func (g *GitCommands) MoveFile(source, destination string) error { + if source == "" || destination == "" { + return fmt.Errorf("source and destination paths are required") + } + + cmd := exec.Command("git", "mv", source, destination) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to move file: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully moved %s to %s\n", source, destination) + return nil +} + +type RestoreOptions struct { + Paths []string + Source string + Staged bool + WorkingDir bool +} + +func (g *GitCommands) Restore(options RestoreOptions) error { + if len(options.Paths) == 0 { + return fmt.Errorf("at least one file path is required") + } + + args := []string{"restore"} + + if options.Staged { + args = append(args, "--staged") + } + + if options.WorkingDir { + args = append(args, "--worktree") + } + + if options.Source != "" { + args = append(args, "--source", options.Source) + } + + args = append(args, options.Paths...) + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to restore files: %v\nOutput: %s", err, output) + } + + fmt.Printf("Successfully restored files\n") + return nil +} + +func (g *GitCommands) Revert(commitHash string) error { + if commitHash == "" { + return fmt.Errorf("commit hash is required") + } + + cmd := exec.Command("git", "revert", commitHash) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to revert commit: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +type StashOptions struct { + Push bool + Pop bool + Apply bool + List bool + Show bool + Drop bool + Message string + StashID string +} + +func (g *GitCommands) Stash(options StashOptions) error { + if !options.Push && !options.Pop && !options.Apply && !options.List && !options.Show && !options.Drop { + options.Push = true + } + + var args []string + + if options.Push { + args = []string{"stash", "push"} + if options.Message != "" { + args = append(args, "-m", options.Message) + } + } else if options.Pop { + args = []string{"stash", "pop"} + if options.StashID != "" { + args = append(args, options.StashID) + } + } else if options.Apply { + args = []string{"stash", "apply"} + if options.StashID != "" { + args = append(args, options.StashID) + } + } else if options.List { + args = []string{"stash", "list"} + } else if options.Show { + args = []string{"stash", "show"} + if options.StashID != "" { + args = append(args, options.StashID) + } + } else if options.Drop { + args = []string{"stash", "drop"} + if options.StashID != "" { + args = append(args, options.StashID) + } + } + + cmd := exec.Command("git", args...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("stash operation failed: %v\nOutput: %s", err, output) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) ListFiles() error { + cmd := exec.Command("git", "ls-files") + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to list files: %v", err) + } + + fmt.Print(string(output)) + return nil +} + +func (g *GitCommands) BlameFile(filePath string) error { + if filePath == "" { + return fmt.Errorf("file path is required") + } + + cmd := exec.Command("git", "blame", filePath) + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to blame file: %v", err) + } + + fmt.Print(string(output)) + return nil +} diff --git a/internal/git/git_test.go b/internal/git/git_test.go new file mode 100644 index 0000000..916a9b6 --- /dev/null +++ b/internal/git/git_test.go @@ -0,0 +1,1363 @@ +package git + +import ( + "os" + "os/exec" + "path/filepath" + "testing" +) + +func TestGitCommands_InitRepository(t *testing.T) { + tests := []struct { + name string + path string + wantErr bool + }{ + { + name: "init in current directory", + path: "", + wantErr: false, + }, + { + name: "init in specific directory", + path: "test-repo", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a temporary directory for testing + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + // Change to temp directory + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + err = g.InitRepository(tt.path) + + if (err != nil) != tt.wantErr { + t.Errorf("GitCommands.InitRepository() error = %v, wantErr %v", err, tt.wantErr) + return + } + + // Check if .git directory was created + gitDir := ".git" + if tt.path != "" { + gitDir = filepath.Join(tt.path, ".git") + } + + if !tt.wantErr { + if _, err := os.Stat(gitDir); os.IsNotExist(err) { + t.Errorf("expected .git directory to be created at %s", gitDir) + } + } + }) + } +} + +func TestGitCommands_CloneRepository(t *testing.T) { + tests := []struct { + name string + repoURL string + directory string + wantErr bool + }{ + { + name: "empty repository URL", + repoURL: "", + directory: "", + wantErr: true, + }, + { + name: "invalid repository URL", + repoURL: "invalid-url", + directory: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + err = g.CloneRepository(tt.repoURL, tt.directory) + + if (err != nil) != tt.wantErr { + t.Errorf("GitCommands.CloneRepository() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGitCommands_ShowStatus(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.ShowStatus() + if err == nil { + t.Error("expected error when running status outside git repository") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + err = g.ShowStatus() + if err != nil { + t.Errorf("unexpected error when running status in git repository: %v", err) + } +} + +func TestGitCommands_ShowLog(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.ShowLog(LogOptions{}) + if err == nil { + t.Error("expected error when running log outside git repository") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + err = g.ShowLog(LogOptions{}) + if err == nil { + t.Error("expected error when running log in empty git repository") + } + + tests := []struct { + name string + options LogOptions + wantErr bool + }{ + { + name: "default options", + options: LogOptions{}, + wantErr: true, + }, + { + name: "oneline option", + options: LogOptions{ + Oneline: true, + }, + wantErr: true, + }, + { + name: "graph option", + options: LogOptions{ + Graph: true, + }, + wantErr: true, + }, + { + name: "max count option", + options: LogOptions{ + MaxCount: 5, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = g.ShowLog(tt.options) + if (err != nil) != tt.wantErr { + t.Errorf("GitCommands.ShowLog() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGitCommands_ShowDiff(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.ShowDiff(DiffOptions{}) + if err == nil { + t.Error("expected error when running diff outside git repository") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + err = g.ShowDiff(DiffOptions{}) + if err != nil { + t.Errorf("unexpected error when running diff in empty git repository: %v", err) + } + + tests := []struct { + name string + options DiffOptions + wantErr bool + }{ + { + name: "default options", + options: DiffOptions{}, + wantErr: false, + }, + { + name: "cached option", + options: DiffOptions{ + Cached: true, + }, + wantErr: false, + }, + { + name: "stat option", + options: DiffOptions{ + Stat: true, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = g.ShowDiff(tt.options) + if (err != nil) != tt.wantErr { + t.Errorf("GitCommands.ShowDiff() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGitCommands_ShowCommit(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.ShowCommit("") + if err == nil { + t.Error("expected error when running show outside git repository") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + err = g.ShowCommit("") + if err == nil { + t.Error("expected error when running show in empty git repository") + } + + err = g.ShowCommit("nonexistent-hash") + if err == nil { + t.Error("expected error when running show with nonexistent commit hash") + } +} + +func TestNewGitCommands(t *testing.T) { + g := NewGitCommands() + if g == nil { + t.Error("NewGitCommands() returned nil") + } +} + +func TestGitCommands_AddFiles(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.AddFiles([]string{"."}) + if err == nil { + t.Error("expected error when running add outside git repository") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Create a test file + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + // Test with no paths (defaults to ".") + err = g.AddFiles([]string{}) + if err != nil { + t.Errorf("unexpected error when adding all files: %v", err) + } + + // Test with specific file + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Errorf("unexpected error when adding specific file: %v", err) + } +} + +func TestGitCommands_ResetFiles(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no paths provided + err = g.ResetFiles([]string{}) + if err == nil { + t.Error("expected error when no paths provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Create and add a test file + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + // Test resetting the file + err = g.ResetFiles([]string{"test.txt"}) + if err != nil { + t.Errorf("unexpected error when resetting file: %v", err) + } +} + +func TestGitCommands_Commit(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no message provided + err = g.Commit(CommitOptions{}) + if err == nil { + t.Error("expected error when no message provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create, add, and commit a test file + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + // Test committing with message + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Errorf("unexpected error when committing: %v", err) + } + + // Test amending commit + err = g.Commit(CommitOptions{Amend: true}) + if err != nil { + t.Errorf("unexpected error when amending commit: %v", err) + } +} + +// Helper function to set git config for tests +func runGitConfig(dir string) error { + cmd := exec.Command("git", "config", "user.name", "Test User") + cmd.Dir = dir + if err := cmd.Run(); err != nil { + return err + } + cmd = exec.Command("git", "config", "user.email", "test@example.com") + cmd.Dir = dir + return cmd.Run() +} + +func TestGitCommands_ManageBranch(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create an initial commit so we can create branches + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Test creating a branch + err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) + if err != nil { + t.Errorf("unexpected error when creating branch: %v", err) + } + + // Test listing branches + err = g.ManageBranch(BranchOptions{}) + if err != nil { + t.Errorf("unexpected error when listing branches: %v", err) + } + + // Test deleting a branch + err = g.ManageBranch(BranchOptions{Delete: true, Name: "test-branch"}) + if err != nil { + t.Errorf("unexpected error when deleting branch: %v", err) + } + + // Test error when no name provided for create + err = g.ManageBranch(BranchOptions{Create: true}) + if err == nil { + t.Error("expected error when no name provided for create") + } + + // Test error when no name provided for delete + err = g.ManageBranch(BranchOptions{Delete: true}) + if err == nil { + t.Error("expected error when no name provided for delete") + } +} + +func TestGitCommands_Checkout(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no branch name provided + err = g.Checkout("") + if err == nil { + t.Error("expected error when no branch name provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create an initial commit + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Create a branch + err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) + if err != nil { + t.Fatalf("failed to create branch: %v", err) + } + + // Test checkout + err = g.Checkout("test-branch") + if err != nil { + t.Errorf("unexpected error when checking out branch: %v", err) + } + + // Test checkout with nonexistent branch + err = g.Checkout("nonexistent-branch") + if err == nil { + t.Error("expected error when checking out nonexistent branch") + } +} + +func TestGitCommands_Switch(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no branch name provided + err = g.Switch("") + if err == nil { + t.Error("expected error when no branch name provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create an initial commit + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Create a branch + err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) + if err != nil { + t.Fatalf("failed to create branch: %v", err) + } + + // Test switch + err = g.Switch("test-branch") + if err != nil { + t.Errorf("unexpected error when switching branch: %v", err) + } + + // Test switch with nonexistent branch + err = g.Switch("nonexistent-branch") + if err == nil { + t.Error("expected error when switching to nonexistent branch") + } +} + +func TestGitCommands_Merge(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no branch name provided + err = g.Merge(MergeOptions{}) + if err == nil { + t.Error("expected error when no branch name provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create an initial commit on main + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Create and checkout a branch + err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) + if err != nil { + t.Fatalf("failed to create branch: %v", err) + } + + err = g.Checkout("test-branch") + if err != nil { + t.Fatalf("failed to checkout branch: %v", err) + } + + // Make a change and commit on the branch + if err := os.WriteFile(testFile, []byte("updated content"), 0644); err != nil { + t.Fatalf("failed to update test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add updated test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Update on branch"}) + if err != nil { + t.Fatalf("failed to commit on branch: %v", err) + } + + // Switch back to main + err = g.Checkout("master") + if err != nil { + t.Fatalf("failed to checkout main branch: %v", err) + } + + // Test merge + err = g.Merge(MergeOptions{BranchName: "test-branch"}) + if err != nil { + t.Errorf("unexpected error when merging branch: %v", err) + } + + // Test merge with options + err = g.Merge(MergeOptions{ + BranchName: "test-branch", + NoFastForward: true, + Message: "Merge test branch", + }) + if err != nil { + t.Errorf("unexpected error when merging with options: %v", err) + } + + // Test merge with nonexistent branch + err = g.Merge(MergeOptions{BranchName: "nonexistent-branch"}) + if err == nil { + t.Error("expected error when merging nonexistent branch") + } +} + +func TestGitCommands_ManageTag(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create an initial commit + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Test creating a tag + err = g.ManageTag(TagOptions{Create: true, Name: "v1.0.0", Message: "Version 1.0.0"}) + if err != nil { + t.Errorf("unexpected error when creating tag: %v", err) + } + + // Test listing tags + err = g.ManageTag(TagOptions{}) + if err != nil { + t.Errorf("unexpected error when listing tags: %v", err) + } + + // Test deleting a tag + err = g.ManageTag(TagOptions{Delete: true, Name: "v1.0.0"}) + if err != nil { + t.Errorf("unexpected error when deleting tag: %v", err) + } + + // Test error when no name provided for create + err = g.ManageTag(TagOptions{Create: true}) + if err == nil { + t.Error("expected error when no name provided for create") + } + + // Test error when no name provided for delete + err = g.ManageTag(TagOptions{Delete: true}) + if err == nil { + t.Error("expected error when no name provided for delete") + } +} + +func TestGitCommands_RemoveFiles(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no paths provided + err = g.RemoveFiles([]string{}, false) + if err == nil { + t.Error("expected error when no paths provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and commit it + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Test removing a file + err = g.RemoveFiles([]string{"test.txt"}, false) + if err != nil { + t.Errorf("unexpected error when removing file: %v", err) + } + + // Create and add another file to test --cached + testFile2 := filepath.Join(tempDir, "test2.txt") + if err := os.WriteFile(testFile2, []byte("test content 2"), 0644); err != nil { + t.Fatalf("failed to create second test file: %v", err) + } + + err = g.AddFiles([]string{"test2.txt"}) + if err != nil { + t.Fatalf("failed to add second test file: %v", err) + } + + // Test removing with --cached option + err = g.RemoveFiles([]string{"test2.txt"}, true) + if err != nil { + t.Errorf("unexpected error when removing file with --cached: %v", err) + } +} + +func TestGitCommands_MoveFile(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when source or destination not provided + err = g.MoveFile("", "dest.txt") + if err == nil { + t.Error("expected error when source not provided") + } + + err = g.MoveFile("source.txt", "") + if err == nil { + t.Error("expected error when destination not provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and commit it + testFile := filepath.Join(tempDir, "source.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"source.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Test moving a file + err = g.MoveFile("source.txt", "dest.txt") + if err != nil { + t.Errorf("unexpected error when moving file: %v", err) + } + + // Verify destination file exists + if _, err := os.Stat(filepath.Join(tempDir, "dest.txt")); os.IsNotExist(err) { + t.Error("destination file does not exist after move") + } +} + +func TestGitCommands_Restore(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no paths provided + err = g.Restore(RestoreOptions{Paths: []string{}}) + if err == nil { + t.Error("expected error when no paths provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and commit it + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to commit: %v", err) + } + + // Modify the file + if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { + t.Fatalf("failed to modify test file: %v", err) + } + + // Test restoring working tree changes + err = g.Restore(RestoreOptions{Paths: []string{"test.txt"}}) + if err != nil { + t.Errorf("unexpected error when restoring file: %v", err) + } + + // Add the modified file to staging + if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { + t.Fatalf("failed to modify test file again: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add modified test file: %v", err) + } + + // Test restoring from staging + err = g.Restore(RestoreOptions{Paths: []string{"test.txt"}, Staged: true}) + if err != nil { + t.Errorf("unexpected error when restoring staged file: %v", err) + } +} + +func TestGitCommands_Revert(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no commit hash provided + err = g.Revert("") + if err == nil { + t.Error("expected error when no commit hash provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and make initial commit + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to make initial commit: %v", err) + } + + // Get commit hash + cmd := exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = tempDir + commitHash, err := cmd.Output() + if err != nil { + t.Fatalf("failed to get commit hash: %v", err) + } + hashStr := string(commitHash) + hashStr = hashStr[:len(hashStr)-1] // Remove newline + + // Modify file and make a second commit + if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { + t.Fatalf("failed to modify test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add modified test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Second commit"}) + if err != nil { + t.Fatalf("failed to make second commit: %v", err) + } + + // Test reverting a commit (this might fail if there are merge conflicts) + err = g.Revert(hashStr) + if err != nil { + // In a real test, we might want to make the commits in such a way that revert won't conflict + t.Logf("Revert failed, might be due to conflicts: %v", err) + } +} + +func TestGitCommands_Stash(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and make initial commit + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Initial commit"}) + if err != nil { + t.Fatalf("failed to make initial commit: %v", err) + } + + // Modify file but don't commit + if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { + t.Fatalf("failed to modify test file: %v", err) + } + + // Test stash push + err = g.Stash(StashOptions{Push: true, Message: "Test stash"}) + if err != nil { + t.Errorf("unexpected error when pushing stash: %v", err) + } + + // Test stash list + err = g.Stash(StashOptions{List: true}) + if err != nil { + t.Errorf("unexpected error when listing stashes: %v", err) + } + + // Test stash show + err = g.Stash(StashOptions{Show: true, StashID: "stash@{0}"}) + if err != nil { + t.Errorf("unexpected error when showing stash: %v", err) + } + + // Test stash apply + err = g.Stash(StashOptions{Apply: true}) + if err != nil { + t.Errorf("unexpected error when applying stash: %v", err) + } + + // Test stash push with default options (should set Push to true) + err = g.Stash(StashOptions{}) + if err != nil { + t.Errorf("unexpected error when pushing stash with default options: %v", err) + } + + // Test stash drop + err = g.Stash(StashOptions{Drop: true}) + if err != nil { + t.Errorf("unexpected error when dropping stash: %v", err) + } +} + +func TestGitCommands_ListFiles(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Test listing files in empty repo + err = g.ListFiles() + if err != nil { + t.Errorf("unexpected error when listing files in empty repo: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and commit it + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Add test file"}) + if err != nil { + t.Fatalf("failed to commit test file: %v", err) + } + + // Test listing files with content + err = g.ListFiles() + if err != nil { + t.Errorf("unexpected error when listing files: %v", err) + } +} + +func TestGitCommands_BlameFile(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + // Should error when no file path provided + err = g.BlameFile("") + if err == nil { + t.Error("expected error when no file path provided") + } + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Set git config for test + err = runGitConfig(tempDir) + if err != nil { + t.Fatalf("failed to set git config: %v", err) + } + + // Create a file and commit it + testFile := filepath.Join(tempDir, "test.txt") + if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + err = g.AddFiles([]string{"test.txt"}) + if err != nil { + t.Fatalf("failed to add test file: %v", err) + } + + err = g.Commit(CommitOptions{Message: "Add test file"}) + if err != nil { + t.Fatalf("failed to commit test file: %v", err) + } + + // Test blame + err = g.BlameFile("test.txt") + if err != nil { + t.Errorf("unexpected error when blaming file: %v", err) + } + + // Test blame nonexistent file + err = g.BlameFile("nonexistent.txt") + if err == nil { + t.Error("expected error when blaming nonexistent file") + } +} + +func TestGitCommands_ManageRemote(t *testing.T) { + tempDir, err := os.MkdirTemp("", "git-test-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + originalDir, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalDir) + + g := NewGitCommands() + + err = g.InitRepository("") + if err != nil { + t.Fatalf("failed to initialize git repository: %v", err) + } + + // Test adding a remote without name or URL + err = g.ManageRemote(RemoteOptions{Add: true}) + if err == nil { + t.Error("expected error when adding remote without name or URL") + } + + // Test adding a remote + err = g.ManageRemote(RemoteOptions{Add: true, Name: "origin", URL: "https://github.com/example/repo.git"}) + if err != nil { + t.Errorf("unexpected error when adding remote: %v", err) + } + + // Test listing remotes + err = g.ManageRemote(RemoteOptions{Verbose: true}) + if err != nil { + t.Errorf("unexpected error when listing remotes: %v", err) + } + + // Test removing a remote without name + err = g.ManageRemote(RemoteOptions{Remove: true}) + if err == nil { + t.Error("expected error when removing remote without name") + } + + // Test removing a remote + err = g.ManageRemote(RemoteOptions{Remove: true, Name: "origin"}) + if err != nil { + t.Errorf("unexpected error when removing remote: %v", err) + } +} From 821e8eef81a3911fbcac9b8f3ff5d5f15ab0c1ab Mon Sep 17 00:00:00 2001 From: Jatin Date: Thu, 17 Jul 2025 11:43:43 +0530 Subject: [PATCH 2/3] Refactored tests Signed-off-by: Jatin --- internal/git/git_test.go | 1381 ++++---------------------------------- 1 file changed, 147 insertions(+), 1234 deletions(-) diff --git a/internal/git/git_test.go b/internal/git/git_test.go index 916a9b6..d429c44 100644 --- a/internal/git/git_test.go +++ b/internal/git/git_test.go @@ -4,272 +4,67 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" ) -func TestGitCommands_InitRepository(t *testing.T) { - tests := []struct { - name string - path string - wantErr bool - }{ - { - name: "init in current directory", - path: "", - wantErr: false, - }, - { - name: "init in specific directory", - path: "test-repo", - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a temporary directory for testing - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - // Change to temp directory - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - err = g.InitRepository(tt.path) - - if (err != nil) != tt.wantErr { - t.Errorf("GitCommands.InitRepository() error = %v, wantErr %v", err, tt.wantErr) - return - } - - // Check if .git directory was created - gitDir := ".git" - if tt.path != "" { - gitDir = filepath.Join(tt.path, ".git") - } - - if !tt.wantErr { - if _, err := os.Stat(gitDir); os.IsNotExist(err) { - t.Errorf("expected .git directory to be created at %s", gitDir) - } - } - }) - } -} - -func TestGitCommands_CloneRepository(t *testing.T) { - tests := []struct { - name string - repoURL string - directory string - wantErr bool - }{ - { - name: "empty repository URL", - repoURL: "", - directory: "", - wantErr: true, - }, - { - name: "invalid repository URL", - repoURL: "invalid-url", - directory: "", - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - err = g.CloneRepository(tt.repoURL, tt.directory) - - if (err != nil) != tt.wantErr { - t.Errorf("GitCommands.CloneRepository() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestGitCommands_ShowStatus(t *testing.T) { +// setupTestRepo creates a new temporary directory, initializes a git repository, +// and returns a cleanup function to be deferred. +func setupTestRepo(t *testing.T) (string, func()) { + t.Helper() tempDir, err := os.MkdirTemp("", "git-test-") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.ShowStatus() - if err == nil { - t.Error("expected error when running status outside git repository") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - err = g.ShowStatus() + originalDir, err := os.Getwd() if err != nil { - t.Errorf("unexpected error when running status in git repository: %v", err) + t.Fatalf("failed to get current working directory: %v", err) } -} -func TestGitCommands_ShowLog(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("failed to change to temp dir: %v", err) } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) g := NewGitCommands() - - err = g.ShowLog(LogOptions{}) - if err == nil { - t.Error("expected error when running log outside git repository") - } - - err = g.InitRepository("") - if err != nil { + if err := g.InitRepository(""); err != nil { t.Fatalf("failed to initialize git repository: %v", err) } - err = g.ShowLog(LogOptions{}) - if err == nil { - t.Error("expected error when running log in empty git repository") + // Configure git user for commits + if err := runGitConfig(tempDir); err != nil { + t.Fatalf("failed to set git config: %v", err) } - tests := []struct { - name string - options LogOptions - wantErr bool - }{ - { - name: "default options", - options: LogOptions{}, - wantErr: true, - }, - { - name: "oneline option", - options: LogOptions{ - Oneline: true, - }, - wantErr: true, - }, - { - name: "graph option", - options: LogOptions{ - Graph: true, - }, - wantErr: true, - }, - { - name: "max count option", - options: LogOptions{ - MaxCount: 5, - }, - wantErr: true, - }, + cleanup := func() { + os.Chdir(originalDir) + os.RemoveAll(tempDir) } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err = g.ShowLog(tt.options) - if (err != nil) != tt.wantErr { - t.Errorf("GitCommands.ShowLog() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } + return tempDir, cleanup } -func TestGitCommands_ShowDiff(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.ShowDiff(DiffOptions{}) - if err == nil { - t.Error("expected error when running diff outside git repository") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) +// createAndCommitFile creates a file with content and commits it. +func createAndCommitFile(t *testing.T, g *GitCommands, filename, content, message string) { + t.Helper() + if err := os.WriteFile(filename, []byte(content), 0644); err != nil { + t.Fatalf("failed to create test file %s: %v", filename, err) } - - err = g.ShowDiff(DiffOptions{}) - if err != nil { - t.Errorf("unexpected error when running diff in empty git repository: %v", err) + if err := g.AddFiles([]string{filename}); err != nil { + t.Fatalf("failed to add file %s: %v", filename, err) } - - tests := []struct { - name string - options DiffOptions - wantErr bool - }{ - { - name: "default options", - options: DiffOptions{}, - wantErr: false, - }, - { - name: "cached option", - options: DiffOptions{ - Cached: true, - }, - wantErr: false, - }, - { - name: "stat option", - options: DiffOptions{ - Stat: true, - }, - wantErr: false, - }, + if err := g.Commit(CommitOptions{Message: message}); err != nil { + t.Fatalf("failed to commit file %s: %v", filename, err) } +} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err = g.ShowDiff(tt.options) - if (err != nil) != tt.wantErr { - t.Errorf("GitCommands.ShowDiff() error = %v, wantErr %v", err, tt.wantErr) - } - }) +func TestNewGitCommands(t *testing.T) { + if g := NewGitCommands(); g == nil { + t.Error("NewGitCommands() returned nil") } } -func TestGitCommands_ShowCommit(t *testing.T) { +func TestGitCommands_InitRepository(t *testing.T) { tempDir, err := os.MkdirTemp("", "git-test-") if err != nil { t.Fatalf("failed to create temp dir: %v", err) @@ -281,1083 +76,201 @@ func TestGitCommands_ShowCommit(t *testing.T) { defer os.Chdir(originalDir) g := NewGitCommands() - - err = g.ShowCommit("") - if err == nil { - t.Error("expected error when running show outside git repository") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - err = g.ShowCommit("") - if err == nil { - t.Error("expected error when running show in empty git repository") + repoPath := "test-repo" + if err := g.InitRepository(repoPath); err != nil { + t.Fatalf("InitRepository() failed: %v", err) } - err = g.ShowCommit("nonexistent-hash") - if err == nil { - t.Error("expected error when running show with nonexistent commit hash") + if _, err := os.Stat(filepath.Join(repoPath, ".git")); os.IsNotExist(err) { + t.Errorf("expected .git directory to be created at %s", repoPath) } } -func TestNewGitCommands(t *testing.T) { +func TestGitCommands_CloneRepository(t *testing.T) { g := NewGitCommands() - if g == nil { - t.Error("NewGitCommands() returned nil") + err := g.CloneRepository("invalid-url", "") + if err == nil { + t.Error("CloneRepository() with invalid URL should have failed, but did not") } -} - -func TestGitCommands_AddFiles(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) + if !strings.Contains(err.Error(), "failed to clone repository") { + t.Errorf("expected clone error, got: %v", err) } - defer os.RemoveAll(tempDir) +} - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) +func TestGitCommands_Status(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() - err = g.AddFiles([]string{"."}) - if err == nil { - t.Error("expected error when running add outside git repository") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) + // Test on a clean repo + if err := g.ShowStatus(); err != nil { + t.Errorf("ShowStatus() on clean repo failed: %v", err) } - // Create a test file - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { + // Test with a new file + if err := os.WriteFile("new-file.txt", []byte("content"), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } - - // Test with no paths (defaults to ".") - err = g.AddFiles([]string{}) - if err != nil { - t.Errorf("unexpected error when adding all files: %v", err) - } - - // Test with specific file - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Errorf("unexpected error when adding specific file: %v", err) + if err := g.ShowStatus(); err != nil { + t.Errorf("ShowStatus() with new file failed: %v", err) } } -func TestGitCommands_ResetFiles(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) +func TestGitCommands_Log(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() + createAndCommitFile(t, g, "log-test.txt", "content", "Initial commit for log test") - // Should error when no paths provided - err = g.ResetFiles([]string{}) - if err == nil { - t.Error("expected error when no paths provided") + if err := g.ShowLog(LogOptions{}); err != nil { + t.Errorf("ShowLog() failed: %v", err) } +} - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } +func TestGitCommands_Diff(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() - // Create and add a test file - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } + g := NewGitCommands() + createAndCommitFile(t, g, "diff-test.txt", "initial", "Initial commit for diff test") - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) + // Modify the file to create a diff + if err := os.WriteFile("diff-test.txt", []byte("modified"), 0644); err != nil { + t.Fatalf("failed to modify test file: %v", err) } - // Test resetting the file - err = g.ResetFiles([]string{"test.txt"}) - if err != nil { - t.Errorf("unexpected error when resetting file: %v", err) + if err := g.ShowDiff(DiffOptions{}); err != nil { + t.Errorf("ShowDiff() failed: %v", err) } } func TestGitCommands_Commit(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() - // Should error when no message provided - err = g.Commit(CommitOptions{}) - if err == nil { - t.Error("expected error when no message provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create, add, and commit a test file - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) + // Test empty commit message + if err := g.Commit(CommitOptions{}); err == nil { + t.Error("Commit() with empty message should fail") } - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } + // Test successful commit + createAndCommitFile(t, g, "commit-test.txt", "content", "Successful commit") - // Test committing with message - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Errorf("unexpected error when committing: %v", err) + // Test amend + if err := os.WriteFile("commit-test.txt", []byte("amended content"), 0644); err != nil { + t.Fatalf("failed to amend test file: %v", err) } - - // Test amending commit - err = g.Commit(CommitOptions{Amend: true}) - if err != nil { - t.Errorf("unexpected error when amending commit: %v", err) + if err := g.AddFiles([]string{"commit-test.txt"}); err != nil { + t.Fatalf("failed to add amended file: %v", err) } -} - -// Helper function to set git config for tests -func runGitConfig(dir string) error { - cmd := exec.Command("git", "config", "user.name", "Test User") - cmd.Dir = dir - if err := cmd.Run(); err != nil { - return err + if err := g.Commit(CommitOptions{Amend: true}); err != nil { + t.Errorf("Commit() with amend failed: %v", err) } - cmd = exec.Command("git", "config", "user.email", "test@example.com") - cmd.Dir = dir - return cmd.Run() } -func TestGitCommands_ManageBranch(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) +func TestGitCommands_BranchAndCheckout(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() + createAndCommitFile(t, g, "branch-test.txt", "content", "Initial commit for branch test") - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create an initial commit so we can create branches - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Test creating a branch - err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) - if err != nil { - t.Errorf("unexpected error when creating branch: %v", err) - } + branchName := "feature-branch" - // Test listing branches - err = g.ManageBranch(BranchOptions{}) - if err != nil { - t.Errorf("unexpected error when listing branches: %v", err) + // Create branch + if err := g.ManageBranch(BranchOptions{Create: true, Name: branchName}); err != nil { + t.Fatalf("ManageBranch() create failed: %v", err) } - // Test deleting a branch - err = g.ManageBranch(BranchOptions{Delete: true, Name: "test-branch"}) - if err != nil { - t.Errorf("unexpected error when deleting branch: %v", err) + // Checkout branch + if err := g.Checkout(branchName); err != nil { + t.Fatalf("Checkout() failed: %v", err) } - // Test error when no name provided for create - err = g.ManageBranch(BranchOptions{Create: true}) - if err == nil { - t.Error("expected error when no name provided for create") + // Switch back to main/master + if err := g.Switch("master"); err != nil { + t.Fatalf("Switch() failed: %v", err) } - // Test error when no name provided for delete - err = g.ManageBranch(BranchOptions{Delete: true}) - if err == nil { - t.Error("expected error when no name provided for delete") + // Delete branch + if err := g.ManageBranch(BranchOptions{Delete: true, Name: branchName}); err != nil { + t.Fatalf("ManageBranch() delete failed: %v", err) } } -func TestGitCommands_Checkout(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) +func TestGitCommands_FileOperations(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() + createAndCommitFile(t, g, "file-ops.txt", "content", "Initial commit for file ops") - // Should error when no branch name provided - err = g.Checkout("") - if err == nil { - t.Error("expected error when no branch name provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) + // Test Add + if err := os.WriteFile("new-file.txt", []byte("new"), 0644); err != nil { + t.Fatalf("failed to create new file: %v", err) } - - // Create an initial commit - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) + if err := g.AddFiles([]string{"new-file.txt"}); err != nil { + t.Errorf("AddFiles() failed: %v", err) } - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) + // Test Reset + if err := g.ResetFiles([]string{"new-file.txt"}); err != nil { + t.Errorf("ResetFiles() failed: %v", err) } - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) + // Test Remove + if err := g.RemoveFiles([]string{"file-ops.txt"}, false); err != nil { + t.Errorf("RemoveFiles() failed: %v", err) } - - // Create a branch - err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) - if err != nil { - t.Fatalf("failed to create branch: %v", err) + if _, err := os.Stat("file-ops.txt"); !os.IsNotExist(err) { + t.Error("file should have been removed from working directory") } - // Test checkout - err = g.Checkout("test-branch") - if err != nil { - t.Errorf("unexpected error when checking out branch: %v", err) + // Test Move + createAndCommitFile(t, g, "source.txt", "move content", "Commit for move test") + if err := g.MoveFile("source.txt", "destination.txt"); err != nil { + t.Errorf("MoveFile() failed: %v", err) } - - // Test checkout with nonexistent branch - err = g.Checkout("nonexistent-branch") - if err == nil { - t.Error("expected error when checking out nonexistent branch") + if _, err := os.Stat("destination.txt"); os.IsNotExist(err) { + t.Error("destination file does not exist after move") } } -func TestGitCommands_Switch(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) +func TestGitCommands_Stash(t *testing.T) { + _, cleanup := setupTestRepo(t) + defer cleanup() g := NewGitCommands() + createAndCommitFile(t, g, "stash-test.txt", "content", "Initial commit for stash test") - // Should error when no branch name provided - err = g.Switch("") - if err == nil { - t.Error("expected error when no branch name provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) + // Modify file to create something to stash + if err := os.WriteFile("stash-test.txt", []byte("modified"), 0644); err != nil { + t.Fatalf("failed to modify test file: %v", err) } - // Create an initial commit - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) + // Stash push + if err := g.Stash(StashOptions{Push: true, Message: "test stash"}); err != nil { + t.Fatalf("Stash() push failed: %v", err) } - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) + // Stash apply + if err := g.Stash(StashOptions{Apply: true}); err != nil { + t.Errorf("Stash() apply failed: %v", err) } +} - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Create a branch - err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) - if err != nil { - t.Fatalf("failed to create branch: %v", err) - } - - // Test switch - err = g.Switch("test-branch") - if err != nil { - t.Errorf("unexpected error when switching branch: %v", err) - } - - // Test switch with nonexistent branch - err = g.Switch("nonexistent-branch") - if err == nil { - t.Error("expected error when switching to nonexistent branch") - } -} - -func TestGitCommands_Merge(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when no branch name provided - err = g.Merge(MergeOptions{}) - if err == nil { - t.Error("expected error when no branch name provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create an initial commit on main - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Create and checkout a branch - err = g.ManageBranch(BranchOptions{Create: true, Name: "test-branch"}) - if err != nil { - t.Fatalf("failed to create branch: %v", err) - } - - err = g.Checkout("test-branch") - if err != nil { - t.Fatalf("failed to checkout branch: %v", err) - } - - // Make a change and commit on the branch - if err := os.WriteFile(testFile, []byte("updated content"), 0644); err != nil { - t.Fatalf("failed to update test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add updated test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Update on branch"}) - if err != nil { - t.Fatalf("failed to commit on branch: %v", err) - } - - // Switch back to main - err = g.Checkout("master") - if err != nil { - t.Fatalf("failed to checkout main branch: %v", err) - } - - // Test merge - err = g.Merge(MergeOptions{BranchName: "test-branch"}) - if err != nil { - t.Errorf("unexpected error when merging branch: %v", err) - } - - // Test merge with options - err = g.Merge(MergeOptions{ - BranchName: "test-branch", - NoFastForward: true, - Message: "Merge test branch", - }) - if err != nil { - t.Errorf("unexpected error when merging with options: %v", err) - } - - // Test merge with nonexistent branch - err = g.Merge(MergeOptions{BranchName: "nonexistent-branch"}) - if err == nil { - t.Error("expected error when merging nonexistent branch") - } -} - -func TestGitCommands_ManageTag(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create an initial commit - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Test creating a tag - err = g.ManageTag(TagOptions{Create: true, Name: "v1.0.0", Message: "Version 1.0.0"}) - if err != nil { - t.Errorf("unexpected error when creating tag: %v", err) - } - - // Test listing tags - err = g.ManageTag(TagOptions{}) - if err != nil { - t.Errorf("unexpected error when listing tags: %v", err) - } - - // Test deleting a tag - err = g.ManageTag(TagOptions{Delete: true, Name: "v1.0.0"}) - if err != nil { - t.Errorf("unexpected error when deleting tag: %v", err) - } - - // Test error when no name provided for create - err = g.ManageTag(TagOptions{Create: true}) - if err == nil { - t.Error("expected error when no name provided for create") - } - - // Test error when no name provided for delete - err = g.ManageTag(TagOptions{Delete: true}) - if err == nil { - t.Error("expected error when no name provided for delete") - } -} - -func TestGitCommands_RemoveFiles(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when no paths provided - err = g.RemoveFiles([]string{}, false) - if err == nil { - t.Error("expected error when no paths provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and commit it - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Test removing a file - err = g.RemoveFiles([]string{"test.txt"}, false) - if err != nil { - t.Errorf("unexpected error when removing file: %v", err) - } - - // Create and add another file to test --cached - testFile2 := filepath.Join(tempDir, "test2.txt") - if err := os.WriteFile(testFile2, []byte("test content 2"), 0644); err != nil { - t.Fatalf("failed to create second test file: %v", err) - } - - err = g.AddFiles([]string{"test2.txt"}) - if err != nil { - t.Fatalf("failed to add second test file: %v", err) - } - - // Test removing with --cached option - err = g.RemoveFiles([]string{"test2.txt"}, true) - if err != nil { - t.Errorf("unexpected error when removing file with --cached: %v", err) - } -} - -func TestGitCommands_MoveFile(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when source or destination not provided - err = g.MoveFile("", "dest.txt") - if err == nil { - t.Error("expected error when source not provided") - } - - err = g.MoveFile("source.txt", "") - if err == nil { - t.Error("expected error when destination not provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and commit it - testFile := filepath.Join(tempDir, "source.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"source.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Test moving a file - err = g.MoveFile("source.txt", "dest.txt") - if err != nil { - t.Errorf("unexpected error when moving file: %v", err) - } - - // Verify destination file exists - if _, err := os.Stat(filepath.Join(tempDir, "dest.txt")); os.IsNotExist(err) { - t.Error("destination file does not exist after move") - } -} - -func TestGitCommands_Restore(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when no paths provided - err = g.Restore(RestoreOptions{Paths: []string{}}) - if err == nil { - t.Error("expected error when no paths provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and commit it - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to commit: %v", err) - } - - // Modify the file - if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { - t.Fatalf("failed to modify test file: %v", err) - } - - // Test restoring working tree changes - err = g.Restore(RestoreOptions{Paths: []string{"test.txt"}}) - if err != nil { - t.Errorf("unexpected error when restoring file: %v", err) - } - - // Add the modified file to staging - if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { - t.Fatalf("failed to modify test file again: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add modified test file: %v", err) - } - - // Test restoring from staging - err = g.Restore(RestoreOptions{Paths: []string{"test.txt"}, Staged: true}) - if err != nil { - t.Errorf("unexpected error when restoring staged file: %v", err) - } -} - -func TestGitCommands_Revert(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when no commit hash provided - err = g.Revert("") - if err == nil { - t.Error("expected error when no commit hash provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and make initial commit - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to make initial commit: %v", err) - } - - // Get commit hash - cmd := exec.Command("git", "rev-parse", "HEAD") - cmd.Dir = tempDir - commitHash, err := cmd.Output() - if err != nil { - t.Fatalf("failed to get commit hash: %v", err) - } - hashStr := string(commitHash) - hashStr = hashStr[:len(hashStr)-1] // Remove newline - - // Modify file and make a second commit - if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { - t.Fatalf("failed to modify test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add modified test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Second commit"}) - if err != nil { - t.Fatalf("failed to make second commit: %v", err) - } - - // Test reverting a commit (this might fail if there are merge conflicts) - err = g.Revert(hashStr) - if err != nil { - // In a real test, we might want to make the commits in such a way that revert won't conflict - t.Logf("Revert failed, might be due to conflicts: %v", err) - } -} - -func TestGitCommands_Stash(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and make initial commit - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("initial content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Initial commit"}) - if err != nil { - t.Fatalf("failed to make initial commit: %v", err) - } - - // Modify file but don't commit - if err := os.WriteFile(testFile, []byte("modified content"), 0644); err != nil { - t.Fatalf("failed to modify test file: %v", err) - } - - // Test stash push - err = g.Stash(StashOptions{Push: true, Message: "Test stash"}) - if err != nil { - t.Errorf("unexpected error when pushing stash: %v", err) - } - - // Test stash list - err = g.Stash(StashOptions{List: true}) - if err != nil { - t.Errorf("unexpected error when listing stashes: %v", err) - } - - // Test stash show - err = g.Stash(StashOptions{Show: true, StashID: "stash@{0}"}) - if err != nil { - t.Errorf("unexpected error when showing stash: %v", err) - } - - // Test stash apply - err = g.Stash(StashOptions{Apply: true}) - if err != nil { - t.Errorf("unexpected error when applying stash: %v", err) - } - - // Test stash push with default options (should set Push to true) - err = g.Stash(StashOptions{}) - if err != nil { - t.Errorf("unexpected error when pushing stash with default options: %v", err) - } - - // Test stash drop - err = g.Stash(StashOptions{Drop: true}) - if err != nil { - t.Errorf("unexpected error when dropping stash: %v", err) - } -} - -func TestGitCommands_ListFiles(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Test listing files in empty repo - err = g.ListFiles() - if err != nil { - t.Errorf("unexpected error when listing files in empty repo: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and commit it - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Add test file"}) - if err != nil { - t.Fatalf("failed to commit test file: %v", err) - } - - // Test listing files with content - err = g.ListFiles() - if err != nil { - t.Errorf("unexpected error when listing files: %v", err) - } -} - -func TestGitCommands_BlameFile(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - // Should error when no file path provided - err = g.BlameFile("") - if err == nil { - t.Error("expected error when no file path provided") - } - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Set git config for test - err = runGitConfig(tempDir) - if err != nil { - t.Fatalf("failed to set git config: %v", err) - } - - // Create a file and commit it - testFile := filepath.Join(tempDir, "test.txt") - if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - err = g.AddFiles([]string{"test.txt"}) - if err != nil { - t.Fatalf("failed to add test file: %v", err) - } - - err = g.Commit(CommitOptions{Message: "Add test file"}) - if err != nil { - t.Fatalf("failed to commit test file: %v", err) - } - - // Test blame - err = g.BlameFile("test.txt") - if err != nil { - t.Errorf("unexpected error when blaming file: %v", err) - } - - // Test blame nonexistent file - err = g.BlameFile("nonexistent.txt") - if err == nil { - t.Error("expected error when blaming nonexistent file") - } -} - -func TestGitCommands_ManageRemote(t *testing.T) { - tempDir, err := os.MkdirTemp("", "git-test-") - if err != nil { - t.Fatalf("failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) - - g := NewGitCommands() - - err = g.InitRepository("") - if err != nil { - t.Fatalf("failed to initialize git repository: %v", err) - } - - // Test adding a remote without name or URL - err = g.ManageRemote(RemoteOptions{Add: true}) - if err == nil { - t.Error("expected error when adding remote without name or URL") - } - - // Test adding a remote - err = g.ManageRemote(RemoteOptions{Add: true, Name: "origin", URL: "https://github.com/example/repo.git"}) - if err != nil { - t.Errorf("unexpected error when adding remote: %v", err) - } - - // Test listing remotes - err = g.ManageRemote(RemoteOptions{Verbose: true}) - if err != nil { - t.Errorf("unexpected error when listing remotes: %v", err) - } - - // Test removing a remote without name - err = g.ManageRemote(RemoteOptions{Remove: true}) - if err == nil { - t.Error("expected error when removing remote without name") - } - - // Test removing a remote - err = g.ManageRemote(RemoteOptions{Remove: true, Name: "origin"}) - if err != nil { - t.Errorf("unexpected error when removing remote: %v", err) +// Helper function to set git config for tests +func runGitConfig(dir string) error { + cmd := exec.Command("git", "config", "user.name", "Test User") + cmd.Dir = dir + if err := cmd.Run(); err != nil { + return err } + cmd = exec.Command("git", "config", "user.email", "test@example.com") + cmd.Dir = dir + return cmd.Run() } From 2ae075965426ef8417823f031342447f491bad9a Mon Sep 17 00:00:00 2001 From: Jatin Date: Thu, 17 Jul 2025 11:55:15 +0530 Subject: [PATCH 3/3] Linter error fixed Signed-off-by: Jatin --- internal/git/git_test.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/git/git_test.go b/internal/git/git_test.go index d429c44..be5e25b 100644 --- a/internal/git/git_test.go +++ b/internal/git/git_test.go @@ -37,8 +37,12 @@ func setupTestRepo(t *testing.T) (string, func()) { } cleanup := func() { - os.Chdir(originalDir) - os.RemoveAll(tempDir) + if err := os.Chdir(originalDir); err != nil { + t.Fatalf("failed to change back to original directory: %v", err) + } + if err := os.RemoveAll(tempDir); err != nil { + t.Logf("failed to remove temp dir: %v", err) + } } return tempDir, cleanup @@ -69,11 +73,24 @@ func TestGitCommands_InitRepository(t *testing.T) { if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tempDir) + defer func() { + if err := os.RemoveAll(tempDir); err != nil { + t.Logf("failed to remove temp dir: %v", err) + } + }() - originalDir, _ := os.Getwd() - os.Chdir(tempDir) - defer os.Chdir(originalDir) + originalDir, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get current working directory: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("failed to change to temp dir: %v", err) + } + defer func() { + if err := os.Chdir(originalDir); err != nil { + t.Fatalf("failed to change back to original directory: %v", err) + } + }() g := NewGitCommands() repoPath := "test-repo"