205 lines
5.3 KiB
Go
205 lines
5.3 KiB
Go
package git_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"superwork-tui/internal/git"
|
|
)
|
|
|
|
// initBareAndClone creates a bare origin repo and a local clone with one commit.
|
|
func initBareAndClone(t *testing.T) (origin, clone string) {
|
|
t.Helper()
|
|
origin = t.TempDir()
|
|
clone = t.TempDir()
|
|
|
|
run := func(dir string, 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(origin, "init", "--bare")
|
|
|
|
run(clone, "init")
|
|
run(clone, "config", "user.email", "test@test.com")
|
|
run(clone, "config", "user.name", "test")
|
|
run(clone, "remote", "add", "origin", origin)
|
|
|
|
f := filepath.Join(clone, "README")
|
|
if err := os.WriteFile(f, []byte("init"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run(clone, "add", ".")
|
|
run(clone, "commit", "-m", "init")
|
|
run(clone, "push", "origin", "HEAD:refs/heads/main")
|
|
run(clone, "push", "origin", "HEAD:refs/heads/build")
|
|
|
|
return origin, clone
|
|
}
|
|
|
|
func TestCheckBranchSync_SameBranch_Unavailable(t *testing.T) {
|
|
_, clone := initBareAndClone(t)
|
|
status, err := git.CheckBranchSync(context.Background(), git.BranchSyncOpts{
|
|
WorkspaceRoot: clone,
|
|
DevBranch: "main",
|
|
AutoBuildBranch: "main",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !status.Unavailable {
|
|
t.Error("same branch should be Unavailable")
|
|
}
|
|
}
|
|
|
|
func TestCheckBranchSync_NotGitRepo_Unavailable(t *testing.T) {
|
|
dir := t.TempDir()
|
|
status, err := git.CheckBranchSync(context.Background(), git.BranchSyncOpts{
|
|
WorkspaceRoot: dir,
|
|
DevBranch: "main",
|
|
AutoBuildBranch: "build",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !status.Unavailable {
|
|
t.Error("non-git-repo should be Unavailable")
|
|
}
|
|
}
|
|
|
|
func TestCheckBranchSync_ZeroBehind(t *testing.T) {
|
|
_, clone := initBareAndClone(t)
|
|
status, err := git.CheckBranchSync(context.Background(), git.BranchSyncOpts{
|
|
WorkspaceRoot: clone,
|
|
DevBranch: "main",
|
|
AutoBuildBranch: "build",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if status.Unavailable {
|
|
t.Errorf("should be available, reason: %s", status.Reason)
|
|
}
|
|
if status.Behind != 0 {
|
|
t.Errorf("Behind = %d, want 0 (branches are in sync)", status.Behind)
|
|
}
|
|
}
|
|
|
|
func TestCheckBranchSync_NonZeroBehind(t *testing.T) {
|
|
_, clone := initBareAndClone(t)
|
|
run := func(args ...string) {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = clone
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
}
|
|
// Advance main by two commits; build stays behind.
|
|
for i := 0; i < 2; i++ {
|
|
f := filepath.Join(clone, "f")
|
|
if err := os.WriteFile(f, []byte{byte(i)}, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run("add", ".")
|
|
run("commit", "-m", "advance")
|
|
}
|
|
run("push", "origin", "HEAD:refs/heads/main")
|
|
|
|
status, err := git.CheckBranchSync(context.Background(), git.BranchSyncOpts{
|
|
WorkspaceRoot: clone,
|
|
DevBranch: "main",
|
|
AutoBuildBranch: "build",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if status.Unavailable {
|
|
t.Fatalf("should be available, reason: %s", status.Reason)
|
|
}
|
|
if status.Behind != 2 {
|
|
t.Errorf("Behind = %d, want 2", status.Behind)
|
|
}
|
|
}
|
|
|
|
func TestRunBranchSync_AdvancesAutoBuild(t *testing.T) {
|
|
origin, clone := initBareAndClone(t)
|
|
run := func(args ...string) {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = clone
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
}
|
|
f := filepath.Join(clone, "f")
|
|
if err := os.WriteFile(f, []byte("x"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run("add", ".")
|
|
run("commit", "-m", "advance")
|
|
run("push", "origin", "HEAD:refs/heads/main")
|
|
|
|
revOf := func(ref string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", "-C", origin, "rev-parse", ref)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("rev-parse %s: %v\n%s", ref, err, out)
|
|
}
|
|
return string(out)
|
|
}
|
|
if revOf("main") == revOf("build") {
|
|
t.Fatal("precondition: build should be behind main")
|
|
}
|
|
|
|
if err := git.RunBranchSync(context.Background(), git.BranchSyncOpts{
|
|
WorkspaceRoot: clone,
|
|
DevBranch: "main",
|
|
AutoBuildBranch: "build",
|
|
}); err != nil {
|
|
t.Fatalf("RunBranchSync: %v", err)
|
|
}
|
|
|
|
if revOf("main") != revOf("build") {
|
|
t.Errorf("build should have advanced to main; main=%q build=%q", revOf("main"), revOf("build"))
|
|
}
|
|
}
|
|
|
|
func TestDeleteLocalBranch_NonExistent_NoOp(t *testing.T) {
|
|
root := initRepo(t)
|
|
err := git.DeleteLocalBranch(context.Background(), root, "nonexistent-branch")
|
|
if err != nil {
|
|
t.Errorf("DeleteLocalBranch on nonexistent branch should be no-op, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDeleteLocalBranch_Exists_Deletes(t *testing.T) {
|
|
root := initRepo(t)
|
|
run := func(args ...string) {
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = root
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
}
|
|
run("branch", "temp-branch")
|
|
err := git.DeleteLocalBranch(context.Background(), root, "temp-branch")
|
|
if err != nil {
|
|
t.Errorf("DeleteLocalBranch: %v", err)
|
|
}
|
|
// Verify branch is gone.
|
|
cmd := exec.Command("git", "-C", root, "show-ref", "--verify", "refs/heads/temp-branch")
|
|
if cmd.Run() == nil {
|
|
t.Error("branch should have been deleted")
|
|
}
|
|
}
|