summaryrefslogtreecommitdiffstats
path: root/pkg/commands/git_commands/working_tree_test.go
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-08 14:00:36 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-01-09 14:09:53 +1100
commitc9a0cc6b30dca6ff6c520268c10afff4e99a68e9 (patch)
tree74a03be28aafb5fba5c8391ca611aeb25ca89445 /pkg/commands/git_commands/working_tree_test.go
parent3621854dc79baf2064b00b561ebea0ecc8fcb5df (diff)
refactor
Diffstat (limited to 'pkg/commands/git_commands/working_tree_test.go')
-rw-r--r--pkg/commands/git_commands/working_tree_test.go565
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))
+ })
+ }
+}