mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-12 11:15:00 +02:00
468 lines
12 KiB
Go
468 lines
12 KiB
Go
|
package commands
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os/exec"
|
||
|
"runtime"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||
|
"github.com/jesseduffield/lazygit/pkg/test"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
)
|
||
|
|
||
|
// TestGitCommandCatFile tests emitting a file using commands, where commands vary by OS.
|
||
|
func TestGitCommandCatFile(t *testing.T) {
|
||
|
var osCmd string
|
||
|
switch os := runtime.GOOS; os {
|
||
|
case "windows":
|
||
|
osCmd = "type"
|
||
|
default:
|
||
|
osCmd = "cat"
|
||
|
}
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, osCmd, cmd)
|
||
|
assert.EqualValues(t, []string{"test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo", "-n", "test")
|
||
|
}
|
||
|
|
||
|
o, err := gitCmd.CatFile("test.txt")
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, "test", o)
|
||
|
}
|
||
|
|
||
|
// TestGitCommandStageFile is a function.
|
||
|
func TestGitCommandStageFile(t *testing.T) {
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"add", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}
|
||
|
|
||
|
assert.NoError(t, gitCmd.StageFile("test.txt"))
|
||
|
}
|
||
|
|
||
|
// TestGitCommandUnstageFile is a function.
|
||
|
func TestGitCommandUnstageFile(t *testing.T) {
|
||
|
type scenario struct {
|
||
|
testName string
|
||
|
command func(string, ...string) *exec.Cmd
|
||
|
test func(error)
|
||
|
reset bool
|
||
|
}
|
||
|
|
||
|
scenarios := []scenario{
|
||
|
{
|
||
|
"Remove an untracked file from staging",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"rm", "--cached", "--force", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
func(err error) {
|
||
|
assert.NoError(t, err)
|
||
|
},
|
||
|
false,
|
||
|
},
|
||
|
{
|
||
|
"Remove a tracked file from staging",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"reset", "HEAD", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
func(err error) {
|
||
|
assert.NoError(t, err)
|
||
|
},
|
||
|
true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, s := range scenarios {
|
||
|
t.Run(s.testName, func(t *testing.T) {
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
gitCmd.OSCommand.Command = s.command
|
||
|
s.test(gitCmd.UnStageFile([]string{"test.txt"}, s.reset))
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestGitCommandDiscardAllFileChanges is a function.
|
||
|
// these tests don't cover everything, in part because we already have an integration
|
||
|
// test which does cover everything. I don't want to unnecessarily assert on the 'how'
|
||
|
// when the 'what' is what matters
|
||
|
func TestGitCommandDiscardAllFileChanges(t *testing.T) {
|
||
|
type scenario struct {
|
||
|
testName string
|
||
|
command func() (func(string, ...string) *exec.Cmd, *[][]string)
|
||
|
test func(*[][]string, error)
|
||
|
file *models.File
|
||
|
removeFile func(string) error
|
||
|
}
|
||
|
|
||
|
scenarios := []scenario{
|
||
|
{
|
||
|
"An error occurred when resetting",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("test")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.Error(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 1)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"reset", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
HasStagedChanges: true,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"An error occurred when removing file",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("test")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.Error(t, err)
|
||
|
assert.EqualError(t, err, "an error occurred when removing file")
|
||
|
assert.Len(t, *cmdsCalled, 0)
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: false,
|
||
|
Added: true,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return fmt.Errorf("an error occurred when removing file")
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"An error occurred with checkout",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("test")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.Error(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 1)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"checkout", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: true,
|
||
|
HasStagedChanges: false,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"Checkout only",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.NoError(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 1)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"checkout", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: true,
|
||
|
HasStagedChanges: false,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"Reset and checkout staged changes",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.NoError(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 2)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"reset", "--", "test"},
|
||
|
{"checkout", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: true,
|
||
|
HasStagedChanges: true,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"Reset and checkout merge conflicts",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.NoError(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 2)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"reset", "--", "test"},
|
||
|
{"checkout", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: true,
|
||
|
HasMergeConflicts: true,
|
||
|
},
|
||
|
func(string) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"Reset and remove",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.NoError(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 1)
|
||
|
assert.EqualValues(t, *cmdsCalled, [][]string{
|
||
|
{"reset", "--", "test"},
|
||
|
})
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: false,
|
||
|
Added: true,
|
||
|
HasStagedChanges: true,
|
||
|
},
|
||
|
func(filename string) error {
|
||
|
assert.Equal(t, "test", filename)
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"Remove only",
|
||
|
func() (func(string, ...string) *exec.Cmd, *[][]string) {
|
||
|
cmdsCalled := [][]string{}
|
||
|
return func(cmd string, args ...string) *exec.Cmd {
|
||
|
cmdsCalled = append(cmdsCalled, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
}, &cmdsCalled
|
||
|
},
|
||
|
func(cmdsCalled *[][]string, err error) {
|
||
|
assert.NoError(t, err)
|
||
|
assert.Len(t, *cmdsCalled, 0)
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test",
|
||
|
Tracked: false,
|
||
|
Added: true,
|
||
|
HasStagedChanges: false,
|
||
|
},
|
||
|
func(filename string) error {
|
||
|
assert.Equal(t, "test", filename)
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, s := range scenarios {
|
||
|
t.Run(s.testName, func(t *testing.T) {
|
||
|
var cmdsCalled *[][]string
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
gitCmd.OSCommand.Command, cmdsCalled = s.command()
|
||
|
gitCmd.removeFile = s.removeFile
|
||
|
s.test(cmdsCalled, gitCmd.DiscardAllFileChanges(s.file))
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestGitCommandDiff is a function.
|
||
|
func TestGitCommandDiff(t *testing.T) {
|
||
|
type scenario struct {
|
||
|
testName string
|
||
|
command func(string, ...string) *exec.Cmd
|
||
|
file *models.File
|
||
|
plain bool
|
||
|
cached bool
|
||
|
}
|
||
|
|
||
|
scenarios := []scenario{
|
||
|
{
|
||
|
"Default case",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"diff", "--submodule", "--no-ext-diff", "--color=always", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test.txt",
|
||
|
HasStagedChanges: false,
|
||
|
Tracked: true,
|
||
|
},
|
||
|
false,
|
||
|
false,
|
||
|
},
|
||
|
{
|
||
|
"cached",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"diff", "--submodule", "--no-ext-diff", "--color=always", "--cached", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test.txt",
|
||
|
HasStagedChanges: false,
|
||
|
Tracked: true,
|
||
|
},
|
||
|
false,
|
||
|
true,
|
||
|
},
|
||
|
{
|
||
|
"plain",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"diff", "--submodule", "--no-ext-diff", "--color=never", "--", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test.txt",
|
||
|
HasStagedChanges: false,
|
||
|
Tracked: true,
|
||
|
},
|
||
|
true,
|
||
|
false,
|
||
|
},
|
||
|
{
|
||
|
"File not tracked and file has no staged changes",
|
||
|
func(cmd string, args ...string) *exec.Cmd {
|
||
|
assert.EqualValues(t, "git", cmd)
|
||
|
assert.EqualValues(t, []string{"diff", "--submodule", "--no-ext-diff", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, args)
|
||
|
|
||
|
return secureexec.Command("echo")
|
||
|
},
|
||
|
&models.File{
|
||
|
Name: "test.txt",
|
||
|
HasStagedChanges: false,
|
||
|
Tracked: false,
|
||
|
},
|
||
|
false,
|
||
|
false,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, s := range scenarios {
|
||
|
t.Run(s.testName, func(t *testing.T) {
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
gitCmd.OSCommand.Command = s.command
|
||
|
gitCmd.WorktreeFileDiff(s.file, s.plain, s.cached)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestGitCommandCheckoutFile is a function.
|
||
|
func TestGitCommandCheckoutFile(t *testing.T) {
|
||
|
type scenario struct {
|
||
|
testName string
|
||
|
commitSha string
|
||
|
fileName string
|
||
|
command func(string, ...string) *exec.Cmd
|
||
|
test func(error)
|
||
|
}
|
||
|
|
||
|
scenarios := []scenario{
|
||
|
{
|
||
|
"typical case",
|
||
|
"11af912",
|
||
|
"test999.txt",
|
||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||
|
{
|
||
|
Expect: "git checkout 11af912 test999.txt",
|
||
|
Replace: "echo",
|
||
|
},
|
||
|
}),
|
||
|
func(err error) {
|
||
|
assert.NoError(t, err)
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"returns error if there is one",
|
||
|
"11af912",
|
||
|
"test999.txt",
|
||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||
|
{
|
||
|
Expect: "git checkout 11af912 test999.txt",
|
||
|
Replace: "test",
|
||
|
},
|
||
|
}),
|
||
|
func(err error) {
|
||
|
assert.Error(t, err)
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
gitCmd := NewDummyGitCommand()
|
||
|
|
||
|
for _, s := range scenarios {
|
||
|
t.Run(s.testName, func(t *testing.T) {
|
||
|
gitCmd.OSCommand.Command = s.command
|
||
|
s.test(gitCmd.CheckoutFile(s.commitSha, s.fileName))
|
||
|
})
|
||
|
}
|
||
|
}
|