Skip to content

Commit 75e8da5

Browse files
Merge pull request #219 from AndreyVMarkelov/update-go-and-fix-ls-root
Update Go to 1.25, fix ls root listing (issue #209)
2 parents dfcfa13 + 12cd2d9 commit 75e8da5

6 files changed

Lines changed: 257 additions & 54 deletions

File tree

cmd/ls.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,18 @@ func getFileMetadata(c files.Client, path string) (files.IsMetadata, error) {
4343

4444
// Invoked by search.go
4545
func printFolderMetadata(w io.Writer, e *files.FolderMetadata, longFormat bool) {
46-
fmt.Fprintf(w, formatFolderMetadata(e, longFormat))
46+
fmt.Fprint(w, formatFolderMetadata(e, longFormat))
4747
}
4848

4949
// Invoked by search.go and revs.go
5050
func printFileMetadata(w io.Writer, e *files.FileMetadata, longFormat bool) {
51-
fmt.Fprintf(w, formatFileMetadata(e, longFormat))
51+
fmt.Fprint(w, formatFileMetadata(e, longFormat))
5252
}
5353

5454
func formatFolderMetadata(e *files.FolderMetadata, longFormat bool) string {
5555
text := fmt.Sprintf("%s\t", e.PathDisplay)
5656
if longFormat {
57-
text = fmt.Sprintf("-\t-\t-\t") + text
57+
text = "-\t-\t-\t" + text
5858
}
5959
return text
6060
}
@@ -70,7 +70,7 @@ func formatFileMetadata(e *files.FileMetadata, longFormat bool) string {
7070
func formatDeletedMetadata(e *files.DeletedMetadata, longFormat bool) string {
7171
text := fmt.Sprintf("%s\t", e.PathDisplay)
7272
if longFormat {
73-
text = fmt.Sprintf("-\t-\t-\t") + text
73+
text = "-\t-\t-\t" + text
7474
}
7575
return text
7676
}
@@ -115,25 +115,25 @@ func ls(cmd *cobra.Command, args []string) (err error) {
115115

116116
dbx := files.New(config)
117117

118-
// check if given object exists
119-
var metaRes files.IsMetadata
120-
metaRes, err = getFileMetadata(dbx, path)
121-
if err != nil {
122-
return err
123-
}
124-
125118
if long {
126119
fmt.Fprint(w, "Revision\tSize\tLast modified\tPath\n")
127120
}
128121

129-
// handle case when path to file was given
130-
switch f := metaRes.(type) {
131-
case *files.FileMetadata:
132-
if !onlyDeleted {
133-
printItem(formatFileMetadata(f, long))
134-
err = w.Flush()
122+
if path != "" {
123+
var metaRes files.IsMetadata
124+
metaRes, err = getFileMetadata(dbx, path)
125+
if err != nil {
135126
return err
136127
}
128+
129+
switch f := metaRes.(type) {
130+
case *files.FileMetadata:
131+
if !onlyDeleted {
132+
printItem(formatFileMetadata(f, long))
133+
err = w.Flush()
134+
return err
135+
}
136+
}
137137
}
138138

139139
res, err := dbx.ListFolder(arg)
@@ -146,7 +146,7 @@ func ls(cmd *cobra.Command, args []string) (err error) {
146146
// get_metadata request for the same path and using that response instead.
147147
if listRevisionError.EndpointError.Path.Tag == files.LookupErrorNotFolder {
148148
var metaRes files.IsMetadata
149-
metaRes, err = getFileMetadata(dbx, path)
149+
metaRes, _ = getFileMetadata(dbx, path)
150150
entries = []files.IsMetadata{metaRes}
151151
} else {
152152
// Return if there's an error other than "not_folder" or if the follow-up

cmd/ls_test.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox"
8+
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/files"
9+
)
10+
11+
func TestValidatePath(t *testing.T) {
12+
tests := []struct {
13+
input string
14+
want string
15+
}{
16+
{"/", ""},
17+
{"/foo", "/foo"},
18+
{"foo", "/foo"},
19+
{"/foo/", "/foo"},
20+
{"/foo/bar", "/foo/bar"},
21+
{"foo/bar/", "/foo/bar"},
22+
}
23+
24+
for _, tt := range tests {
25+
got, err := validatePath(tt.input)
26+
if err != nil {
27+
t.Errorf("validatePath(%q) returned error: %v", tt.input, err)
28+
continue
29+
}
30+
if got != tt.want {
31+
t.Errorf("validatePath(%q) = %q, want %q", tt.input, got, tt.want)
32+
}
33+
}
34+
}
35+
36+
func TestFormatFolderMetadata(t *testing.T) {
37+
meta := &files.FolderMetadata{
38+
Metadata: files.Metadata{
39+
PathDisplay: "/Documents",
40+
},
41+
}
42+
43+
got := formatFolderMetadata(meta, false)
44+
want := "/Documents\t"
45+
if got != want {
46+
t.Errorf("formatFolderMetadata(short) = %q, want %q", got, want)
47+
}
48+
49+
got = formatFolderMetadata(meta, true)
50+
want = "-\t-\t-\t/Documents\t"
51+
if got != want {
52+
t.Errorf("formatFolderMetadata(long) = %q, want %q", got, want)
53+
}
54+
}
55+
56+
func TestFormatFileMetadata(t *testing.T) {
57+
meta := &files.FileMetadata{
58+
Metadata: files.Metadata{
59+
PathDisplay: "/test.txt",
60+
},
61+
Rev: "abc123",
62+
Size: 1024,
63+
}
64+
65+
got := formatFileMetadata(meta, false)
66+
want := "/test.txt\t"
67+
if got != want {
68+
t.Errorf("formatFileMetadata(short) = %q, want %q", got, want)
69+
}
70+
71+
got = formatFileMetadata(meta, true)
72+
if got == "" {
73+
t.Error("formatFileMetadata(long) returned empty string")
74+
}
75+
if len(got) <= len("/test.txt\t") {
76+
t.Errorf("formatFileMetadata(long) should include rev/size/time, got %q", got)
77+
}
78+
}
79+
80+
func TestFormatDeletedMetadata(t *testing.T) {
81+
meta := &files.DeletedMetadata{
82+
Metadata: files.Metadata{
83+
PathDisplay: "/removed.txt",
84+
},
85+
}
86+
87+
got := formatDeletedMetadata(meta, false)
88+
want := "/removed.txt\t"
89+
if got != want {
90+
t.Errorf("formatDeletedMetadata(short) = %q, want %q", got, want)
91+
}
92+
93+
got = formatDeletedMetadata(meta, true)
94+
want = "-\t-\t-\t/removed.txt\t"
95+
if got != want {
96+
t.Errorf("formatDeletedMetadata(long) = %q, want %q", got, want)
97+
}
98+
}
99+
100+
func TestSetPathDisplayAsDeleted(t *testing.T) {
101+
file := &files.FileMetadata{
102+
Metadata: files.Metadata{PathDisplay: "/file.txt"},
103+
}
104+
SetPathDisplayAsDeleted(file)
105+
if file.PathDisplay != "<</file.txt>>" {
106+
t.Errorf("file PathDisplay = %q, want %q", file.PathDisplay, "<</file.txt>>")
107+
}
108+
109+
folder := &files.FolderMetadata{
110+
Metadata: files.Metadata{PathDisplay: "/folder"},
111+
}
112+
SetPathDisplayAsDeleted(folder)
113+
if folder.PathDisplay != "<</folder>>" {
114+
t.Errorf("folder PathDisplay = %q, want %q", folder.PathDisplay, "<</folder>>")
115+
}
116+
117+
deleted := &files.DeletedMetadata{
118+
Metadata: files.Metadata{PathDisplay: "/gone"},
119+
}
120+
SetPathDisplayAsDeleted(deleted)
121+
if deleted.PathDisplay != "<</gone>>" {
122+
t.Errorf("deleted PathDisplay = %q, want %q", deleted.PathDisplay, "<</gone>>")
123+
}
124+
}
125+
126+
func TestFormatFileMetadataLongIncludesFields(t *testing.T) {
127+
ts := time.Date(2025, 1, 15, 10, 30, 0, 0, time.UTC)
128+
meta := &files.FileMetadata{
129+
Metadata: files.Metadata{
130+
PathDisplay: "/data.csv",
131+
},
132+
Rev: "rev999",
133+
Size: 2048,
134+
ServerModified: ts,
135+
}
136+
137+
got := formatFileMetadata(meta, true)
138+
if !contains(got, "rev999") {
139+
t.Errorf("long format should contain rev, got %q", got)
140+
}
141+
if !contains(got, "2.0 KiB") {
142+
t.Errorf("long format should contain human size, got %q", got)
143+
}
144+
if !contains(got, "/data.csv") {
145+
t.Errorf("long format should contain path, got %q", got)
146+
}
147+
}
148+
149+
func TestValidatePathRootBecomesEmpty(t *testing.T) {
150+
got, err := validatePath("/")
151+
if err != nil {
152+
t.Fatalf("validatePath('/') error: %v", err)
153+
}
154+
if got != "" {
155+
t.Errorf("validatePath('/') = %q, want empty string for root", got)
156+
}
157+
}
158+
159+
func TestGetFileMetadataNotCalledForRoot(t *testing.T) {
160+
// This test verifies the ls function logic:
161+
// when path is "" (root), getFileMetadata should not be called.
162+
// We test this indirectly by confirming that NewGetMetadataArg
163+
// with empty string would be invalid for the API.
164+
arg := files.NewGetMetadataArg("")
165+
if arg.Path != "" {
166+
t.Errorf("NewGetMetadataArg('') path = %q, want empty", arg.Path)
167+
}
168+
}
169+
170+
func TestMakeRelocationArg(t *testing.T) {
171+
arg, err := makeRelocationArg("src", "dst")
172+
if err != nil {
173+
t.Fatalf("makeRelocationArg error: %v", err)
174+
}
175+
if arg.FromPath != "/src" {
176+
t.Errorf("FromPath = %q, want %q", arg.FromPath, "/src")
177+
}
178+
if arg.ToPath != "/dst" {
179+
t.Errorf("ToPath = %q, want %q", arg.ToPath, "/dst")
180+
}
181+
}
182+
183+
func contains(s, substr string) bool {
184+
return len(s) >= len(substr) && (s == substr || len(s) > 0 && stringContains(s, substr))
185+
}
186+
187+
func stringContains(s, substr string) bool {
188+
for i := 0; i <= len(s)-len(substr); i++ {
189+
if s[i:i+len(substr)] == substr {
190+
return true
191+
}
192+
}
193+
return false
194+
}
195+
196+
// Verify that dropbox config can be constructed without panicking
197+
func TestDropboxConfigConstruction(t *testing.T) {
198+
cfg := dropbox.Config{
199+
Token: "test-token",
200+
LogLevel: dropbox.LogOff,
201+
}
202+
if cfg.Token != "test-token" {
203+
t.Error("config token not set")
204+
}
205+
}

cmd/put.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"errors"
2020
"fmt"
2121
"io"
22-
"io/ioutil"
2322
"log"
2423
"os"
2524
"path"
@@ -96,7 +95,7 @@ func uploadChunked(dbx files.Client, r io.Reader, commitInfo *files.CommitInfo,
9695

9796
written := int64(0)
9897
for written < sizeTotal {
99-
data, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: chunkSize})
98+
data, err := io.ReadAll(&io.LimitedReader{R: r, N: chunkSize})
10099
if err != nil {
101100
return err
102101
}
@@ -208,7 +207,8 @@ func put(cmd *cobra.Command, args []string) (err error) {
208207
return uploadChunked(dbx, progressbar, commitInfo, contentsInfo.Size(), workers, chunkSize, debug)
209208
}
210209

211-
if _, err = dbx.Upload(commitInfo, progressbar); err != nil {
210+
uploadArg := &files.UploadArg{CommitInfo: *commitInfo}
211+
if _, err = dbx.Upload(uploadArg, progressbar); err != nil {
212212
return
213213
}
214214

cmd/root.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ package cmd
1717
import (
1818
"encoding/json"
1919
"fmt"
20-
"io/ioutil"
2120
"os"
2221
"path"
2322
"path/filepath"
2423
"strings"
2524

25+
"context"
26+
2627
"golang.org/x/oauth2"
2728

2829
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox"
2930
"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/files"
3031
"github.com/mitchellh/go-homedir"
3132
"github.com/spf13/cobra"
32-
"golang.org/x/net/context"
3333
)
3434

3535
const (
@@ -108,7 +108,7 @@ func makeRelocationArg(s string, d string) (arg *files.RelocationArg, err error)
108108
}
109109

110110
func readTokens(filePath string) (TokenMap, error) {
111-
b, err := ioutil.ReadFile(filePath)
111+
b, err := os.ReadFile(filePath)
112112
if err != nil {
113113
return nil, err
114114
}
@@ -136,7 +136,7 @@ func writeTokens(filePath string, tokens TokenMap) {
136136
if err != nil {
137137
return
138138
}
139-
if err = ioutil.WriteFile(filePath, b, 0600); err != nil {
139+
if err = os.WriteFile(filePath, b, 0600); err != nil {
140140
return
141141
}
142142
}
@@ -234,7 +234,7 @@ func init() {
234234
RootCmd.PersistentFlags().String("as-member", "", "Member ID to perform action as")
235235
// This flag should only be used for testing. Marked hidden so it doesn't clutter usage etc.
236236
RootCmd.PersistentFlags().String("domain", "", "Override default Dropbox domain, useful for testing")
237-
RootCmd.PersistentFlags().MarkHidden("domain")
237+
_ = RootCmd.PersistentFlags().MarkHidden("domain")
238238

239239
personalAppKey = getEnv("DROPBOX_PERSONAL_APP_KEY", personalAppKey)
240240
personalAppSecret = getEnv("DROPBOX_PERSONAL_APP_SECRET", personalAppSecret)

go.mod

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
module github.com/dropbox/dbxcli
22

3-
go 1.11
3+
go 1.25.0
44

55
require (
6-
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.1
7-
github.com/dustin/go-humanize v1.0.0
8-
github.com/inconshreveable/mousetrap v1.0.0 // indirect
6+
github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5
7+
github.com/dustin/go-humanize v1.0.1
98
github.com/mitchellh/go-homedir v1.1.0
109
github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e
11-
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85
12-
github.com/spf13/pflag v1.0.3 // indirect
13-
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
14-
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
10+
github.com/spf13/cobra v1.10.2
11+
golang.org/x/oauth2 v0.36.0
12+
)
13+
14+
require (
15+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
16+
github.com/spf13/pflag v1.0.10 // indirect
1517
)

0 commit comments

Comments
 (0)