From 46d8437bc2f568eda6389c8e917b595de1212d56 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 23 Mar 2026 13:21:13 +0100 Subject: [PATCH] Change test setup to allow nested directories --- .../git_commands/working_tree_test.go | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/pkg/commands/git_commands/working_tree_test.go b/pkg/commands/git_commands/working_tree_test.go index ce9d7971d..0b07a2c76 100644 --- a/pkg/commands/git_commands/working_tree_test.go +++ b/pkg/commands/git_commands/working_tree_test.go @@ -475,14 +475,17 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) { // testNode implements IFileNode for unit tests. type testNode struct { - files []*models.File // all leaf files under this node - path string - file *models.File // non-nil only for file nodes + children []*testNode + path string + file *models.File // non-nil only for file nodes } func (n *testNode) ForEachFile(cb func(*models.File) error) error { - for _, f := range n.files { - if err := cb(f); err != nil { + if n.file != nil { + return cb(n.file) + } + for _, child := range n.children { + if err := child.ForEachFile(cb); err != nil { return err } } @@ -490,8 +493,14 @@ func (n *testNode) ForEachFile(cb func(*models.File) error) error { } func (n *testNode) GetFilePathsMatching(test func(*models.File) bool) []string { - return lo.FilterMap(n.files, func(f *models.File, _ int) (string, bool) { - return f.Path, test(f) + if n.file != nil { + if test(n.file) { + return []string{n.path} + } + return nil + } + return lo.FlatMap(n.children, func(child *testNode, _ int) []string { + return child.GetFilePathsMatching(test) }) } @@ -510,10 +519,10 @@ func TestWorkingTreeDiscardAllDirChanges(t *testing.T) { { testName: "multiple regular tracked files batched into a single checkout call", nodes: []IFileNode{&testNode{ - files: []*models.File{ - {Path: "a.txt", Tracked: true}, - {Path: "b.txt", Tracked: true}, - {Path: "c.txt", Tracked: true}, + children: []*testNode{ + {path: "a.txt", file: &models.File{Path: "a.txt", Tracked: true}}, + {path: "b.txt", file: &models.File{Path: "b.txt", Tracked: true}}, + {path: "c.txt", file: &models.File{Path: "c.txt", Tracked: true}}, }, }}, runner: oscommands.NewFakeRunner(t). @@ -522,9 +531,9 @@ func TestWorkingTreeDiscardAllDirChanges(t *testing.T) { { testName: "staged files batched into a single reset then a single checkout", nodes: []IFileNode{&testNode{ - files: []*models.File{ - {Path: "a.txt", Tracked: true, HasStagedChanges: true}, - {Path: "b.txt", Tracked: true, HasStagedChanges: true}, + children: []*testNode{ + {path: "a.txt", file: &models.File{Path: "a.txt", Tracked: true, HasStagedChanges: true}}, + {path: "b.txt", file: &models.File{Path: "b.txt", Tracked: true, HasStagedChanges: true}}, }, }}, runner: oscommands.NewFakeRunner(t). @@ -534,9 +543,9 @@ func TestWorkingTreeDiscardAllDirChanges(t *testing.T) { { testName: "added files with no staged changes are removed from disk without any git call", nodes: []IFileNode{&testNode{ - files: []*models.File{ - {Path: "new1.txt", Added: true}, - {Path: "new2.txt", Added: true}, + children: []*testNode{ + {path: "new1.txt", file: &models.File{Path: "new1.txt", Added: true}}, + {path: "new2.txt", file: &models.File{Path: "new2.txt", Added: true}}, }, }}, runner: oscommands.NewFakeRunner(t), @@ -547,16 +556,16 @@ func TestWorkingTreeDiscardAllDirChanges(t *testing.T) { nodes: []IFileNode{ &testNode{ path: "dir1", - files: []*models.File{ - {Path: "dir1/a.txt", Tracked: true}, - {Path: "dir1/b.txt", Added: true}, + children: []*testNode{ + {path: "dir1/a.txt", file: &models.File{Path: "dir1/a.txt", Tracked: true}}, + {path: "dir1/b.txt", file: &models.File{Path: "dir1/b.txt", Added: true}}, }, }, &testNode{ path: "dir2", - files: []*models.File{ - {Path: "dir2/c.txt", Tracked: true}, - {Path: "dir2/d.txt", Added: true}, + children: []*testNode{ + {path: "dir2/c.txt", file: &models.File{Path: "dir2/c.txt", Tracked: true}}, + {path: "dir2/d.txt", file: &models.File{Path: "dir2/d.txt", Added: true}}, }, }, }, @@ -595,10 +604,10 @@ func TestWorkingTreeDiscardUnstagedDirChanges(t *testing.T) { testName: "directory node: removes untracked files and checks out tracked files by path, not by directory", nodes: []IFileNode{&testNode{ path: "dir", - files: []*models.File{ - {Path: "dir/tracked1.txt", Tracked: true}, - {Path: "dir/tracked2.txt", Tracked: true}, - {Path: "dir/new.txt", Tracked: false}, + children: []*testNode{ + {path: "dir/tracked1.txt", file: &models.File{Path: "dir/tracked1.txt", Tracked: true}}, + {path: "dir/tracked2.txt", file: &models.File{Path: "dir/tracked2.txt", Tracked: true}}, + {path: "dir/new.txt", file: &models.File{Path: "dir/new.txt", Tracked: false}}, }, }}, // Must checkout the individual files, not "dir" — otherwise a filter would be ignored. @@ -610,13 +619,13 @@ func TestWorkingTreeDiscardUnstagedDirChanges(t *testing.T) { testName: "directory node: staged-but-not-committed file (Tracked=false, HasStagedChanges=true) is left alone; purely untracked file is removed", nodes: []IFileNode{&testNode{ path: "dir", - files: []*models.File{ + children: []*testNode{ // Staged new files: not removed from disk, but checked out in // case they also have unstaged changes on top (AM status). - {Path: "dir/staged-new1.txt", Tracked: false, Added: true, HasStagedChanges: true}, - {Path: "dir/staged-new2.txt", Tracked: false, Added: true, HasStagedChanges: true}, + {path: "dir/staged-new1.txt", file: &models.File{Path: "dir/staged-new1.txt", Tracked: false, Added: true, HasStagedChanges: true}}, + {path: "dir/staged-new2.txt", file: &models.File{Path: "dir/staged-new2.txt", Tracked: false, Added: true, HasStagedChanges: true}}, // Purely untracked file: removed from disk, not checked out. - {Path: "dir/untracked.txt", Tracked: false, Added: true, HasStagedChanges: false}, + {path: "dir/untracked.txt", file: &models.File{Path: "dir/untracked.txt", Tracked: false, Added: true, HasStagedChanges: false}}, }, }}, runner: oscommands.NewFakeRunner(t). @@ -626,9 +635,8 @@ func TestWorkingTreeDiscardUnstagedDirChanges(t *testing.T) { { testName: "file node: added and unstaged file is removed from disk", nodes: []IFileNode{&testNode{ - path: "new.txt", - files: []*models.File{{Path: "new.txt", Added: true}}, - file: &models.File{Path: "new.txt", Added: true, HasStagedChanges: false}, + path: "new.txt", + file: &models.File{Path: "new.txt", Added: true, HasStagedChanges: false}, }}, runner: oscommands.NewFakeRunner(t), expectedRemovedFiles: []string{"new.txt"}, @@ -638,16 +646,16 @@ func TestWorkingTreeDiscardUnstagedDirChanges(t *testing.T) { nodes: []IFileNode{ &testNode{ path: "dir1", - files: []*models.File{ - {Path: "dir1/tracked.txt", Tracked: true}, - {Path: "dir1/untracked.txt", Tracked: false}, + children: []*testNode{ + {path: "dir1/tracked.txt", file: &models.File{Path: "dir1/tracked.txt", Tracked: true}}, + {path: "dir1/untracked.txt", file: &models.File{Path: "dir1/untracked.txt", Tracked: false}}, }, }, &testNode{ path: "dir2", - files: []*models.File{ - {Path: "dir2/tracked.txt", Tracked: true}, - {Path: "dir2/untracked.txt", Tracked: false}, + children: []*testNode{ + {path: "dir2/tracked.txt", file: &models.File{Path: "dir2/tracked.txt", Tracked: true}}, + {path: "dir2/untracked.txt", file: &models.File{Path: "dir2/untracked.txt", Tracked: false}}, }, }, },