From 65f4aad87caa86f1a8fbc76fc100f652bb0cc819 Mon Sep 17 00:00:00 2001 From: andersrexdb Date: Fri, 9 Aug 2024 12:40:25 +0300 Subject: [PATCH] Add command line autocomplete to the fs commands (#1622) ## Changes This PR adds autocomplete for cat, cp, ls, mkdir and rm. The new completer can do completion for any `Filer`. The command completion for the `sync` command can be moved to use this general completer as a follow-up. ## Tests - Tested manually against a workspace - Unit tests --- cmd/fs/cat.go | 3 + cmd/fs/cp.go | 5 + cmd/fs/helpers.go | 56 ++++++++++- cmd/fs/helpers_test.go | 89 ++++++++++++++++ cmd/fs/ls.go | 4 + cmd/fs/mkdir.go | 4 + cmd/fs/rm.go | 3 + internal/completer_test.go | 27 +++++ libs/filer/completer/completer.go | 95 ++++++++++++++++++ libs/filer/completer/completer_test.go | 104 +++++++++++++++++++ libs/filer/fake_filer.go | 134 +++++++++++++++++++++++++ libs/filer/fs_test.go | 132 ++---------------------- libs/fileset/glob_test.go | 5 +- 13 files changed, 532 insertions(+), 129 deletions(-) create mode 100644 internal/completer_test.go create mode 100644 libs/filer/completer/completer.go create mode 100644 libs/filer/completer/completer_test.go create mode 100644 libs/filer/fake_filer.go diff --git a/cmd/fs/cat.go b/cmd/fs/cat.go index 7a6f42cb..28df80d7 100644 --- a/cmd/fs/cat.go +++ b/cmd/fs/cat.go @@ -30,5 +30,8 @@ func newCatCommand() *cobra.Command { return cmdio.Render(ctx, r) } + v := newValidArgs() + cmd.ValidArgsFunction = v.Validate + return cmd } diff --git a/cmd/fs/cp.go b/cmd/fs/cp.go index 52feb890..6fb3e5e6 100644 --- a/cmd/fs/cp.go +++ b/cmd/fs/cp.go @@ -200,5 +200,10 @@ func newCpCommand() *cobra.Command { return c.cpFileToFile(sourcePath, targetPath) } + v := newValidArgs() + // The copy command has two paths that can be completed (SOURCE_PATH & TARGET_PATH) + v.pathArgCount = 2 + cmd.ValidArgsFunction = v.Validate + return cmd } diff --git a/cmd/fs/helpers.go b/cmd/fs/helpers.go index 43d65b5d..bda3239c 100644 --- a/cmd/fs/helpers.go +++ b/cmd/fs/helpers.go @@ -8,6 +8,8 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/filer" + "github.com/databricks/cli/libs/filer/completer" + "github.com/spf13/cobra" ) func filerForPath(ctx context.Context, fullPath string) (filer.Filer, string, error) { @@ -46,6 +48,58 @@ func filerForPath(ctx context.Context, fullPath string) (filer.Filer, string, er return f, path, err } +const dbfsPrefix string = "dbfs:" + func isDbfsPath(path string) bool { - return strings.HasPrefix(path, "dbfs:/") + return strings.HasPrefix(path, dbfsPrefix) +} + +type validArgs struct { + mustWorkspaceClientFunc func(cmd *cobra.Command, args []string) error + filerForPathFunc func(ctx context.Context, fullPath string) (filer.Filer, string, error) + pathArgCount int + onlyDirs bool +} + +func newValidArgs() *validArgs { + return &validArgs{ + mustWorkspaceClientFunc: root.MustWorkspaceClient, + filerForPathFunc: filerForPath, + pathArgCount: 1, + onlyDirs: false, + } +} + +func (v *validArgs) Validate(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + cmd.SetContext(root.SkipPrompt(cmd.Context())) + + if len(args) >= v.pathArgCount { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + err := v.mustWorkspaceClientFunc(cmd, args) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + filer, toCompletePath, err := v.filerForPathFunc(cmd.Context(), toComplete) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completer := completer.New(cmd.Context(), filer, v.onlyDirs) + + // Dbfs should have a prefix and always use the "/" separator + isDbfsPath := isDbfsPath(toComplete) + if isDbfsPath { + completer.SetPrefix(dbfsPrefix) + completer.SetIsLocalPath(false) + } + + completions, directive, err := completer.CompletePath(toCompletePath) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + return completions, directive } diff --git a/cmd/fs/helpers_test.go b/cmd/fs/helpers_test.go index d86bd46e..10b4aa16 100644 --- a/cmd/fs/helpers_test.go +++ b/cmd/fs/helpers_test.go @@ -3,9 +3,13 @@ package fs import ( "context" "runtime" + "strings" "testing" + "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/filer" + "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -60,3 +64,88 @@ func TestFilerForWindowsLocalPaths(t *testing.T) { testWindowsFilerForPath(t, ctx, `d:\abc`) testWindowsFilerForPath(t, ctx, `f:\abc\ef`) } + +func mockMustWorkspaceClientFunc(cmd *cobra.Command, args []string) error { + return nil +} + +func setupCommand(t *testing.T) (*cobra.Command, *mocks.MockWorkspaceClient) { + m := mocks.NewMockWorkspaceClient(t) + ctx := context.Background() + ctx = root.SetWorkspaceClient(ctx, m.WorkspaceClient) + + cmd := &cobra.Command{} + cmd.SetContext(ctx) + + return cmd, m +} + +func setupTest(t *testing.T) (*validArgs, *cobra.Command, *mocks.MockWorkspaceClient) { + cmd, m := setupCommand(t) + + fakeFilerForPath := func(ctx context.Context, fullPath string) (filer.Filer, string, error) { + fakeFiler := filer.NewFakeFiler(map[string]filer.FakeFileInfo{ + "dir": {FakeName: "root", FakeDir: true}, + "dir/dirA": {FakeDir: true}, + "dir/dirB": {FakeDir: true}, + "dir/fileA": {}, + }) + return fakeFiler, strings.TrimPrefix(fullPath, "dbfs:/"), nil + } + + v := newValidArgs() + v.filerForPathFunc = fakeFilerForPath + v.mustWorkspaceClientFunc = mockMustWorkspaceClientFunc + + return v, cmd, m +} + +func TestGetValidArgsFunctionDbfsCompletion(t *testing.T) { + v, cmd, _ := setupTest(t) + completions, directive := v.Validate(cmd, []string{}, "dbfs:/dir/") + assert.Equal(t, []string{"dbfs:/dir/dirA/", "dbfs:/dir/dirB/", "dbfs:/dir/fileA"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) +} + +func TestGetValidArgsFunctionLocalCompletion(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip() + } + + v, cmd, _ := setupTest(t) + completions, directive := v.Validate(cmd, []string{}, "dir/") + assert.Equal(t, []string{"dir/dirA/", "dir/dirB/", "dir/fileA", "dbfs:/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) +} + +func TestGetValidArgsFunctionLocalCompletionWindows(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip() + } + + v, cmd, _ := setupTest(t) + completions, directive := v.Validate(cmd, []string{}, "dir/") + assert.Equal(t, []string{"dir\\dirA\\", "dir\\dirB\\", "dir\\fileA", "dbfs:/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) +} + +func TestGetValidArgsFunctionCompletionOnlyDirs(t *testing.T) { + v, cmd, _ := setupTest(t) + v.onlyDirs = true + completions, directive := v.Validate(cmd, []string{}, "dbfs:/dir/") + assert.Equal(t, []string{"dbfs:/dir/dirA/", "dbfs:/dir/dirB/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) +} + +func TestGetValidArgsFunctionNotCompletedArgument(t *testing.T) { + cmd, _ := setupCommand(t) + + v := newValidArgs() + v.pathArgCount = 0 + v.mustWorkspaceClientFunc = mockMustWorkspaceClientFunc + + completions, directive := v.Validate(cmd, []string{}, "dbfs:/") + + assert.Nil(t, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive) +} diff --git a/cmd/fs/ls.go b/cmd/fs/ls.go index cec9b98b..d7eac513 100644 --- a/cmd/fs/ls.go +++ b/cmd/fs/ls.go @@ -89,5 +89,9 @@ func newLsCommand() *cobra.Command { `)) } + v := newValidArgs() + v.onlyDirs = true + cmd.ValidArgsFunction = v.Validate + return cmd } diff --git a/cmd/fs/mkdir.go b/cmd/fs/mkdir.go index 074a7543..5e9ac784 100644 --- a/cmd/fs/mkdir.go +++ b/cmd/fs/mkdir.go @@ -28,5 +28,9 @@ func newMkdirCommand() *cobra.Command { return f.Mkdir(ctx, path) } + v := newValidArgs() + v.onlyDirs = true + cmd.ValidArgsFunction = v.Validate + return cmd } diff --git a/cmd/fs/rm.go b/cmd/fs/rm.go index 5f2904e7..a133a830 100644 --- a/cmd/fs/rm.go +++ b/cmd/fs/rm.go @@ -32,5 +32,8 @@ func newRmCommand() *cobra.Command { return f.Delete(ctx, path) } + v := newValidArgs() + cmd.ValidArgsFunction = v.Validate + return cmd } diff --git a/internal/completer_test.go b/internal/completer_test.go new file mode 100644 index 00000000..0f7d2093 --- /dev/null +++ b/internal/completer_test.go @@ -0,0 +1,27 @@ +package internal + +import ( + "context" + "fmt" + "strings" + "testing" + + _ "github.com/databricks/cli/cmd/fs" + "github.com/databricks/cli/libs/filer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupCompletionFile(t *testing.T, f filer.Filer) { + err := f.Write(context.Background(), "dir1/file1.txt", strings.NewReader("abc"), filer.CreateParentDirectories) + require.NoError(t, err) +} + +func TestAccFsCompletion(t *testing.T) { + f, tmpDir := setupDbfsFiler(t) + setupCompletionFile(t, f) + + stdout, _ := RequireSuccessfulRun(t, "__complete", "fs", "ls", tmpDir) + expectedOutput := fmt.Sprintf("%s/dir1/\n:2\n", tmpDir) + assert.Equal(t, expectedOutput, stdout.String()) +} diff --git a/libs/filer/completer/completer.go b/libs/filer/completer/completer.go new file mode 100644 index 00000000..569286ca --- /dev/null +++ b/libs/filer/completer/completer.go @@ -0,0 +1,95 @@ +package completer + +import ( + "context" + "path" + "path/filepath" + "strings" + + "github.com/databricks/cli/libs/filer" + "github.com/spf13/cobra" +) + +type completer struct { + ctx context.Context + + // The filer to use for completing remote or local paths. + filer filer.Filer + + // CompletePath will only return directories when onlyDirs is true. + onlyDirs bool + + // Prefix to prepend to completions. + prefix string + + // Whether the path is local or remote. If the path is local we use the `filepath` + // package for path manipulation. Otherwise we use the `path` package. + isLocalPath bool +} + +// General completer that takes a filer to complete remote paths when TAB-ing through a path. +func New(ctx context.Context, filer filer.Filer, onlyDirs bool) *completer { + return &completer{ctx: ctx, filer: filer, onlyDirs: onlyDirs, prefix: "", isLocalPath: true} +} + +func (c *completer) SetPrefix(p string) { + c.prefix = p +} + +func (c *completer) SetIsLocalPath(i bool) { + c.isLocalPath = i +} + +func (c *completer) CompletePath(p string) ([]string, cobra.ShellCompDirective, error) { + trailingSeparator := "/" + joinFunc := path.Join + + // Use filepath functions if we are in a local path. + if c.isLocalPath { + joinFunc = filepath.Join + trailingSeparator = string(filepath.Separator) + } + + // If the user is TAB-ing their way through a path and the + // path ends in a trailing slash, we should list nested directories. + // If the path is incomplete, however, then we should list adjacent + // directories. + dirPath := p + if !strings.HasSuffix(p, trailingSeparator) { + dirPath = path.Dir(p) + } + + entries, err := c.filer.ReadDir(c.ctx, dirPath) + if err != nil { + return nil, cobra.ShellCompDirectiveError, err + } + + completions := []string{} + for _, entry := range entries { + if c.onlyDirs && !entry.IsDir() { + continue + } + + // Join directory path and entry name + completion := joinFunc(dirPath, entry.Name()) + + // Prepend prefix if it has been set + if c.prefix != "" { + completion = joinFunc(c.prefix, completion) + } + + // Add trailing separator for directories. + if entry.IsDir() { + completion += trailingSeparator + } + + completions = append(completions, completion) + } + + // If the path is local, we add the dbfs:/ prefix suggestion as an option + if c.isLocalPath { + completions = append(completions, "dbfs:/") + } + + return completions, cobra.ShellCompDirectiveNoSpace, err +} diff --git a/libs/filer/completer/completer_test.go b/libs/filer/completer/completer_test.go new file mode 100644 index 00000000..c533f0b6 --- /dev/null +++ b/libs/filer/completer/completer_test.go @@ -0,0 +1,104 @@ +package completer + +import ( + "context" + "runtime" + "testing" + + "github.com/databricks/cli/cmd/root" + "github.com/databricks/cli/libs/filer" + "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func setupCompleter(t *testing.T, onlyDirs bool) *completer { + ctx := context.Background() + // Needed to make type context.valueCtx for mockFilerForPath + ctx = root.SetWorkspaceClient(ctx, mocks.NewMockWorkspaceClient(t).WorkspaceClient) + + fakeFiler := filer.NewFakeFiler(map[string]filer.FakeFileInfo{ + "dir": {FakeName: "root", FakeDir: true}, + "dir/dirA": {FakeDir: true}, + "dir/dirB": {FakeDir: true}, + "dir/fileA": {}, + }) + + completer := New(ctx, fakeFiler, onlyDirs) + completer.SetIsLocalPath(false) + return completer +} + +func TestFilerCompleterSetsPrefix(t *testing.T) { + completer := setupCompleter(t, true) + completer.SetPrefix("dbfs:") + completions, directive, err := completer.CompletePath("dir/") + + assert.Equal(t, []string{"dbfs:/dir/dirA/", "dbfs:/dir/dirB/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterReturnsNestedDirs(t *testing.T) { + completer := setupCompleter(t, true) + completions, directive, err := completer.CompletePath("dir/") + + assert.Equal(t, []string{"dir/dirA/", "dir/dirB/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterReturnsAdjacentDirs(t *testing.T) { + completer := setupCompleter(t, true) + completions, directive, err := completer.CompletePath("dir/wrong_path") + + assert.Equal(t, []string{"dir/dirA/", "dir/dirB/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterReturnsNestedDirsAndFiles(t *testing.T) { + completer := setupCompleter(t, false) + completions, directive, err := completer.CompletePath("dir/") + + assert.Equal(t, []string{"dir/dirA/", "dir/dirB/", "dir/fileA"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterAddsDbfsPath(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip() + } + + completer := setupCompleter(t, true) + completer.SetIsLocalPath(true) + completions, directive, err := completer.CompletePath("dir/") + + assert.Equal(t, []string{"dir/dirA/", "dir/dirB/", "dbfs:/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterWindowsSeparator(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip() + } + + completer := setupCompleter(t, true) + completer.SetIsLocalPath(true) + completions, directive, err := completer.CompletePath("dir/") + + assert.Equal(t, []string{"dir\\dirA\\", "dir\\dirB\\", "dbfs:/"}, completions) + assert.Equal(t, cobra.ShellCompDirectiveNoSpace, directive) + assert.Nil(t, err) +} + +func TestFilerCompleterNoCompletions(t *testing.T) { + completer := setupCompleter(t, true) + completions, directive, err := completer.CompletePath("wrong_dir/wrong_dir") + + assert.Nil(t, completions) + assert.Equal(t, cobra.ShellCompDirectiveError, directive) + assert.Error(t, err) +} diff --git a/libs/filer/fake_filer.go b/libs/filer/fake_filer.go new file mode 100644 index 00000000..0e650ff6 --- /dev/null +++ b/libs/filer/fake_filer.go @@ -0,0 +1,134 @@ +package filer + +import ( + "context" + "fmt" + "io" + "io/fs" + "path" + "sort" + "strings" + "time" +) + +type FakeDirEntry struct { + FakeFileInfo +} + +func (entry FakeDirEntry) Type() fs.FileMode { + typ := fs.ModePerm + if entry.FakeDir { + typ |= fs.ModeDir + } + return typ +} + +func (entry FakeDirEntry) Info() (fs.FileInfo, error) { + return entry.FakeFileInfo, nil +} + +type FakeFileInfo struct { + FakeName string + FakeSize int64 + FakeDir bool + FakeMode fs.FileMode +} + +func (info FakeFileInfo) Name() string { + return info.FakeName +} + +func (info FakeFileInfo) Size() int64 { + return info.FakeSize +} + +func (info FakeFileInfo) Mode() fs.FileMode { + return info.FakeMode +} + +func (info FakeFileInfo) ModTime() time.Time { + return time.Now() +} + +func (info FakeFileInfo) IsDir() bool { + return info.FakeDir +} + +func (info FakeFileInfo) Sys() any { + return nil +} + +type FakeFiler struct { + entries map[string]FakeFileInfo +} + +func (f *FakeFiler) Write(ctx context.Context, p string, reader io.Reader, mode ...WriteMode) error { + return fmt.Errorf("not implemented") +} + +func (f *FakeFiler) Read(ctx context.Context, p string) (io.ReadCloser, error) { + _, ok := f.entries[p] + if !ok { + return nil, fs.ErrNotExist + } + + return io.NopCloser(strings.NewReader("foo")), nil +} + +func (f *FakeFiler) Delete(ctx context.Context, p string, mode ...DeleteMode) error { + return fmt.Errorf("not implemented") +} + +func (f *FakeFiler) ReadDir(ctx context.Context, p string) ([]fs.DirEntry, error) { + p = strings.TrimSuffix(p, "/") + entry, ok := f.entries[p] + if !ok { + return nil, NoSuchDirectoryError{p} + } + + if !entry.FakeDir { + return nil, fs.ErrInvalid + } + + // Find all entries contained in the specified directory `p`. + var out []fs.DirEntry + for k, v := range f.entries { + if k == p || path.Dir(k) != p { + continue + } + + out = append(out, FakeDirEntry{v}) + } + + sort.Slice(out, func(i, j int) bool { return out[i].Name() < out[j].Name() }) + return out, nil +} + +func (f *FakeFiler) Mkdir(ctx context.Context, path string) error { + return fmt.Errorf("not implemented") +} + +func (f *FakeFiler) Stat(ctx context.Context, path string) (fs.FileInfo, error) { + entry, ok := f.entries[path] + if !ok { + return nil, fs.ErrNotExist + } + + return entry, nil +} + +func NewFakeFiler(entries map[string]FakeFileInfo) *FakeFiler { + fakeFiler := &FakeFiler{ + entries: entries, + } + + for k, v := range fakeFiler.entries { + if v.FakeName != "" { + continue + } + v.FakeName = path.Base(k) + fakeFiler.entries[k] = v + } + + return fakeFiler +} diff --git a/libs/filer/fs_test.go b/libs/filer/fs_test.go index 03ed312b..a74c10f0 100644 --- a/libs/filer/fs_test.go +++ b/libs/filer/fs_test.go @@ -2,124 +2,14 @@ package filer import ( "context" - "fmt" "io" "io/fs" - "path" - "sort" - "strings" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -type fakeDirEntry struct { - fakeFileInfo -} - -func (entry fakeDirEntry) Type() fs.FileMode { - typ := fs.ModePerm - if entry.dir { - typ |= fs.ModeDir - } - return typ -} - -func (entry fakeDirEntry) Info() (fs.FileInfo, error) { - return entry.fakeFileInfo, nil -} - -type fakeFileInfo struct { - name string - size int64 - dir bool - mode fs.FileMode -} - -func (info fakeFileInfo) Name() string { - return info.name -} - -func (info fakeFileInfo) Size() int64 { - return info.size -} - -func (info fakeFileInfo) Mode() fs.FileMode { - return info.mode -} - -func (info fakeFileInfo) ModTime() time.Time { - return time.Now() -} - -func (info fakeFileInfo) IsDir() bool { - return info.dir -} - -func (info fakeFileInfo) Sys() any { - return nil -} - -type fakeFiler struct { - entries map[string]fakeFileInfo -} - -func (f *fakeFiler) Write(ctx context.Context, p string, reader io.Reader, mode ...WriteMode) error { - return fmt.Errorf("not implemented") -} - -func (f *fakeFiler) Read(ctx context.Context, p string) (io.ReadCloser, error) { - _, ok := f.entries[p] - if !ok { - return nil, fs.ErrNotExist - } - - return io.NopCloser(strings.NewReader("foo")), nil -} - -func (f *fakeFiler) Delete(ctx context.Context, p string, mode ...DeleteMode) error { - return fmt.Errorf("not implemented") -} - -func (f *fakeFiler) ReadDir(ctx context.Context, p string) ([]fs.DirEntry, error) { - entry, ok := f.entries[p] - if !ok { - return nil, fs.ErrNotExist - } - - if !entry.dir { - return nil, fs.ErrInvalid - } - - // Find all entries contained in the specified directory `p`. - var out []fs.DirEntry - for k, v := range f.entries { - if k == p || path.Dir(k) != p { - continue - } - - out = append(out, fakeDirEntry{v}) - } - - sort.Slice(out, func(i, j int) bool { return out[i].Name() < out[j].Name() }) - return out, nil -} - -func (f *fakeFiler) Mkdir(ctx context.Context, path string) error { - return fmt.Errorf("not implemented") -} - -func (f *fakeFiler) Stat(ctx context.Context, path string) (fs.FileInfo, error) { - entry, ok := f.entries[path] - if !ok { - return nil, fs.ErrNotExist - } - - return entry, nil -} - func TestFsImplementsFS(t *testing.T) { var _ fs.FS = &filerFS{} } @@ -145,22 +35,12 @@ func TestFsDirImplementsFsReadDirFile(t *testing.T) { } func fakeFS() fs.FS { - fakeFiler := &fakeFiler{ - entries: map[string]fakeFileInfo{ - ".": {name: "root", dir: true}, - "dirA": {dir: true}, - "dirB": {dir: true}, - "fileA": {size: 3}, - }, - } - - for k, v := range fakeFiler.entries { - if v.name != "" { - continue - } - v.name = path.Base(k) - fakeFiler.entries[k] = v - } + fakeFiler := NewFakeFiler(map[string]FakeFileInfo{ + ".": {FakeName: "root", FakeDir: true}, + "dirA": {FakeDir: true}, + "dirB": {FakeDir: true}, + "fileA": {FakeSize: 3}, + }) return NewFS(context.Background(), fakeFiler) } diff --git a/libs/fileset/glob_test.go b/libs/fileset/glob_test.go index 70b9c444..8418df73 100644 --- a/libs/fileset/glob_test.go +++ b/libs/fileset/glob_test.go @@ -20,7 +20,7 @@ func collectRelativePaths(files []File) []string { } func TestGlobFileset(t *testing.T) { - root := vfs.MustNew("../filer") + root := vfs.MustNew("./") entries, err := root.ReadDir(".") require.NoError(t, err) @@ -32,6 +32,7 @@ func TestGlobFileset(t *testing.T) { files, err := g.All() require.NoError(t, err) + // +1 as there's one folder in ../filer require.Equal(t, len(files), len(entries)) for _, f := range files { exists := slices.ContainsFunc(entries, func(de fs.DirEntry) bool { @@ -51,7 +52,7 @@ func TestGlobFileset(t *testing.T) { } func TestGlobFilesetWithRelativeRoot(t *testing.T) { - root := vfs.MustNew("../filer") + root := vfs.MustNew("../set") entries, err := root.ReadDir(".") require.NoError(t, err)