diff options
Diffstat (limited to 'fs/operations/rc_test.go')
| -rw-r--r-- | fs/operations/rc_test.go | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/fs/operations/rc_test.go b/fs/operations/rc_test.go new file mode 100644 index 0000000..04357fb --- /dev/null +++ b/fs/operations/rc_test.go @@ -0,0 +1,892 @@ +package operations_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path" + "sort" + "strings" + "testing" + "time" + + "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/cache" + "github.com/rclone/rclone/fs/hash" + "github.com/rclone/rclone/fs/operations" + "github.com/rclone/rclone/fs/rc" + "github.com/rclone/rclone/fstest" + "github.com/rclone/rclone/lib/diskusage" + "github.com/rclone/rclone/lib/rest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func rcNewRun(t *testing.T, method string) (*fstest.Run, *rc.Call) { + if *fstest.RemoteName != "" { + t.Skip("Skipping test on non local remote") + } + r := fstest.NewRun(t) + call := rc.Calls.Get(method) + assert.NotNil(t, call) + cache.Put(r.LocalName, r.Flocal) + cache.Put(r.FremoteName, r.Fremote) + return r, call +} + +// operations/about: Return the space used on the remote +func TestRcAbout(t *testing.T) { + r, call := rcNewRun(t, "operations/about") + r.Mkdir(context.Background(), r.Fremote) + + // Will get an error if remote doesn't support About + expectedErr := r.Fremote.Features().About == nil + + in := rc.Params{ + "fs": r.FremoteName, + } + out, err := call.Fn(context.Background(), in) + if expectedErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + + // Can't really check the output much! + assert.NotEqual(t, int64(0), out["Total"]) +} + +// operations/cleanup: Remove trashed files in the remote or path +func TestRcCleanup(t *testing.T) { + r, call := rcNewRun(t, "operations/cleanup") + + in := rc.Params{ + "fs": r.LocalName, + } + out, err := call.Fn(context.Background(), in) + require.Error(t, err) + assert.Equal(t, rc.Params(nil), out) + assert.Contains(t, err.Error(), "doesn't support cleanup") +} + +// operations/copyfile: Copy a file from source remote to destination remote +func TestRcCopyfile(t *testing.T) { + r, call := rcNewRun(t, "operations/copyfile") + file1 := r.WriteFile("file1", "file1 contents", t1) + r.Mkdir(context.Background(), r.Fremote) + r.CheckLocalItems(t, file1) + r.CheckRemoteItems(t) + + in := rc.Params{ + "srcFs": r.LocalName, + "srcRemote": "file1", + "dstFs": r.FremoteName, + "dstRemote": "file1-renamed", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + r.CheckLocalItems(t, file1) + file1.Path = "file1-renamed" + r.CheckRemoteItems(t, file1) +} + +// operations/copyurl: Copy the URL to the object +func TestRcCopyurl(t *testing.T) { + r, call := rcNewRun(t, "operations/copyurl") + contents := "file1 contents\n" + file1 := r.WriteFile("file1", contents, t1) + r.Mkdir(context.Background(), r.Fremote) + r.CheckRemoteItems(t) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(contents)) + assert.NoError(t, err) + })) + defer ts.Close() + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "file1", + "url": ts.URL, + "autoFilename": false, + "noClobber": false, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + in = rc.Params{ + "fs": r.FremoteName, + "remote": "file1", + "url": ts.URL, + "autoFilename": false, + "noClobber": true, + } + out, err = call.Fn(context.Background(), in) + require.Error(t, err) + assert.Equal(t, rc.Params(nil), out) + + urlFileName := "filename.txt" + in = rc.Params{ + "fs": r.FremoteName, + "remote": "", + "url": ts.URL + "/" + urlFileName, + "autoFilename": true, + "noClobber": false, + } + out, err = call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + in = rc.Params{ + "fs": r.FremoteName, + "remote": "", + "url": ts.URL, + "autoFilename": true, + "noClobber": false, + } + out, err = call.Fn(context.Background(), in) + require.Error(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, fstest.NewItem(urlFileName, contents, t1)}, nil, fs.ModTimeNotSupported) +} + +// operations/delete: Remove files in the path +func TestRcDelete(t *testing.T) { + r, call := rcNewRun(t, "operations/delete") + + file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes + file2 := r.WriteObject(context.Background(), "medium", "------------------------------------------------------------", t1) // 60 bytes + file3 := r.WriteObject(context.Background(), "large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 100 bytes + r.CheckRemoteItems(t, file1, file2, file3) + + in := rc.Params{ + "fs": r.FremoteName, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + r.CheckRemoteItems(t) +} + +// operations/deletefile: Remove the single file pointed to +func TestRcDeletefile(t *testing.T) { + r, call := rcNewRun(t, "operations/deletefile") + + file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes + file2 := r.WriteObject(context.Background(), "medium", "------------------------------------------------------------", t1) // 60 bytes + r.CheckRemoteItems(t, file1, file2) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "small", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + r.CheckRemoteItems(t, file2) +} + +// operations/list: List the given remote and path in JSON format. +func TestRcList(t *testing.T) { + r, call := rcNewRun(t, "operations/list") + + file1 := r.WriteObject(context.Background(), "a", "a", t1) + file2 := r.WriteObject(context.Background(), "subdir/b", "bb", t2) + + r.CheckRemoteItems(t, file1, file2) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + + list := out["list"].([]*operations.ListJSONItem) + assert.Equal(t, 2, len(list)) + + checkFile1 := func(got *operations.ListJSONItem) { + assert.WithinDuration(t, t1, got.ModTime.When, time.Second) + assert.Equal(t, "a", got.Path) + assert.Equal(t, "a", got.Name) + assert.Equal(t, int64(1), got.Size) + assert.Equal(t, "application/octet-stream", got.MimeType) + assert.Equal(t, false, got.IsDir) + } + checkFile1(list[0]) + + checkSubdir := func(got *operations.ListJSONItem) { + assert.Equal(t, "subdir", got.Path) + assert.Equal(t, "subdir", got.Name) + // assert.Equal(t, int64(-1), got.Size) // size can vary for directories + assert.Equal(t, "inode/directory", got.MimeType) + assert.Equal(t, true, got.IsDir) + } + checkSubdir(list[1]) + + in = rc.Params{ + "fs": r.FremoteName, + "remote": "", + "opt": rc.Params{ + "recurse": true, + }, + } + out, err = call.Fn(context.Background(), in) + require.NoError(t, err) + + list = out["list"].([]*operations.ListJSONItem) + assert.Equal(t, 3, len(list)) + checkFile1(list[0]) + checkSubdir(list[1]) + + checkFile2 := func(got *operations.ListJSONItem) { + assert.WithinDuration(t, t2, got.ModTime.When, time.Second) + assert.Equal(t, "subdir/b", got.Path) + assert.Equal(t, "b", got.Name) + assert.Equal(t, int64(2), got.Size) + assert.Equal(t, "application/octet-stream", got.MimeType) + assert.Equal(t, false, got.IsDir) + } + checkFile2(list[2]) +} + +// operations/stat: Stat the given remote and path in JSON format. +func TestRcStat(t *testing.T) { + r, call := rcNewRun(t, "operations/stat") + + file1 := r.WriteObject(context.Background(), "subdir/a", "a", t1) + + r.CheckRemoteItems(t, file1) + + fetch := func(t *testing.T, remotePath string) *operations.ListJSONItem { + in := rc.Params{ + "fs": r.FremoteName, + "remote": remotePath, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + return out["item"].(*operations.ListJSONItem) + } + + t.Run("Root", func(t *testing.T) { + stat := fetch(t, "") + assert.Equal(t, "", stat.Path) + assert.Equal(t, "", stat.Name) + assert.Equal(t, int64(-1), stat.Size) + assert.Equal(t, "inode/directory", stat.MimeType) + assert.Equal(t, true, stat.IsDir) + }) + + t.Run("File", func(t *testing.T) { + stat := fetch(t, "subdir/a") + assert.WithinDuration(t, t1, stat.ModTime.When, time.Second) + assert.Equal(t, "subdir/a", stat.Path) + assert.Equal(t, "a", stat.Name) + assert.Equal(t, int64(1), stat.Size) + assert.Equal(t, "application/octet-stream", stat.MimeType) + assert.Equal(t, false, stat.IsDir) + }) + + t.Run("Subdir", func(t *testing.T) { + stat := fetch(t, "subdir") + assert.Equal(t, "subdir", stat.Path) + assert.Equal(t, "subdir", stat.Name) + // assert.Equal(t, int64(-1), stat.Size) // size can vary for directories + assert.Equal(t, "inode/directory", stat.MimeType) + assert.Equal(t, true, stat.IsDir) + }) + + t.Run("NotFound", func(t *testing.T) { + stat := fetch(t, "notfound") + assert.Nil(t, stat) + }) +} + +// operations/settier: Set the storage tier of a fs +func TestRcSetTier(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/settier") + if !r.Fremote.Features().SetTier { + t.Skip("settier not supported") + } + file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1) + r.CheckRemoteItems(t, file1) + + // Because we don't know what the current tier options here are, let's + // just get the current tier, and reuse that + o, err := r.Fremote.NewObject(ctx, file1.Path) + require.NoError(t, err) + trr, ok := o.(fs.GetTierer) + require.True(t, ok) + ctier := trr.GetTier() + in := rc.Params{ + "fs": r.FremoteName, + "tier": ctier, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + +} + +// operations/settier: Set the storage tier of a file +func TestRcSetTierFile(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/settierfile") + if !r.Fremote.Features().SetTier { + t.Skip("settier not supported") + } + file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1) + r.CheckRemoteItems(t, file1) + + // Because we don't know what the current tier options here are, let's + // just get the current tier, and reuse that + o, err := r.Fremote.NewObject(ctx, file1.Path) + require.NoError(t, err) + trr, ok := o.(fs.GetTierer) + require.True(t, ok) + ctier := trr.GetTier() + in := rc.Params{ + "fs": r.FremoteName, + "remote": "file1", + "tier": ctier, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + +} + +// operations/mkdir: Make a destination directory or container +func TestRcMkdir(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/mkdir") + r.Mkdir(context.Background(), r.Fremote) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(ctx, r.Fremote)) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "subdir", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(ctx, r.Fremote)) +} + +// operations/movefile: Move a file from source remote to destination remote +func TestRcMovefile(t *testing.T) { + r, call := rcNewRun(t, "operations/movefile") + file1 := r.WriteFile("file1", "file1 contents", t1) + r.Mkdir(context.Background(), r.Fremote) + r.CheckLocalItems(t, file1) + r.CheckRemoteItems(t) + + in := rc.Params{ + "srcFs": r.LocalName, + "srcRemote": "file1", + "dstFs": r.FremoteName, + "dstRemote": "file1-renamed", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + r.CheckLocalItems(t) + file1.Path = "file1-renamed" + r.CheckRemoteItems(t, file1) +} + +// operations/purge: Remove a directory or container and all of its contents +func TestRcPurge(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/purge") + file1 := r.WriteObject(context.Background(), "subdir/file1", "subdir/file1 contents", t1) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"subdir"}, fs.GetModifyWindow(ctx, r.Fremote)) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "subdir", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(ctx, r.Fremote)) +} + +// operations/rmdir: Remove an empty directory or container +func TestRcRmdir(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/rmdir") + r.Mkdir(context.Background(), r.Fremote) + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(ctx, r.Fremote)) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "subdir", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(ctx, r.Fremote)) +} + +// operations/rmdirs: Remove all the empty directories in the path +func TestRcRmdirs(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/rmdirs") + r.Mkdir(context.Background(), r.Fremote) + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir/subsubdir")) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir", "subdir/subsubdir"}, fs.GetModifyWindow(ctx, r.Fremote)) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": "subdir", + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, fs.GetModifyWindow(ctx, r.Fremote)) + + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir/subsubdir")) + + in = rc.Params{ + "fs": r.FremoteName, + "remote": "subdir", + "leaveRoot": true, + } + out, err = call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params(nil), out) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"subdir"}, fs.GetModifyWindow(ctx, r.Fremote)) + +} + +// operations/size: Count the number of bytes and files in remote +func TestRcSize(t *testing.T) { + r, call := rcNewRun(t, "operations/size") + file1 := r.WriteObject(context.Background(), "small", "1234567890", t2) // 10 bytes + file2 := r.WriteObject(context.Background(), "subdir/medium", "------------------------------------------------------------", t1) // 60 bytes + file3 := r.WriteObject(context.Background(), "subdir/subsubdir/large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 50 bytes + r.CheckRemoteItems(t, file1, file2, file3) + + in := rc.Params{ + "fs": r.FremoteName, + } + out, err := call.Fn(context.Background(), in) + require.NoError(t, err) + assert.Equal(t, rc.Params{ + "count": int64(3), + "bytes": int64(120), + "sizeless": int64(0), + }, out) +} + +// operations/publiclink: Create or retrieve a public link to the given file or folder. +func TestRcPublicLink(t *testing.T) { + r, call := rcNewRun(t, "operations/publiclink") + in := rc.Params{ + "fs": r.FremoteName, + "remote": "", + "expire": "5m", + "unlink": false, + } + _, err := call.Fn(context.Background(), in) + require.Error(t, err) + assert.Contains(t, err.Error(), "doesn't support public links") +} + +// operations/fsinfo: Return information about the remote +func TestRcFsInfo(t *testing.T) { + r, call := rcNewRun(t, "operations/fsinfo") + in := rc.Params{ + "fs": r.FremoteName, + } + got, err := call.Fn(context.Background(), in) + require.NoError(t, err) + want := operations.GetFsInfo(r.Fremote) + assert.Equal(t, want.Name, got["Name"]) + assert.Equal(t, want.Root, got["Root"]) + assert.Equal(t, want.String, got["String"]) + assert.Equal(t, float64(want.Precision), got["Precision"]) + var hashes []any + for _, hash := range want.Hashes { + hashes = append(hashes, hash) + } + assert.Equal(t, hashes, got["Hashes"]) + var features = map[string]any{} + for k, v := range want.Features { + features[k] = v + } + assert.Equal(t, features, got["Features"]) + +} + +// operations/uploadfile : Tests if upload file succeeds +func TestUploadFile(t *testing.T) { + r, call := rcNewRun(t, "operations/uploadfile") + ctx := context.Background() + + testFileName := "uploadfile-test.txt" + testFileContent := "Hello World" + r.WriteFile(testFileName, testFileContent, t1) + testItem1 := fstest.NewItem(testFileName, testFileContent, t1) + testItem2 := fstest.NewItem(path.Join("subdir", testFileName), testFileContent, t1) + + currentFile, err := os.Open(path.Join(r.LocalName, testFileName)) + require.NoError(t, err) + + defer func() { + assert.NoError(t, currentFile.Close()) + }() + + formReader, contentType, _, err := rest.MultipartUpload(ctx, currentFile, url.Values{}, "file", testFileName, "application/octet-stream") + require.NoError(t, err) + + httpReq := httptest.NewRequest("POST", "/", formReader) + httpReq.Header.Add("Content-Type", contentType) + + in := rc.Params{ + "_request": httpReq, + "fs": r.FremoteName, + "remote": "", + } + + _, err = call.Fn(context.Background(), in) + require.NoError(t, err) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{testItem1}, nil, fs.ModTimeNotSupported) + + assert.NoError(t, r.Fremote.Mkdir(context.Background(), "subdir")) + + currentFile2, err := os.Open(path.Join(r.LocalName, testFileName)) + require.NoError(t, err) + + defer func() { + assert.NoError(t, currentFile2.Close()) + }() + + formReader, contentType, _, err = rest.MultipartUpload(ctx, currentFile2, url.Values{}, "file", testFileName, "application/octet-stream") + require.NoError(t, err) + + httpReq = httptest.NewRequest("POST", "/", formReader) + httpReq.Header.Add("Content-Type", contentType) + + in = rc.Params{ + "_request": httpReq, + "fs": r.FremoteName, + "remote": "subdir", + } + + _, err = call.Fn(context.Background(), in) + require.NoError(t, err) + + fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{testItem1, testItem2}, nil, fs.ModTimeNotSupported) + +} + +// operations/command: Runs a backend command +func TestRcCommand(t *testing.T) { + r, call := rcNewRun(t, "backend/command") + in := rc.Params{ + "fs": r.FremoteName, + "command": "noop", + "opt": map[string]string{ + "echo": "true", + "blue": "", + }, + "arg": []string{ + "path1", + "path2", + }, + } + got, err := call.Fn(context.Background(), in) + if err != nil { + assert.False(t, r.Fremote.Features().IsLocal, "mustn't fail on local remote") + assert.Contains(t, err.Error(), "command not found") + return + } + want := rc.Params{"result": map[string]any{ + "arg": []string{ + "path1", + "path2", + }, + "name": "noop", + "opt": map[string]string{ + "blue": "", + "echo": "true", + }, + }} + assert.Equal(t, want, got) + errTxt := "explosion in the sausage factory" + in["opt"].(map[string]string)["error"] = errTxt + _, err = call.Fn(context.Background(), in) + assert.Error(t, err) + assert.Contains(t, err.Error(), errTxt) +} + +// operations/command: Runs a backend command +func TestRcDu(t *testing.T) { + ctx := context.Background() + _, call := rcNewRun(t, "core/du") + in := rc.Params{} + out, err := call.Fn(ctx, in) + if err == diskusage.ErrUnsupported { + t.Skip(err) + } + assert.NotEqual(t, "", out["dir"]) + info := out["info"].(diskusage.Info) + assert.True(t, info.Total != 0) + assert.True(t, info.Total > info.Free) + assert.True(t, info.Total > info.Available) + assert.True(t, info.Free >= info.Available) +} + +// operations/check: check the source and destination are the same +func TestRcCheck(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/check") + r.Mkdir(ctx, r.Fremote) + + MD5SUMS := ` +0ef726ce9b1a7692357ff70dd321d595 file1 +deadbeefcafe00000000000000000000 subdir/file2 +0386a8b8fcf672c326845c00ba41b9e2 subdir/subsubdir/file4 +` + + file1 := r.WriteBoth(ctx, "file1", "file1 contents", t1) + file2 := r.WriteFile("subdir/file2", MD5SUMS, t2) + file3 := r.WriteObject(ctx, "subdir/subsubdir/file3", "file3 contents", t3) + file4a := r.WriteFile("subdir/subsubdir/file4", "file4 contents", t3) + file4b := r.WriteObject(ctx, "subdir/subsubdir/file4", "file4 different contents", t3) + // operations.HashLister(ctx, hash.MD5, false, false, r.Fremote, os.Stdout) + + r.CheckLocalItems(t, file1, file2, file4a) + r.CheckRemoteItems(t, file1, file3, file4b) + + pstring := func(items ...fstest.Item) *[]string { + xs := make([]string, len(items)) + for i, item := range items { + xs[i] = item.Path + } + return &xs + } + + for _, testName := range []string{"Normal", "Download"} { + t.Run(testName, func(t *testing.T) { + in := rc.Params{ + "srcFs": r.LocalName, + "dstFs": r.FremoteName, + "combined": true, + "missingOnSrc": true, + "missingOnDst": true, + "match": true, + "differ": true, + "error": true, + } + if testName == "Download" { + in["download"] = true + } + out, err := call.Fn(ctx, in) + require.NoError(t, err) + + combined := []string{ + "= " + file1.Path, + "+ " + file2.Path, + "- " + file3.Path, + "* " + file4a.Path, + } + sort.Strings(combined) + sort.Strings(*out["combined"].(*[]string)) + want := rc.Params{ + "missingOnSrc": pstring(file3), + "missingOnDst": pstring(file2), + "differ": pstring(file4a), + "error": pstring(), + "match": pstring(file1), + "combined": &combined, + "status": "3 differences found", + "success": false, + } + if testName == "Normal" { + want["hashType"] = "md5" + } + + assert.Equal(t, want, out) + }) + } + + t.Run("CheckFile", func(t *testing.T) { + // The checksum file is treated as the source and srcFs is not used + in := rc.Params{ + "dstFs": r.FremoteName, + "combined": true, + "missingOnSrc": true, + "missingOnDst": true, + "match": true, + "differ": true, + "error": true, + "checkFileFs": r.LocalName, + "checkFileRemote": file2.Path, + "checkFileHash": "md5", + } + out, err := call.Fn(ctx, in) + require.NoError(t, err) + + combined := []string{ + "= " + file1.Path, + "+ " + file2.Path, + "- " + file3.Path, + "* " + file4a.Path, + } + sort.Strings(combined) + sort.Strings(*out["combined"].(*[]string)) + if strings.HasPrefix(out["status"].(string), "file not in") { + out["status"] = "file not in" + } + want := rc.Params{ + "missingOnSrc": pstring(file3), + "missingOnDst": pstring(file2), + "differ": pstring(file4a), + "error": pstring(), + "match": pstring(file1), + "combined": &combined, + "hashType": "md5", + "status": "file not in", + "success": false, + } + + assert.Equal(t, want, out) + }) + +} + +// operations/hashsum: hashsum a directory +func TestRcHashsum(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/hashsum") + r.Mkdir(ctx, r.Fremote) + + file1Contents := "file1 contents" + file1 := r.WriteBoth(ctx, "hashsum-file1", file1Contents, t1) + r.CheckLocalItems(t, file1) + r.CheckRemoteItems(t, file1) + + hasher := hash.NewMultiHasher() + _, err := hasher.Write([]byte(file1Contents)) + require.NoError(t, err) + + for _, test := range []struct { + ht hash.Type + base64 bool + download bool + }{ + { + ht: r.Fremote.Hashes().GetOne(), + }, { + ht: r.Fremote.Hashes().GetOne(), + base64: true, + }, { + ht: hash.Whirlpool, + base64: false, + download: true, + }, { + ht: hash.Whirlpool, + base64: true, + download: true, + }, + } { + t.Run(fmt.Sprintf("hash=%v,base64=%v,download=%v", test.ht, test.base64, test.download), func(t *testing.T) { + file1Hash, err := hasher.SumString(test.ht, test.base64) + require.NoError(t, err) + + in := rc.Params{ + "fs": r.FremoteName, + "hashType": test.ht.String(), + "base64": test.base64, + "download": test.download, + } + + out, err := call.Fn(ctx, in) + require.NoError(t, err) + assert.Equal(t, test.ht.String(), out["hashType"]) + want := []string{ + fmt.Sprintf("%s hashsum-file1", file1Hash), + } + assert.Equal(t, want, out["hashsum"]) + }) + } +} + +// operations/hashsum: hashsum a single file +func TestRcHashsumSingleFile(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/hashsum") + r.Mkdir(ctx, r.Fremote) + + file1Contents := "file1 contents" + file1 := r.WriteBoth(ctx, "hashsum-file1", file1Contents, t1) + file2Contents := "file2 contents" + file2 := r.WriteBoth(ctx, "hashsum-file2", file2Contents, t1) + r.CheckLocalItems(t, file1, file2) + r.CheckRemoteItems(t, file1, file2) + + // Make an fs pointing to just the file + fsString := path.Join(r.FremoteName, file1.Path) + + in := rc.Params{ + "fs": fsString, + "hashType": "MD5", + "download": true, + } + + out, err := call.Fn(ctx, in) + require.NoError(t, err) + assert.Equal(t, "md5", out["hashType"]) + assert.Equal(t, []string{"0ef726ce9b1a7692357ff70dd321d595 hashsum-file1"}, out["hashsum"]) +} + +// operations/hashsumfile: hashsum a single file +func TestRcHashsumFile(t *testing.T) { + ctx := context.Background() + r, call := rcNewRun(t, "operations/hashsumfile") + r.Mkdir(ctx, r.Fremote) + + file1Contents := "file1 contents" + file1 := r.WriteBoth(ctx, "hashsumfile-file1", file1Contents, t1) + r.CheckLocalItems(t, file1) + r.CheckRemoteItems(t, file1) + + in := rc.Params{ + "fs": r.FremoteName, + "remote": file1.Path, + "hashType": "MD5", + "download": true, + } + + out, err := call.Fn(ctx, in) + require.NoError(t, err) + assert.Equal(t, "md5", out["hashType"]) + assert.Equal(t, "0ef726ce9b1a7692357ff70dd321d595", out["hash"]) +} |
