Files
2026-06-23 05:02:15 +08:00

149 lines
3.7 KiB
Go

package git_test
import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"
"superwork-tui/internal/git"
)
// initTestRepo creates a temporary git repo with one commit on the default branch.
func initTestRepo(t *testing.T) string {
t.Helper()
dir := t.TempDir()
run := func(args ...string) {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
}
run("init")
run("config", "user.email", "test@test.com")
run("config", "user.name", "test")
f := filepath.Join(dir, "file.txt")
if err := os.WriteFile(f, []byte("hello"), 0o644); err != nil {
t.Fatal(err)
}
run("add", ".")
run("commit", "-m", "init")
return dir
}
func TestHasChanges_Clean(t *testing.T) {
dir := initTestRepo(t)
has, err := git.HasChanges(context.Background(), dir)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if has {
t.Error("expected no changes on clean repo")
}
}
func TestHasChanges_Dirty(t *testing.T) {
dir := initTestRepo(t)
f := filepath.Join(dir, "new.txt")
if err := os.WriteFile(f, []byte("dirty"), 0o644); err != nil {
t.Fatal(err)
}
has, err := git.HasChanges(context.Background(), dir)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !has {
t.Error("expected changes with untracked file")
}
}
func TestMergePreview_Clean(t *testing.T) {
dir := initTestRepo(t)
run := func(args ...string) {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
}
// Create a branch with a non-conflicting new file.
run("checkout", "-b", "feature")
f := filepath.Join(dir, "feature.txt")
if err := os.WriteFile(f, []byte("feature"), 0o644); err != nil {
t.Fatal(err)
}
run("add", ".")
run("commit", "-m", "feature commit")
// Return to the original branch via checkout -.
run("checkout", "-")
result, err := git.MergePreview(context.Background(), git.MergePreviewOpts{
WorkspaceRoot: dir,
Branch: "feature",
})
// A fast-forward-prevented merge that succeeds may leave MERGE_HEAD; abort to clean up.
defer func() {
cmd := exec.Command("git", "-C", dir, "merge", "--abort")
_ = cmd.Run()
}()
if err != nil {
t.Fatalf("expected clean merge, got error: %v (output: %s)", err, result.Output)
}
if !result.Clean {
t.Errorf("expected Clean=true, got false; conflicts: %v", result.Conflicts)
}
}
func TestMergePreview_Conflict(t *testing.T) {
dir := initTestRepo(t)
run := func(args ...string) {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
}
// Create a branch that modifies file.txt.
run("checkout", "-b", "conflict-branch")
f := filepath.Join(dir, "file.txt")
if err := os.WriteFile(f, []byte("branch change"), 0o644); err != nil {
t.Fatal(err)
}
run("add", ".")
run("commit", "-m", "branch change")
// Return to original branch and make a conflicting change to the same file.
run("checkout", "-")
if err := os.WriteFile(f, []byte("main change"), 0o644); err != nil {
t.Fatal(err)
}
run("add", ".")
run("commit", "-m", "main change")
result, _ := git.MergePreview(context.Background(), git.MergePreviewOpts{
WorkspaceRoot: dir,
Branch: "conflict-branch",
})
// Abort the in-progress merge to leave repo clean for test teardown.
defer func() {
cmd := exec.Command("git", "-C", dir, "merge", "--abort")
_ = cmd.Run()
}()
if result.Clean {
t.Error("expected Clean=false for conflicting merge")
}
if len(result.Conflicts) == 0 {
t.Errorf("expected at least one conflict entry, got none; output: %s", result.Output)
}
}