From 15374d22e9ec7c78e772f13d2cbbff2e0f799291 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 22 Aug 2020 20:17:31 +0200 Subject: [PATCH] integration tests: Add basic tests for copy command --- cmd/restic/cmd_copy.go | 11 +++- cmd/restic/integration_test.go | 115 +++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 7c2752792..be2c67356 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -30,6 +30,7 @@ their deduplication. // CopyOptions bundles all options for the copy command. type CopyOptions struct { Repo string + password string PasswordFile string PasswordCommand string KeyHint string @@ -64,9 +65,13 @@ func runCopy(opts CopyOptions, gopts GlobalOptions, args []string) error { dstGopts.PasswordFile = opts.PasswordFile dstGopts.PasswordCommand = opts.PasswordCommand dstGopts.KeyHint = opts.KeyHint - dstGopts.password, err = resolvePassword(dstGopts, "RESTIC_PASSWORD2") - if err != nil { - return err + if opts.password != "" { + dstGopts.password = opts.password + } else { + dstGopts.password, err = resolvePassword(dstGopts, "RESTIC_PASSWORD2") + if err != nil { + return err + } } dstGopts.password, err = ReadPassword(dstGopts, "enter password for destination repository: ") if err != nil { diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index 43cf8ed41..36ce9e487 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -600,6 +600,121 @@ func TestBackupTags(t *testing.T) { "expected parent to be %v, got %v", parent.ID, newest.Parent) } +func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) { + copyOpts := CopyOptions{ + Repo: dstGopts.Repo, + password: dstGopts.password, + } + + rtest.OK(t, runCopy(copyOpts, srcGopts, nil)) +} + +func TestCopy(t *testing.T) { + env, cleanup := withTestEnvironment(t) + defer cleanup() + env2, cleanup2 := withTestEnvironment(t) + defer cleanup2() + + testSetupBackupData(t, env) + opts := BackupOptions{} + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9")}, opts, env.gopts) + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "2")}, opts, env.gopts) + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "3")}, opts, env.gopts) + testRunCheck(t, env.gopts) + + testRunInit(t, env2.gopts) + testRunCopy(t, env.gopts, env2.gopts) + + snapshotIDs := testRunList(t, "snapshots", env.gopts) + copiedSnapshotIDs := testRunList(t, "snapshots", env2.gopts) + + // Check that the copies size seems reasonable + rtest.Assert(t, len(snapshotIDs) == len(copiedSnapshotIDs), "expected %v snapshots, found %v", + len(snapshotIDs), len(copiedSnapshotIDs)) + stat := dirStats(env.repo) + stat2 := dirStats(env2.repo) + sizeDiff := int64(stat.size) - int64(stat2.size) + if sizeDiff < 0 { + sizeDiff = -sizeDiff + } + rtest.Assert(t, sizeDiff < int64(stat.size)/50, "expected less than 2%% size difference: %v vs. %v", + stat.size, stat2.size) + + // Check integrity of the copy + testRunCheck(t, env2.gopts) + + // Check that the copied snapshots have the same tree contents as the old ones (= identical tree hash) + origRestores := make(map[string]struct{}) + for i, snapshotID := range snapshotIDs { + restoredir := filepath.Join(env.base, fmt.Sprintf("restore%d", i)) + origRestores[restoredir] = struct{}{} + testRunRestore(t, env.gopts, restoredir, snapshotID) + } + for i, snapshotID := range copiedSnapshotIDs { + restoredir := filepath.Join(env2.base, fmt.Sprintf("restore%d", i)) + testRunRestore(t, env2.gopts, restoredir, snapshotID) + foundMatch := false + for cmpdir := range origRestores { + diff := directoriesContentsDiff(restoredir, cmpdir) + if diff == "" { + delete(origRestores, cmpdir) + foundMatch = true + } + } + + rtest.Assert(t, foundMatch, "found no counterpart for snapshot %v", snapshotID) + } + + rtest.Assert(t, len(origRestores) == 0, "found not copied snapshots") +} + +func TestCopyIncremental(t *testing.T) { + env, cleanup := withTestEnvironment(t) + defer cleanup() + env2, cleanup2 := withTestEnvironment(t) + defer cleanup2() + + testSetupBackupData(t, env) + opts := BackupOptions{} + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9")}, opts, env.gopts) + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "2")}, opts, env.gopts) + testRunCheck(t, env.gopts) + + testRunInit(t, env2.gopts) + testRunCopy(t, env.gopts, env2.gopts) + + snapshotIDs := testRunList(t, "snapshots", env.gopts) + copiedSnapshotIDs := testRunList(t, "snapshots", env2.gopts) + + // Check that the copies size seems reasonable + testRunCheck(t, env2.gopts) + rtest.Assert(t, len(snapshotIDs) == len(copiedSnapshotIDs), "expected %v snapshots, found %v", + len(snapshotIDs), len(copiedSnapshotIDs)) + + // check that no snapshots are copied, as there are no new ones + testRunCopy(t, env.gopts, env2.gopts) + testRunCheck(t, env2.gopts) + copiedSnapshotIDs = testRunList(t, "snapshots", env2.gopts) + rtest.Assert(t, len(snapshotIDs) == len(copiedSnapshotIDs), "still expected %v snapshots, found %v", + len(snapshotIDs), len(copiedSnapshotIDs)) + + // check that only new snapshots are copied + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "3")}, opts, env.gopts) + testRunCopy(t, env.gopts, env2.gopts) + testRunCheck(t, env2.gopts) + snapshotIDs = testRunList(t, "snapshots", env.gopts) + copiedSnapshotIDs = testRunList(t, "snapshots", env2.gopts) + rtest.Assert(t, len(snapshotIDs) == len(copiedSnapshotIDs), "still expected %v snapshots, found %v", + len(snapshotIDs), len(copiedSnapshotIDs)) + + // also test the reverse direction + testRunCopy(t, env2.gopts, env.gopts) + testRunCheck(t, env.gopts) + snapshotIDs = testRunList(t, "snapshots", env.gopts) + rtest.Assert(t, len(snapshotIDs) == len(copiedSnapshotIDs), "still expected %v snapshots, found %v", + len(copiedSnapshotIDs), len(snapshotIDs)) +} + func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) { rtest.OK(t, runTag(opts, gopts, []string{})) }