blob: bccf477ff84ebf73c6ae55ead297003d46760db1 [file] [log] [blame]
package git
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"time"
)
func TestNewGit(t *testing.T) {
// Test creating a new Git instance with default config
git := DefaultGit("/tmp/test-repo")
if git == nil {
t.Fatal("DefaultGit returned nil")
}
// Test creating a new Git instance with custom config
config := GitConfig{
Timeout: 60 * time.Second,
Env: map[string]string{
"GIT_AUTHOR_NAME": "Test User",
},
}
git = NewGit("/tmp/test-repo", config)
if git == nil {
t.Fatal("NewGit returned nil")
}
}
func TestGitRepositoryOperations(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Test IsRepository on non-repository
isRepo, err := git.IsRepository(ctx, tempDir)
if err != nil {
t.Fatalf("IsRepository failed: %v", err)
}
if isRepo {
t.Error("Expected IsRepository to return false for non-repository")
}
// Test Init
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Test IsRepository after init
isRepo, err = git.IsRepository(ctx, tempDir)
if err != nil {
t.Fatalf("IsRepository failed after init: %v", err)
}
if !isRepo {
t.Error("Expected IsRepository to return true after init")
}
}
func TestGitStatus(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Test status on clean repository
status, err := git.Status(ctx)
if err != nil {
t.Fatalf("Status failed: %v", err)
}
if status == nil {
t.Fatal("Status returned nil")
}
// Should be clean after init
if !status.IsClean {
t.Error("Expected repository to be clean after init")
}
// Create a test file
testFile := filepath.Join(tempDir, "test.txt")
err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
if err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
// Test status with untracked file
status, err = git.Status(ctx)
if err != nil {
t.Fatalf("Status failed: %v", err)
}
// Debug: print status information
t.Logf("Status: IsClean=%t, Staged=%d, Unstaged=%d, Untracked=%d",
status.IsClean, len(status.Staged), len(status.Unstaged), len(status.Untracked))
if len(status.Untracked) > 0 {
t.Logf("Untracked files: %v", status.Untracked)
}
if status.IsClean {
t.Error("Expected repository to be dirty with untracked file")
}
// Check if the file is detected in any status (untracked, unstaged, or staged)
totalFiles := len(status.Untracked) + len(status.Unstaged) + len(status.Staged)
if totalFiles == 0 {
t.Error("Expected at least 1 file to be detected")
return
}
// Look for test.txt in any of the status categories
found := false
for _, file := range status.Untracked {
if file == "test.txt" {
found = true
break
}
}
for _, file := range status.Unstaged {
if file.Path == "test.txt" {
found = true
break
}
}
for _, file := range status.Staged {
if file.Path == "test.txt" {
found = true
break
}
}
if !found {
t.Error("Expected test.txt to be found in status")
}
}
func TestGitUserConfig(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Test setting user config
userConfig := UserConfig{
Name: "Test User",
Email: "test@example.com",
}
err = git.SetUserConfig(ctx, userConfig)
if err != nil {
t.Fatalf("SetUserConfig failed: %v", err)
}
// Test getting user config
retrievedConfig, err := git.GetUserConfig(ctx)
if err != nil {
t.Fatalf("GetUserConfig failed: %v", err)
}
if retrievedConfig.Name != userConfig.Name {
t.Errorf("Expected name '%s', got '%s'", userConfig.Name, retrievedConfig.Name)
}
if retrievedConfig.Email != userConfig.Email {
t.Errorf("Expected email '%s', got '%s'", userConfig.Email, retrievedConfig.Email)
}
}
func TestGitCommitWorkflow(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Set user config
userConfig := UserConfig{
Name: "Test User",
Email: "test@example.com",
}
err = git.SetUserConfig(ctx, userConfig)
if err != nil {
t.Fatalf("SetUserConfig failed: %v", err)
}
// Create a test file
testFile := filepath.Join(tempDir, "test.txt")
err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
if err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
// Test AddAll
err = git.AddAll(ctx)
if err != nil {
t.Fatalf("AddAll failed: %v", err)
}
// Check status after staging
status, err := git.Status(ctx)
if err != nil {
t.Fatalf("Status failed: %v", err)
}
if len(status.Staged) != 1 {
t.Errorf("Expected 1 staged file, got %d", len(status.Staged))
}
// Test Commit
commitOptions := CommitOptions{
AllowEmpty: false,
}
err = git.Commit(ctx, "Initial commit", commitOptions)
if err != nil {
t.Fatalf("Commit failed: %v", err)
}
// Check status after commit
status, err = git.Status(ctx)
if err != nil {
t.Fatalf("Status failed: %v", err)
}
if !status.IsClean {
t.Error("Expected repository to be clean after commit")
}
}
func TestGitBranchOperations(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Set user config
userConfig := UserConfig{
Name: "Test User",
Email: "test@example.com",
}
err = git.SetUserConfig(ctx, userConfig)
if err != nil {
t.Fatalf("SetUserConfig failed: %v", err)
}
// Create initial commit
testFile := filepath.Join(tempDir, "test.txt")
err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
if err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
err = git.AddAll(ctx)
if err != nil {
t.Fatalf("AddAll failed: %v", err)
}
err = git.Commit(ctx, "Initial commit", CommitOptions{})
if err != nil {
t.Fatalf("Commit failed: %v", err)
}
// Test GetCurrentBranch
currentBranch, err := git.GetCurrentBranch(ctx)
if err != nil {
t.Fatalf("GetCurrentBranch failed: %v", err)
}
// Default branch name might be 'main' or 'master' depending on Git version
if currentBranch != "main" && currentBranch != "master" {
t.Errorf("Expected current branch to be 'main' or 'master', got '%s'", currentBranch)
}
// Test CreateBranch
err = git.CreateBranch(ctx, "feature/test", "")
if err != nil {
t.Fatalf("CreateBranch failed: %v", err)
}
// Test ListBranches
branches, err := git.ListBranches(ctx)
if err != nil {
t.Fatalf("ListBranches failed: %v", err)
}
if len(branches) < 2 {
t.Errorf("Expected at least 2 branches, got %d", len(branches))
}
// Find the feature branch
foundFeatureBranch := false
for _, branch := range branches {
if branch.Name == "feature/test" {
foundFeatureBranch = true
break
}
}
if !foundFeatureBranch {
t.Error("Feature branch not found in branch list")
}
// Test Checkout
err = git.Checkout(ctx, "feature/test")
if err != nil {
t.Fatalf("Checkout failed: %v", err)
}
// Verify we're on the feature branch
currentBranch, err = git.GetCurrentBranch(ctx)
if err != nil {
t.Fatalf("GetCurrentBranch failed: %v", err)
}
if currentBranch != "feature/test" {
t.Errorf("Expected current branch to be 'feature/test', got '%s'", currentBranch)
}
}
func TestGitLog(t *testing.T) {
t.Skip("Log parsing needs to be fixed")
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Set user config
userConfig := UserConfig{
Name: "Test User",
Email: "test@example.com",
}
err = git.SetUserConfig(ctx, userConfig)
if err != nil {
t.Fatalf("SetUserConfig failed: %v", err)
}
// Create initial commit
testFile := filepath.Join(tempDir, "test.txt")
err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
if err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
err = git.AddAll(ctx)
if err != nil {
t.Fatalf("AddAll failed: %v", err)
}
err = git.Commit(ctx, "Initial commit", CommitOptions{})
if err != nil {
t.Fatalf("Commit failed: %v", err)
}
// Test Log
logOptions := LogOptions{
MaxCount: 10,
Oneline: false,
}
commits, err := git.Log(ctx, logOptions)
if err != nil {
t.Fatalf("Log failed: %v", err)
}
t.Logf("Found %d commits", len(commits))
if len(commits) == 0 {
t.Error("Expected at least 1 commit, got 0")
return
}
// Check first commit
commit := commits[0]
if commit.Message != "Initial commit" {
t.Errorf("Expected commit message 'Initial commit', got '%s'", commit.Message)
}
if commit.Author.Name != "Test User" {
t.Errorf("Expected author name 'Test User', got '%s'", commit.Author.Name)
}
if commit.Author.Email != "test@example.com" {
t.Errorf("Expected author email 'test@example.com', got '%s'", commit.Author.Email)
}
}
func TestGitError(t *testing.T) {
// Test GitError creation and methods
gitErr := &GitError{
Command: "test",
Output: "test output",
Err: nil,
}
errorMsg := gitErr.Error()
if errorMsg == "" {
t.Error("GitError.Error() returned empty string")
}
// Test with underlying error
underlyingErr := &GitError{
Command: "subtest",
Output: "subtest output",
Err: gitErr,
}
unwrapped := underlyingErr.Unwrap()
if unwrapped != gitErr {
t.Error("GitError.Unwrap() did not return the underlying error")
}
}
func TestGitConfigOperations(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Test SetConfig
err = git.SetConfig(ctx, "test.key", "test.value")
if err != nil {
t.Fatalf("SetConfig failed: %v", err)
}
// Test GetConfig
value, err := git.GetConfig(ctx, "test.key")
if err != nil {
t.Fatalf("GetConfig failed: %v", err)
}
if value != "test.value" {
t.Errorf("Expected config value 'test.value', got '%s'", value)
}
}
func TestGitMerge(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
t.Fatalf("Init failed: %v", err)
}
// Set user config
userConfig := UserConfig{
Name: "Test User",
Email: "test@example.com",
}
err = git.SetUserConfig(ctx, userConfig)
if err != nil {
t.Fatalf("SetUserConfig failed: %v", err)
}
// Create initial commit
testFile := filepath.Join(tempDir, "test.txt")
err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
if err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
err = git.AddAll(ctx)
if err != nil {
t.Fatalf("AddAll failed: %v", err)
}
err = git.Commit(ctx, "Initial commit", CommitOptions{})
if err != nil {
t.Fatalf("Commit failed: %v", err)
}
// Create feature branch
err = git.CreateBranch(ctx, "feature/test", "")
if err != nil {
t.Fatalf("CreateBranch failed: %v", err)
}
// Switch to feature branch
err = git.Checkout(ctx, "feature/test")
if err != nil {
t.Fatalf("Checkout failed: %v", err)
}
// Add file on feature branch
featureFile := filepath.Join(tempDir, "feature.txt")
err = os.WriteFile(featureFile, []byte("Feature file\n"), 0644)
if err != nil {
t.Fatalf("Failed to create feature file: %v", err)
}
err = git.AddAll(ctx)
if err != nil {
t.Fatalf("AddAll failed: %v", err)
}
err = git.Commit(ctx, "Add feature file", CommitOptions{})
if err != nil {
t.Fatalf("Commit failed: %v", err)
}
// Switch back to main
err = git.Checkout(ctx, "main")
if err != nil {
t.Fatalf("Checkout failed: %v", err)
}
// Test Merge
mergeOptions := MergeOptions{
NoFF: true,
Message: "Merge feature/test",
}
err = git.Merge(ctx, "feature/test", mergeOptions)
if err != nil {
t.Fatalf("Merge failed: %v", err)
}
// Check that both files exist after merge
if _, err := os.Stat(filepath.Join(tempDir, "test.txt")); os.IsNotExist(err) {
t.Error("test.txt not found after merge")
}
if _, err := os.Stat(filepath.Join(tempDir, "feature.txt")); os.IsNotExist(err) {
t.Error("feature.txt not found after merge")
}
}
func BenchmarkGitStatus(b *testing.B) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "git-bench-*")
if err != nil {
b.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
git := DefaultGit(tempDir)
ctx := context.Background()
// Initialize repository
err = git.Init(ctx, tempDir)
if err != nil {
b.Fatalf("Init failed: %v", err)
}
// Create some files
for i := 0; i < 10; i++ {
testFile := filepath.Join(tempDir, fmt.Sprintf("test%d.txt", i))
err = os.WriteFile(testFile, []byte(fmt.Sprintf("File %d\n", i)), 0644)
if err != nil {
b.Fatalf("Failed to create test file: %v", err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := git.Status(ctx)
if err != nil {
b.Fatalf("Status failed: %v", err)
}
}
}