diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2022-01-08 14:00:36 +1100 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-01-09 14:09:53 +1100 |
commit | c9a0cc6b30dca6ff6c520268c10afff4e99a68e9 (patch) | |
tree | 74a03be28aafb5fba5c8391ca611aeb25ca89445 /pkg/commands/git_commands/working_tree_test.go | |
parent | 3621854dc79baf2064b00b561ebea0ecc8fcb5df (diff) |
refactor
Diffstat (limited to 'pkg/commands/git_commands/working_tree_test.go')
-rw-r--r-- | pkg/commands/git_commands/working_tree_test.go | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/pkg/commands/git_commands/working_tree_test.go b/pkg/commands/git_commands/working_tree_test.go new file mode 100644 index 000000000..a6d2345e9 --- /dev/null +++ b/pkg/commands/git_commands/working_tree_test.go @@ -0,0 +1,565 @@ +package git_commands + +import ( + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/stretchr/testify/assert" +) + +func TestWorkingTreeStageFile(t *testing.T) { + runner := oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "add", "--", "test.txt"}, "", nil) + + instance := buildWorkingTreeCommands(commonDeps{runner: runner}) + + assert.NoError(t, instance.StageFile("test.txt")) + runner.CheckForMissingCalls() +} + +func TestWorkingTreeUnstageFile(t *testing.T) { + type scenario struct { + testName string + reset bool + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + testName: "Remove an untracked file from staging", + reset: false, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "rm", "--cached", "--force", "--", "test.txt"}, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + { + testName: "Remove a tracked file from staging", + reset: true, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "reset", "HEAD", "--", "test.txt"}, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.UnStageFile([]string{"test.txt"}, s.reset)) + }) + } +} + +// 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 TestWorkingTreeDiscardAllFileChanges(t *testing.T) { + type scenario struct { + testName string + file *models.File + removeFile func(string) error + runner *oscommands.FakeCmdObjRunner + expectedError string + } + + scenarios := []scenario{ + { + testName: "An error occurred when resetting", + file: &models.File{ + Name: "test", + HasStagedChanges: true, + }, + removeFile: func(string) error { return nil }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "reset", "--", "test"}, "", errors.New("error")), + expectedError: "error", + }, + { + testName: "An error occurred when removing file", + file: &models.File{ + Name: "test", + Tracked: false, + Added: true, + }, + removeFile: func(string) error { + return fmt.Errorf("an error occurred when removing file") + }, + runner: oscommands.NewFakeRunner(t), + expectedError: "an error occurred when removing file", + }, + { + testName: "An error occurred with checkout", + file: &models.File{ + Name: "test", + Tracked: true, + HasStagedChanges: false, + }, + removeFile: func(string) error { return nil }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "checkout", "--", "test"}, "", errors.New("error")), + expectedError: "error", + }, + { + testName: "Checkout only", + file: &models.File{ + Name: "test", + Tracked: true, + HasStagedChanges: false, + }, + removeFile: func(string) error { return nil }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "checkout", "--", "test"}, "", nil), + expectedError: "", + }, + { + testName: "Reset and checkout staged changes", + file: &models.File{ + Name: "test", + Tracked: true, + HasStagedChanges: true, + }, + removeFile: func(string) error { return nil }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "reset", "--", "test"}, "", nil). + ExpectArgs([]string{"git", "checkout", "--", "test"}, "", nil), + expectedError: "", + }, + { + testName: "Reset and checkout merge conflicts", + file: &models.File{ + Name: "test", + Tracked: true, + HasMergeConflicts: true, + }, + removeFile: func(string) error { return nil }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "reset", "--", "test"}, "", nil). + ExpectArgs([]string{"git", "checkout", "--", "test"}, "", nil), + expectedError: "", + }, + { + testName: "Reset and remove", + file: &models.File{ + Name: "test", + Tracked: false, + Added: true, + HasStagedChanges: true, + }, + removeFile: func(filename string) error { + assert.Equal(t, "test", filename) + return nil + }, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "reset", "--", "test"}, "", nil), + expectedError: "", + }, + { + testName: "Remove only", + file: &models.File{ + Name: "test", + Tracked: false, + Added: true, + HasStagedChanges: false, + }, + removeFile: func(filename string) error { + assert.Equal(t, "test", filename) + return nil + }, + runner: oscommands.NewFakeRunner(t), + expectedError: "", + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: s.removeFile}) + err := instance.DiscardAllFileChanges(s.file) + + if s.expectedError == "" { + assert.Nil(t, err) + } else { + assert.Equal(t, s.expectedError, err.Error()) + } + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeDiff(t *testing.T) { + type scenario struct { + testName string + file *models.File + plain bool + cached bool + ignoreWhitespace bool + contextSize int + runner *oscommands.FakeCmdObjRunner + } + + const expectedResult = "pretend this is an actual git diff" + + scenarios := []scenario{ + { + testName: "Default case", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: true, + }, + plain: false, + cached: false, + ignoreWhitespace: false, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil), + }, + { + testName: "cached", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: true, + }, + plain: false, + cached: true, + ignoreWhitespace: false, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil), + }, + { + testName: "plain", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: true, + }, + plain: true, + cached: false, + ignoreWhitespace: false, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil), + }, + { + testName: "File not tracked and file has no staged changes", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: false, + }, + plain: false, + cached: false, + ignoreWhitespace: false, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil), + }, + { + testName: "Default case (ignore whitespace)", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: true, + }, + plain: false, + cached: false, + ignoreWhitespace: true, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil), + }, + { + testName: "Show diff with custom context size", + file: &models.File{ + Name: "test.txt", + HasStagedChanges: false, + Tracked: true, + }, + plain: false, + cached: false, + ignoreWhitespace: false, + contextSize: 17, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil), + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + userConfig := config.GetDefaultConfig() + userConfig.Git.DiffContextSize = s.contextSize + + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig}) + result := instance.WorktreeFileDiff(s.file, s.plain, s.cached, s.ignoreWhitespace) + assert.Equal(t, expectedResult, result) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeShowFileDiff(t *testing.T) { + type scenario struct { + testName string + from string + to string + reverse bool + plain bool + contextSize int + runner *oscommands.FakeCmdObjRunner + } + + const expectedResult = "pretend this is an actual git diff" + + scenarios := []scenario{ + { + testName: "Default case", + from: "1234567890", + to: "0987654321", + reverse: false, + plain: false, + contextSize: 3, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), + }, + { + testName: "Show diff with custom context size", + from: "1234567890", + to: "0987654321", + reverse: false, + plain: false, + contextSize: 123, + runner: oscommands.NewFakeRunner(t). + ExpectArgs([]string{"git", "diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + userConfig := config.GetDefaultConfig() + userConfig.Git.DiffContextSize = s.contextSize + + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig}) + + result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain) + assert.NoError(t, err) + assert.Equal(t, expectedResult, result) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeCheckoutFile(t *testing.T) { + type scenario struct { + testName string + commitSha string + fileName string + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + testName: "typical case", + commitSha: "11af912", + fileName: "test999.txt", + runner: oscommands.NewFakeRunner(t). + Expect(`git checkout 11af912 -- "test999.txt"`, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + { + testName: "returns error if there is one", + commitSha: "11af912", + fileName: "test999.txt", + runner: oscommands.NewFakeRunner(t). + Expect(`git checkout 11af912 -- "test999.txt"`, "", errors.New("error")), + test: func(err error) { + assert.Error(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + + s.test(instance.CheckoutFile(s.commitSha, s.fileName)) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeApplyPatch(t *testing.T) { + type scenario struct { + testName string + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) { + return func(cmdObj oscommands.ICmdObj) (string, error) { + re := regexp.MustCompile(regexStr) + matches := re.FindStringSubmatch(cmdObj.ToString()) + assert.Equal(t, 2, len(matches)) + + filename := matches[1] + + content, err := ioutil.ReadFile(filename) + assert.NoError(t, err) + + assert.Equal(t, "test", string(content)) + + return "", errToReturn + } + } + + scenarios := []scenario{ + { + testName: "valid case", + runner: oscommands.NewFakeRunner(t). + ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)), + test: func(err error) { + assert.NoError(t, err) + }, + }, + { + testName: "command returns error", + runner: oscommands.NewFakeRunner(t). + ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))), + test: func(err error) { + assert.Error(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.ApplyPatch("test", "cached")) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) { + type scenario struct { + testName string + file *models.File + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + testName: "valid case", + file: &models.File{Name: "test.txt"}, + runner: oscommands.NewFakeRunner(t). + Expect(`git checkout -- "test.txt"`, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.DiscardUnstagedFileChanges(s.file)) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) { + type scenario struct { + testName string + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + testName: "valid case", + runner: oscommands.NewFakeRunner(t). + Expect(`git checkout -- .`, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.DiscardAnyUnstagedFileChanges()) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) { + type scenario struct { + testName string + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + testName: "valid case", + runner: oscommands.NewFakeRunner(t). + Expect(`git clean -fd`, "", nil), + test: func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.RemoveUntrackedFiles()) + s.runner.CheckForMissingCalls() + }) + } +} + +func TestWorkingTreeResetHard(t *testing.T) { + type scenario struct { + testName string + ref string + runner *oscommands.FakeCmdObjRunner + test func(error) + } + + scenarios := []scenario{ + { + "valid case", + "HEAD", + oscommands.NewFakeRunner(t). + Expect(`git reset --hard "HEAD"`, "", nil), + func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + s.test(instance.ResetHard(s.ref)) + }) + } +} |