github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/operations/rc_test.go (about) 1 package operations_test 2 3 import ( 4 "context" 5 "net/http" 6 "net/http/httptest" 7 "testing" 8 "time" 9 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/cache" 12 "github.com/rclone/rclone/fs/operations" 13 "github.com/rclone/rclone/fs/rc" 14 "github.com/rclone/rclone/fstest" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func rcNewRun(t *testing.T, method string) (*fstest.Run, *rc.Call) { 20 if *fstest.RemoteName != "" { 21 t.Skip("Skipping test on non local remote") 22 } 23 r := fstest.NewRun(t) 24 call := rc.Calls.Get(method) 25 assert.NotNil(t, call) 26 cache.Put(r.LocalName, r.Flocal) 27 cache.Put(r.FremoteName, r.Fremote) 28 return r, call 29 } 30 31 // operations/about: Return the space used on the remote 32 func TestRcAbout(t *testing.T) { 33 r, call := rcNewRun(t, "operations/about") 34 defer r.Finalise() 35 r.Mkdir(context.Background(), r.Fremote) 36 37 // Will get an error if remote doesn't support About 38 expectedErr := r.Fremote.Features().About == nil 39 40 in := rc.Params{ 41 "fs": r.FremoteName, 42 } 43 out, err := call.Fn(context.Background(), in) 44 if expectedErr { 45 assert.Error(t, err) 46 return 47 } 48 require.NoError(t, err) 49 50 // Can't really check the output much! 51 assert.NotEqual(t, int64(0), out["Total"]) 52 } 53 54 // operations/cleanup: Remove trashed files in the remote or path 55 func TestRcCleanup(t *testing.T) { 56 r, call := rcNewRun(t, "operations/cleanup") 57 defer r.Finalise() 58 59 in := rc.Params{ 60 "fs": r.LocalName, 61 } 62 out, err := call.Fn(context.Background(), in) 63 require.Error(t, err) 64 assert.Equal(t, rc.Params(nil), out) 65 assert.Contains(t, err.Error(), "doesn't support cleanup") 66 } 67 68 // operations/copyfile: Copy a file from source remote to destination remote 69 func TestRcCopyfile(t *testing.T) { 70 r, call := rcNewRun(t, "operations/copyfile") 71 defer r.Finalise() 72 file1 := r.WriteFile("file1", "file1 contents", t1) 73 r.Mkdir(context.Background(), r.Fremote) 74 fstest.CheckItems(t, r.Flocal, file1) 75 fstest.CheckItems(t, r.Fremote) 76 77 in := rc.Params{ 78 "srcFs": r.LocalName, 79 "srcRemote": "file1", 80 "dstFs": r.FremoteName, 81 "dstRemote": "file1-renamed", 82 } 83 out, err := call.Fn(context.Background(), in) 84 require.NoError(t, err) 85 assert.Equal(t, rc.Params(nil), out) 86 87 fstest.CheckItems(t, r.Flocal, file1) 88 file1.Path = "file1-renamed" 89 fstest.CheckItems(t, r.Fremote, file1) 90 } 91 92 // operations/copyurl: Copy the URL to the object 93 func TestRcCopyurl(t *testing.T) { 94 r, call := rcNewRun(t, "operations/copyurl") 95 defer r.Finalise() 96 contents := "file1 contents\n" 97 file1 := r.WriteFile("file1", contents, t1) 98 r.Mkdir(context.Background(), r.Fremote) 99 fstest.CheckItems(t, r.Fremote) 100 101 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 102 _, err := w.Write([]byte(contents)) 103 assert.NoError(t, err) 104 })) 105 defer ts.Close() 106 107 in := rc.Params{ 108 "fs": r.FremoteName, 109 "remote": "file1", 110 "url": ts.URL, 111 "autoFilename": false, 112 "noClobber": false, 113 } 114 out, err := call.Fn(context.Background(), in) 115 require.NoError(t, err) 116 assert.Equal(t, rc.Params(nil), out) 117 118 in = rc.Params{ 119 "fs": r.FremoteName, 120 "remote": "file1", 121 "url": ts.URL, 122 "autoFilename": false, 123 "noClobber": true, 124 } 125 out, err = call.Fn(context.Background(), in) 126 require.Error(t, err) 127 assert.Equal(t, rc.Params(nil), out) 128 129 urlFileName := "filename.txt" 130 in = rc.Params{ 131 "fs": r.FremoteName, 132 "remote": "", 133 "url": ts.URL + "/" + urlFileName, 134 "autoFilename": true, 135 "noClobber": false, 136 } 137 out, err = call.Fn(context.Background(), in) 138 require.NoError(t, err) 139 assert.Equal(t, rc.Params(nil), out) 140 141 in = rc.Params{ 142 "fs": r.FremoteName, 143 "remote": "", 144 "url": ts.URL, 145 "autoFilename": true, 146 "noClobber": false, 147 } 148 out, err = call.Fn(context.Background(), in) 149 require.Error(t, err) 150 assert.Equal(t, rc.Params(nil), out) 151 152 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, fstest.NewItem(urlFileName, contents, t1)}, nil, fs.ModTimeNotSupported) 153 } 154 155 // operations/delete: Remove files in the path 156 func TestRcDelete(t *testing.T) { 157 r, call := rcNewRun(t, "operations/delete") 158 defer r.Finalise() 159 160 file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes 161 file2 := r.WriteObject(context.Background(), "medium", "------------------------------------------------------------", t1) // 60 bytes 162 file3 := r.WriteObject(context.Background(), "large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 100 bytes 163 fstest.CheckItems(t, r.Fremote, file1, file2, file3) 164 165 in := rc.Params{ 166 "fs": r.FremoteName, 167 } 168 out, err := call.Fn(context.Background(), in) 169 require.NoError(t, err) 170 assert.Equal(t, rc.Params(nil), out) 171 172 fstest.CheckItems(t, r.Fremote) 173 } 174 175 // operations/deletefile: Remove the single file pointed to 176 func TestRcDeletefile(t *testing.T) { 177 r, call := rcNewRun(t, "operations/deletefile") 178 defer r.Finalise() 179 180 file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes 181 file2 := r.WriteObject(context.Background(), "medium", "------------------------------------------------------------", t1) // 60 bytes 182 fstest.CheckItems(t, r.Fremote, file1, file2) 183 184 in := rc.Params{ 185 "fs": r.FremoteName, 186 "remote": "small", 187 } 188 out, err := call.Fn(context.Background(), in) 189 require.NoError(t, err) 190 assert.Equal(t, rc.Params(nil), out) 191 192 fstest.CheckItems(t, r.Fremote, file2) 193 } 194 195 // operations/list: List the given remote and path in JSON format 196 func TestRcList(t *testing.T) { 197 r, call := rcNewRun(t, "operations/list") 198 defer r.Finalise() 199 200 file1 := r.WriteObject(context.Background(), "a", "a", t1) 201 file2 := r.WriteObject(context.Background(), "subdir/b", "bb", t2) 202 203 fstest.CheckItems(t, r.Fremote, file1, file2) 204 205 in := rc.Params{ 206 "fs": r.FremoteName, 207 "remote": "", 208 } 209 out, err := call.Fn(context.Background(), in) 210 require.NoError(t, err) 211 212 list := out["list"].([]*operations.ListJSONItem) 213 assert.Equal(t, 2, len(list)) 214 215 checkFile1 := func(got *operations.ListJSONItem) { 216 assert.WithinDuration(t, t1, got.ModTime.When, time.Second) 217 assert.Equal(t, "a", got.Path) 218 assert.Equal(t, "a", got.Name) 219 assert.Equal(t, int64(1), got.Size) 220 assert.Equal(t, "application/octet-stream", got.MimeType) 221 assert.Equal(t, false, got.IsDir) 222 } 223 checkFile1(list[0]) 224 225 checkSubdir := func(got *operations.ListJSONItem) { 226 assert.Equal(t, "subdir", got.Path) 227 assert.Equal(t, "subdir", got.Name) 228 assert.Equal(t, int64(-1), got.Size) 229 assert.Equal(t, "inode/directory", got.MimeType) 230 assert.Equal(t, true, got.IsDir) 231 } 232 checkSubdir(list[1]) 233 234 in = rc.Params{ 235 "fs": r.FremoteName, 236 "remote": "", 237 "opt": rc.Params{ 238 "recurse": true, 239 }, 240 } 241 out, err = call.Fn(context.Background(), in) 242 require.NoError(t, err) 243 244 list = out["list"].([]*operations.ListJSONItem) 245 assert.Equal(t, 3, len(list)) 246 checkFile1(list[0]) 247 checkSubdir(list[1]) 248 249 checkFile2 := func(got *operations.ListJSONItem) { 250 assert.WithinDuration(t, t2, got.ModTime.When, time.Second) 251 assert.Equal(t, "subdir/b", got.Path) 252 assert.Equal(t, "b", got.Name) 253 assert.Equal(t, int64(2), got.Size) 254 assert.Equal(t, "application/octet-stream", got.MimeType) 255 assert.Equal(t, false, got.IsDir) 256 } 257 checkFile2(list[2]) 258 } 259 260 // operations/mkdir: Make a destination directory or container 261 func TestRcMkdir(t *testing.T) { 262 r, call := rcNewRun(t, "operations/mkdir") 263 defer r.Finalise() 264 r.Mkdir(context.Background(), r.Fremote) 265 266 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(r.Fremote)) 267 268 in := rc.Params{ 269 "fs": r.FremoteName, 270 "remote": "subdir", 271 } 272 out, err := call.Fn(context.Background(), in) 273 require.NoError(t, err) 274 assert.Equal(t, rc.Params(nil), out) 275 276 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(r.Fremote)) 277 } 278 279 // operations/movefile: Move a file from source remote to destination remote 280 func TestRcMovefile(t *testing.T) { 281 r, call := rcNewRun(t, "operations/movefile") 282 defer r.Finalise() 283 file1 := r.WriteFile("file1", "file1 contents", t1) 284 r.Mkdir(context.Background(), r.Fremote) 285 fstest.CheckItems(t, r.Flocal, file1) 286 fstest.CheckItems(t, r.Fremote) 287 288 in := rc.Params{ 289 "srcFs": r.LocalName, 290 "srcRemote": "file1", 291 "dstFs": r.FremoteName, 292 "dstRemote": "file1-renamed", 293 } 294 out, err := call.Fn(context.Background(), in) 295 require.NoError(t, err) 296 assert.Equal(t, rc.Params(nil), out) 297 298 fstest.CheckItems(t, r.Flocal) 299 file1.Path = "file1-renamed" 300 fstest.CheckItems(t, r.Fremote, file1) 301 } 302 303 // operations/purge: Remove a directory or container and all of its contents 304 func TestRcPurge(t *testing.T) { 305 r, call := rcNewRun(t, "operations/purge") 306 defer r.Finalise() 307 file1 := r.WriteObject(context.Background(), "subdir/file1", "subdir/file1 contents", t1) 308 309 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"subdir"}, fs.GetModifyWindow(r.Fremote)) 310 311 in := rc.Params{ 312 "fs": r.FremoteName, 313 "remote": "subdir", 314 } 315 out, err := call.Fn(context.Background(), in) 316 require.NoError(t, err) 317 assert.Equal(t, rc.Params(nil), out) 318 319 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(r.Fremote)) 320 } 321 322 // operations/rmdir: Remove an empty directory or container 323 func TestRcRmdir(t *testing.T) { 324 r, call := rcNewRun(t, "operations/rmdir") 325 defer r.Finalise() 326 r.Mkdir(context.Background(), r.Fremote) 327 assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) 328 329 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(r.Fremote)) 330 331 in := rc.Params{ 332 "fs": r.FremoteName, 333 "remote": "subdir", 334 } 335 out, err := call.Fn(context.Background(), in) 336 require.NoError(t, err) 337 assert.Equal(t, rc.Params(nil), out) 338 339 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(r.Fremote)) 340 } 341 342 // operations/rmdirs: Remove all the empty directories in the path 343 func TestRcRmdirs(t *testing.T) { 344 r, call := rcNewRun(t, "operations/rmdirs") 345 defer r.Finalise() 346 r.Mkdir(context.Background(), r.Fremote) 347 assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) 348 assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir/subsubdir")) 349 350 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir", "subdir/subsubdir"}, fs.GetModifyWindow(r.Fremote)) 351 352 in := rc.Params{ 353 "fs": r.FremoteName, 354 "remote": "subdir", 355 } 356 out, err := call.Fn(context.Background(), in) 357 require.NoError(t, err) 358 assert.Equal(t, rc.Params(nil), out) 359 360 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(r.Fremote)) 361 362 assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) 363 assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir/subsubdir")) 364 365 in = rc.Params{ 366 "fs": r.FremoteName, 367 "remote": "subdir", 368 "leaveRoot": true, 369 } 370 out, err = call.Fn(context.Background(), in) 371 require.NoError(t, err) 372 assert.Equal(t, rc.Params(nil), out) 373 374 fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(r.Fremote)) 375 376 } 377 378 // operations/size: Count the number of bytes and files in remote 379 func TestRcSize(t *testing.T) { 380 r, call := rcNewRun(t, "operations/size") 381 defer r.Finalise() 382 file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes 383 file2 := r.WriteObject(context.Background(), "subdir/medium", "------------------------------------------------------------", t1) // 60 bytes 384 file3 := r.WriteObject(context.Background(), "subdir/subsubdir/large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 50 bytes 385 fstest.CheckItems(t, r.Fremote, file1, file2, file3) 386 387 in := rc.Params{ 388 "fs": r.FremoteName, 389 } 390 out, err := call.Fn(context.Background(), in) 391 require.NoError(t, err) 392 assert.Equal(t, rc.Params{ 393 "count": int64(3), 394 "bytes": int64(120), 395 }, out) 396 } 397 398 // operations/publiclink: Create or retrieve a public link to the given file or folder. 399 func TestRcPublicLink(t *testing.T) { 400 r, call := rcNewRun(t, "operations/publiclink") 401 defer r.Finalise() 402 in := rc.Params{ 403 "fs": r.FremoteName, 404 "remote": "", 405 } 406 _, err := call.Fn(context.Background(), in) 407 require.Error(t, err) 408 assert.Contains(t, err.Error(), "doesn't support public links") 409 } 410 411 // operations/fsinfo: Return information about the remote 412 func TestRcFsInfo(t *testing.T) { 413 r, call := rcNewRun(t, "operations/fsinfo") 414 defer r.Finalise() 415 in := rc.Params{ 416 "fs": r.FremoteName, 417 } 418 got, err := call.Fn(context.Background(), in) 419 require.NoError(t, err) 420 want := operations.GetFsInfo(r.Fremote) 421 assert.Equal(t, want.Name, got["Name"]) 422 assert.Equal(t, want.Root, got["Root"]) 423 assert.Equal(t, want.String, got["String"]) 424 assert.Equal(t, float64(want.Precision), got["Precision"]) 425 var hashes []interface{} 426 for _, hash := range want.Hashes { 427 hashes = append(hashes, hash) 428 } 429 assert.Equal(t, hashes, got["Hashes"]) 430 var features = map[string]interface{}{} 431 for k, v := range want.Features { 432 features[k] = v 433 } 434 assert.Equal(t, features, got["Features"]) 435 436 } 437 438 // operations/command: Runs a backend command 439 func TestRcCommand(t *testing.T) { 440 r, call := rcNewRun(t, "backend/command") 441 defer r.Finalise() 442 in := rc.Params{ 443 "fs": r.FremoteName, 444 "command": "noop", 445 "opt": map[string]string{ 446 "echo": "true", 447 "blue": "", 448 }, 449 "arg": []string{ 450 "path1", 451 "path2", 452 }, 453 } 454 got, err := call.Fn(context.Background(), in) 455 if err != nil { 456 assert.False(t, r.Fremote.Features().IsLocal, "mustn't fail on local remote") 457 assert.Contains(t, err.Error(), "command not found") 458 return 459 } 460 want := rc.Params{"result": map[string]interface{}{ 461 "arg": []string{ 462 "path1", 463 "path2", 464 }, 465 "name": "noop", 466 "opt": map[string]string{ 467 "blue": "", 468 "echo": "true", 469 }, 470 }} 471 assert.Equal(t, want, got) 472 errTxt := "explosion in the sausage factory" 473 in["opt"].(map[string]string)["error"] = errTxt 474 _, err = call.Fn(context.Background(), in) 475 assert.Error(t, err) 476 assert.Contains(t, err.Error(), errTxt) 477 }