Files
github__gh-stack/cmd/init_test.go
2026-03-24 16:44:35 -04:00

204 lines
6.3 KiB
Go

package cmd
import (
"io"
"os"
"testing"
"github.com/github/gh-stack/internal/config"
"github.com/github/gh-stack/internal/git"
"github.com/github/gh-stack/internal/stack"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// collectOutput closes the write ends of the test config pipes and returns
// the captured stderr content. Shared across cmd test files.
func collectOutput(cfg *config.Config, outR, errR *os.File) string {
cfg.Out.Close()
cfg.Err.Close()
stderr, _ := io.ReadAll(errR)
outR.Close()
errR.Close()
return string(stderr)
}
func TestInit_CreatesStackWithCorrectTrunk(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"myBranch"}})
output := collectOutput(cfg, outR, errR)
require.NotContains(t, output, "\u2717", "unexpected error in output")
sf, err := stack.Load(gitDir)
require.NoError(t, err, "loading stack")
require.Len(t, sf.Stacks, 1)
s := sf.Stacks[0]
assert.Equal(t, "main", s.Trunk.Branch)
names := s.BranchNames()
require.Len(t, names, 1)
assert.Equal(t, "myBranch", names[0])
}
func TestInit_CustomTrunk(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"myBranch"}, base: "develop"})
output := collectOutput(cfg, outR, errR)
require.NotContains(t, output, "\u2717", "unexpected error")
sf, err := stack.Load(gitDir)
require.NoError(t, err, "loading stack")
assert.Equal(t, "develop", sf.Stacks[0].Trunk.Branch)
}
func TestInit_AdoptExistingBranches(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
BranchExistsFn: func(string) bool { return true },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{
branches: []string{"b1", "b2", "b3"},
adopt: true,
})
output := collectOutput(cfg, outR, errR)
require.NotContains(t, output, "\u2717", "unexpected error")
sf, err := stack.Load(gitDir)
require.NoError(t, err, "loading stack")
names := sf.Stacks[0].BranchNames()
assert.Equal(t, []string{"b1", "b2", "b3"}, names)
}
func TestInit_PrefixStoredInStack(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"myBranch"}, prefix: "feat"})
collectOutput(cfg, outR, errR)
sf, err := stack.Load(gitDir)
require.NoError(t, err, "loading stack")
assert.Equal(t, "feat", sf.Stacks[0].Prefix)
}
func TestInit_RerereAlreadyEnabled(t *testing.T) {
gitDir := t.TempDir()
enableRerereCalled := false
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
IsRerereEnabledFn: func() (bool, error) { return true, nil },
EnableRerereFn: func() error {
enableRerereCalled = true
return nil
},
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"b1"}})
collectOutput(cfg, outR, errR)
assert.False(t, enableRerereCalled, "EnableRerere should not be called when rerere is already enabled")
}
func TestInit_RefuseIfBranchAlreadyInStack(t *testing.T) {
gitDir := t.TempDir()
// Pre-create stack file with "feature-1" as a non-trunk branch
sf := &stack.StackFile{
SchemaVersion: 1,
Stacks: []stack.Stack{{
Trunk: stack.BranchRef{Branch: "main"},
Branches: []stack.BranchRef{{Branch: "feature-1"}},
}},
}
require.NoError(t, stack.Save(gitDir, sf), "saving seed stack")
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "feature-1", nil },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"newBranch"}})
output := collectOutput(cfg, outR, errR)
assert.Contains(t, output, "already part of a stack")
}
func TestInit_AdoptNonexistentBranch(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
BranchExistsFn: func(string) bool { return false },
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"nonexistent"}, adopt: true})
output := collectOutput(cfg, outR, errR)
assert.Contains(t, output, "does not exist")
}
func TestInit_MultipleBranches_CreatesAll(t *testing.T) {
gitDir := t.TempDir()
var created []string
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
DefaultBranchFn: func() (string, error) { return "main", nil },
CurrentBranchFn: func() (string, error) { return "main", nil },
CreateBranchFn: func(name, base string) error {
created = append(created, name)
return nil
},
})
defer restore()
cfg, outR, errR := config.NewTestConfig()
runInit(cfg, &initOptions{branches: []string{"b1", "b2", "b3"}})
output := collectOutput(cfg, outR, errR)
require.NotContains(t, output, "\u2717", "unexpected error")
sf, err := stack.Load(gitDir)
require.NoError(t, err, "loading stack")
names := sf.Stacks[0].BranchNames()
assert.Equal(t, []string{"b1", "b2", "b3"}, names)
}