diff --git a/client.go b/client.go index 87b00741..2e5c0dfe 100644 --- a/client.go +++ b/client.go @@ -353,30 +353,35 @@ func (c *client) UploadFile(path string, contents io.ReadCloser) error { // ListFiles will return the filepaths of files within dir func (c *client) ListFiles(dir string) ([]string, error) { - c.mu.Lock() - defer c.mu.Unlock() - - conn, err := c.connection() - if err != nil { - return nil, err - } - - infos, err := conn.ReadDir(dir) - if err != nil { - return nil, fmt.Errorf("sftp: readdir %s: %w", dir, err) + pattern := filepath.Clean(strings.TrimPrefix(dir, string(os.PathSeparator))) + switch { + case pattern == ".": + if dir == "" { + pattern = "*" + } else { + pattern = filepath.Join(dir, "*") + } + case pattern != "": + pattern += "/*" } var filenames []string - for _, info := range infos { - if info.IsDir() { - continue + err := c.Walk(".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err } - - filenames = append(filenames, filepath.Join(dir, info.Name())) + if d.IsDir() { + return nil + } + matches, err := filepath.Match(pattern, path) + if matches && err == nil { + filenames = append(filenames, filepath.Join(dir, filepath.Base(path))) + } + return err + }) + if err != nil { + return nil, fmt.Errorf("listing %s failed: %w", dir, err) } - - c.logger.Logf("found %d files: %s", len(infos), strings.Join(filenames, ", ")) - return filenames, nil } @@ -454,7 +459,11 @@ func (c *client) Walk(dir string, fn fs.WalkDirFunc) error { } // Pass the callback to each file found for w.Step() { - err := fn(w.Path(), fs.FileInfoToDirEntry(w.Stat()), w.Err()) + info := w.Stat() + if info.IsDir() { + continue + } + err := fn(w.Path(), fs.FileInfoToDirEntry(info), w.Err()) if err != nil { if err == fs.SkipDir { w.SkipDir() diff --git a/client_test.go b/client_test.go index 8d6de2d8..f6a72095 100644 --- a/client_test.go +++ b/client_test.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "io/fs" - "sort" "testing" "time" @@ -33,22 +32,29 @@ func TestClientErr(t *testing.T) { require.Error(t, err) } -func TestClient_New(t *testing.T) { +func TestClient(t *testing.T) { if testing.Short() { t.Skip("-short flag was provided") } - t.Run("Open and Close", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - }) - require.NoError(t, err) + client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ + Hostname: "localhost:2222", + Username: "demo", + Password: "password", + Timeout: 5 * time.Second, + MaxConnections: 1, + PacketSize: 32000, + }) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, client.Close()) + }) + t.Run("Ping", func(t *testing.T) { + require.NoError(t, client.Ping()) + }) + + t.Run("Open and Close", func(t *testing.T) { file, err := client.Open("/outbox/one.txt") require.NoError(t, err) require.Greater(t, file.ModTime.Unix(), int64(1e7)) // valid unix time @@ -58,20 +64,9 @@ func TestClient_New(t *testing.T) { require.Equal(t, "one\n", string(content)) require.NoError(t, file.Close()) - require.NoError(t, client.Close()) }) t.Run("Open with Reader and consume file", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - }) - require.NoError(t, err) - file, err := client.Reader("/outbox/one.txt") require.NoError(t, err) require.Greater(t, file.ModTime.Unix(), int64(1e7)) // valid unix time @@ -81,42 +76,43 @@ func TestClient_New(t *testing.T) { require.Equal(t, "one\n", string(content)) require.NoError(t, file.Close()) - require.NoError(t, client.Close()) }) t.Run("ListFiles", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - }) + files, err := client.ListFiles("/") require.NoError(t, err) + require.Len(t, files, 0) - files, err := client.ListFiles("/outbox") + files, err = client.ListFiles("/outbox") require.NoError(t, err) + require.ElementsMatch(t, files, []string{"/outbox/one.txt", "/outbox/two.txt", "/outbox/empty.txt"}) - sort.Strings(files) - require.Equal(t, []string{"/outbox/one.txt", "/outbox/two.txt"}, files) + files, err = client.ListFiles("outbox") + require.NoError(t, err) + require.ElementsMatch(t, files, []string{"outbox/one.txt", "outbox/two.txt", "outbox/empty.txt"}) - require.NoError(t, client.Close()) + files, err = client.ListFiles("outbox/") + require.NoError(t, err) + require.ElementsMatch(t, files, []string{"outbox/one.txt", "outbox/two.txt", "outbox/empty.txt"}) }) - t.Run("Walk directory", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - }) + t.Run("ListFiles subdir", func(t *testing.T) { + files, err := client.ListFiles("/outbox/archive") + require.NoError(t, err) + require.ElementsMatch(t, files, []string{"/outbox/archive/empty2.txt", "/outbox/archive/three.txt"}) + + files, err = client.ListFiles("outbox/archive") + require.NoError(t, err) + require.ElementsMatch(t, files, []string{"outbox/archive/empty2.txt", "outbox/archive/three.txt"}) + + files, err = client.ListFiles("outbox/archive/") require.NoError(t, err) + require.ElementsMatch(t, files, []string{"outbox/archive/empty2.txt", "outbox/archive/three.txt"}) + }) + t.Run("Walk", func(t *testing.T) { var walkedFiles []string - err = client.Walk("/outbox", func(path string, info fs.DirEntry, err error) error { + err = client.Walk(".", func(path string, info fs.DirEntry, err error) error { if err != nil { return err } @@ -124,36 +120,29 @@ func TestClient_New(t *testing.T) { return nil }) require.NoError(t, err) - require.Contains(t, walkedFiles, "/outbox/one.txt", "/outbox/two.txt") - - require.NoError(t, client.Close()) + require.ElementsMatch(t, walkedFiles, []string{ + "outbox/one.txt", "outbox/two.txt", "outbox/empty.txt", + "outbox/archive/empty2.txt", "outbox/archive/three.txt", + }) }) - t.Run("Ping", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, + t.Run("Walk subdir", func(t *testing.T) { + var walkedFiles []string + err = client.Walk("/outbox", func(path string, info fs.DirEntry, err error) error { + if err != nil { + return err + } + walkedFiles = append(walkedFiles, path) + return nil }) require.NoError(t, err) - - require.NoError(t, client.Ping()) + require.ElementsMatch(t, walkedFiles, []string{ + "/outbox/one.txt", "/outbox/two.txt", "/outbox/empty.txt", + "/outbox/archive/empty2.txt", "/outbox/archive/three.txt", + }) }) t.Run("Upload and Delete", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - }) - require.NoError(t, err) - // upload file fileName := fmt.Sprintf("/upload/%d.txt", time.Now().Unix()) err = client.UploadFile(fileName, io.NopCloser(bytes.NewBufferString("random"))) @@ -175,21 +164,9 @@ func TestClient_New(t *testing.T) { require.EqualError(t, err, fmt.Sprintf("sftp: open %s: file does not exist", fileName)) require.NoError(t, file.Close()) - require.NoError(t, client.Close()) }) t.Run("Skip chmod after upload", func(t *testing.T) { - client, err := sftp.NewClient(log.NewNopLogger(), &sftp.ClientConfig{ - Hostname: "localhost:2222", - Username: "demo", - Password: "password", - Timeout: 5 * time.Second, - MaxConnections: 1, - PacketSize: 32000, - SkipChmodAfterUpload: true, - }) - require.NoError(t, err) - // upload file fileName := fmt.Sprintf("/upload/%d.txt", time.Now().Unix()) err = client.UploadFile(fileName, io.NopCloser(bytes.NewBufferString("random"))) diff --git a/testdata/outbox/archive/empty2.txt b/testdata/outbox/archive/empty2.txt new file mode 100644 index 00000000..e69de29b diff --git a/testdata/outbox/archive/three.txt b/testdata/outbox/archive/three.txt new file mode 100644 index 00000000..1269488f --- /dev/null +++ b/testdata/outbox/archive/three.txt @@ -0,0 +1 @@ +data diff --git a/testdata/outbox/empty.txt b/testdata/outbox/empty.txt new file mode 100644 index 00000000..e69de29b