From 1eccd6504bc19d431aa4f18b65929d0d911e93d8 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 31 May 2024 18:15:50 +0200 Subject: [PATCH] restore: test restore only changed parts functionality --- internal/restorer/restorer_test.go | 93 +++++++++++++++++++++++++ internal/restorer/restorer_unix_test.go | 32 +++++++++ 2 files changed, 125 insertions(+) diff --git a/internal/restorer/restorer_test.go b/internal/restorer/restorer_test.go index 635c30ee6..0d43ffa9d 100644 --- a/internal/restorer/restorer_test.go +++ b/internal/restorer/restorer_test.go @@ -10,6 +10,7 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "testing" "time" @@ -929,6 +930,13 @@ func TestRestorerOverwriteBehavior(t *testing.T) { "dirtest/file": "content: file2\n", }, }, + { + Overwrite: OverwriteIfChanged, + Files: map[string]string{ + "foo": "content: new\n", + "dirtest/file": "content: file2\n", + }, + }, { Overwrite: OverwriteIfNewer, Files: map[string]string{ @@ -982,3 +990,88 @@ func TestRestorerOverwriteBehavior(t *testing.T) { }) } } + +func TestRestoreModified(t *testing.T) { + // overwrite files between snapshots and also change their filesize + snapshots := []Snapshot{ + { + Nodes: map[string]Node{ + "foo": File{Data: "content: foo\n", ModTime: time.Now()}, + "bar": File{Data: "content: a\n", ModTime: time.Now()}, + }, + }, + { + Nodes: map[string]Node{ + "foo": File{Data: "content: a\n", ModTime: time.Now()}, + "bar": File{Data: "content: bar\n", ModTime: time.Now()}, + }, + }, + } + + repo := repository.TestRepository(t) + tempdir := filepath.Join(rtest.TempDir(t), "target") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + for _, snapshot := range snapshots { + sn, id := saveSnapshot(t, repo, snapshot, noopGetGenericAttributes) + t.Logf("snapshot saved as %v", id.Str()) + + res := NewRestorer(repo, sn, Options{Overwrite: OverwriteIfChanged}) + rtest.OK(t, res.RestoreTo(ctx, tempdir)) + n, err := res.VerifyFiles(ctx, tempdir) + rtest.OK(t, err) + rtest.Equals(t, 2, n, "unexpected number of verified files") + } +} + +func TestRestoreIfChanged(t *testing.T) { + origData := "content: foo\n" + modData := "content: bar\n" + rtest.Equals(t, len(modData), len(origData), "broken testcase") + snapshot := Snapshot{ + Nodes: map[string]Node{ + "foo": File{Data: origData, ModTime: time.Now()}, + }, + } + + repo := repository.TestRepository(t) + tempdir := filepath.Join(rtest.TempDir(t), "target") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sn, id := saveSnapshot(t, repo, snapshot, noopGetGenericAttributes) + t.Logf("snapshot saved as %v", id.Str()) + + res := NewRestorer(repo, sn, Options{}) + rtest.OK(t, res.RestoreTo(ctx, tempdir)) + + // modify file but maintain size and timestamp + path := filepath.Join(tempdir, "foo") + f, err := os.OpenFile(path, os.O_RDWR, 0) + rtest.OK(t, err) + fi, err := f.Stat() + rtest.OK(t, err) + _, err = f.Write([]byte(modData)) + rtest.OK(t, err) + rtest.OK(t, f.Close()) + var utimes = [...]syscall.Timespec{ + syscall.NsecToTimespec(fi.ModTime().UnixNano()), + syscall.NsecToTimespec(fi.ModTime().UnixNano()), + } + rtest.OK(t, syscall.UtimesNano(path, utimes[:])) + + for _, overwrite := range []OverwriteBehavior{OverwriteIfChanged, OverwriteAlways} { + res = NewRestorer(repo, sn, Options{Overwrite: overwrite}) + rtest.OK(t, res.RestoreTo(ctx, tempdir)) + data, err := os.ReadFile(path) + rtest.OK(t, err) + if overwrite == OverwriteAlways { + // restore should notice the changed file content + rtest.Equals(t, origData, string(data), "expected original file content") + } else { + // restore should not have noticed the changed file content + rtest.Equals(t, modData, string(data), "expeced modified file content") + } + } +} diff --git a/internal/restorer/restorer_unix_test.go b/internal/restorer/restorer_unix_test.go index 97d2dd07d..9523440cf 100644 --- a/internal/restorer/restorer_unix_test.go +++ b/internal/restorer/restorer_unix_test.go @@ -5,6 +5,7 @@ package restorer import ( "context" + "io/fs" "os" "path/filepath" "syscall" @@ -118,3 +119,34 @@ func TestRestorerProgressBar(t *testing.T) { AllBytesSkipped: 0, }, mock.s) } + +func TestRestorePermissions(t *testing.T) { + snapshot := Snapshot{ + Nodes: map[string]Node{ + "foo": File{Data: "content: foo\n", Mode: 0o600, ModTime: time.Now()}, + }, + } + + repo := repository.TestRepository(t) + tempdir := filepath.Join(rtest.TempDir(t), "target") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sn, id := saveSnapshot(t, repo, snapshot, noopGetGenericAttributes) + t.Logf("snapshot saved as %v", id.Str()) + + res := NewRestorer(repo, sn, Options{}) + rtest.OK(t, res.RestoreTo(ctx, tempdir)) + + for _, overwrite := range []OverwriteBehavior{OverwriteIfChanged, OverwriteAlways} { + // tamper with permissions + path := filepath.Join(tempdir, "foo") + rtest.OK(t, os.Chmod(path, 0o700)) + + res = NewRestorer(repo, sn, Options{Overwrite: overwrite}) + rtest.OK(t, res.RestoreTo(ctx, tempdir)) + fi, err := os.Stat(path) + rtest.OK(t, err) + rtest.Equals(t, fs.FileMode(0o600), fi.Mode().Perm(), "unexpected permissions") + } +}