Skip to content

Commit

Permalink
Add UT for uploader
Browse files Browse the repository at this point in the history
Signed-off-by: Ming <mqiu@vmware.com>
  • Loading branch information
qiuming-best committed Jun 13, 2023
1 parent c807198 commit caa5c6e
Show file tree
Hide file tree
Showing 13 changed files with 1,542 additions and 58 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/6374-qiuming-best
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add unit test for pkg/uploader
2 changes: 1 addition & 1 deletion pkg/repository/backup_repo_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func GetBackupRepository(ctx context.Context, cli client.Client, namespace strin
return repo, nil
}

func newBackupRepository(namespace string, key BackupRepositoryKey) *velerov1api.BackupRepository {
func NewBackupRepository(namespace string, key BackupRepositoryKey) *velerov1api.BackupRepository {
return &velerov1api.BackupRepository{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Expand Down
2 changes: 1 addition & 1 deletion pkg/repository/ensurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (r *Ensurer) repoLock(key BackupRepositoryKey) *sync.Mutex {
}

func (r *Ensurer) createBackupRepositoryAndWait(ctx context.Context, namespace string, backupRepoKey BackupRepositoryKey) (*velerov1api.BackupRepository, error) {
toCreate := newBackupRepository(namespace, backupRepoKey)
toCreate := NewBackupRepository(namespace, backupRepoKey)
if err := r.repoClient.Create(ctx, toCreate, &client.CreateOptions{}); err != nil {
return nil, errors.Wrap(err, "unable to create backup repository resource")
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/repository/ensurer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

func TestEnsureRepo(t *testing.T) {
bkRepoObj := newBackupRepository(velerov1.DefaultNamespace, BackupRepositoryKey{
bkRepoObj := NewBackupRepository(velerov1.DefaultNamespace, BackupRepositoryKey{
VolumeNamespace: "fake-ns",
BackupLocation: "fake-bsl",
RepositoryType: "fake-repo-type",
Expand Down Expand Up @@ -121,7 +121,7 @@ func TestEnsureRepo(t *testing.T) {
}

func TestCreateBackupRepositoryAndWait(t *testing.T) {
bkRepoObj := newBackupRepository(velerov1.DefaultNamespace, BackupRepositoryKey{
bkRepoObj := NewBackupRepository(velerov1.DefaultNamespace, BackupRepositoryKey{
VolumeNamespace: "fake-ns",
BackupLocation: "fake-bsl",
RepositoryType: "fake-repo-type",
Expand Down
96 changes: 96 additions & 0 deletions pkg/uploader/kopia/progress_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Copyright The Velero Contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kopia

import (
"testing"
"time"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/vmware-tanzu/velero/pkg/uploader"
)

type fakeProgressUpdater struct{}

func (f *fakeProgressUpdater) UpdateProgress(p *uploader.Progress) {}

func TestThrottle_ShouldOutput(t *testing.T) {
testCases := []struct {
interval time.Duration
throttle int64
expectedOutput bool
}{
{interval: time.Second, expectedOutput: true},
{interval: time.Second, throttle: time.Now().UnixNano() + int64(time.Nanosecond*10000), expectedOutput: false},
}
p := new(Progress)
for _, tc := range testCases {
// Setup
p.InitThrottle(tc.interval)
p.outputThrottle.throttle = int64(tc.throttle)
// Perform the test

output := p.outputThrottle.ShouldOutput()

// Verify the result
if output != tc.expectedOutput {
t.Errorf("Expected ShouldOutput to return %v, but got %v", tc.expectedOutput, output)
}
}
}

func TestProgress(t *testing.T) {
fileName := "test-filename"
var numBytes int64 = 1
testCases := []struct {
interval time.Duration
throttle int64
}{
{interval: time.Second},
{interval: time.Second, throttle: time.Now().UnixNano() + int64(time.Nanosecond*10000)},
}
p := new(Progress)
p.Log = logrus.New()
p.Updater = &fakeProgressUpdater{}
for _, tc := range testCases {
// Setup
p.InitThrottle(tc.interval)
p.outputThrottle.throttle = int64(tc.throttle)
p.InitThrottle(time.Duration(time.Second))
// All below calls put together for the implementation are empty or just very simple and just want to cover testing
// If wanting to write unit tests for some functions could remove it and with writing new function alone
p.UpdateProgress()
p.UploadedBytes(numBytes)
p.Error("test-path", nil, true)
p.Error("test-path", errors.New("processing error"), false)
p.UploadStarted()
p.EstimatedDataSize(1, numBytes)
p.CachedFile(fileName, numBytes)
p.HashedBytes(numBytes)
p.HashingFile(fileName)
p.ExcludedFile(fileName, numBytes)
p.ExcludedDir(fileName)
p.FinishedHashingFile(fileName, numBytes)
p.StartedDirectory(fileName)
p.FinishedDirectory(fileName)
p.UploadFinished()
p.ProgressBytes(numBytes, numBytes)
p.FinishedFile(fileName, nil)
}
}
204 changes: 204 additions & 0 deletions pkg/uploader/kopia/shim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
Copyright The Velero Contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kopia

import (
"context"
"errors"
"testing"
"time"

"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/content"
"github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/repo/object"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo/mocks"
)

func TestShimRepo(t *testing.T) {
ctx := context.Background()
backupRepo := &mocks.BackupRepo{}
backupRepo.On("Time").Return(time.Time{})
shim := NewShimRepo(backupRepo)
// All below calls put together for the implementation are empty or just very simple, and just want to cover testing
// If wanting to write unit tests for some functions could remove it and with writing new function alone
shim.VerifyObject(ctx, object.ID{})
shim.Time()
shim.ClientOptions()
shim.Refresh(ctx)
shim.ContentInfo(ctx, content.ID{})
shim.PrefetchContents(ctx, []content.ID{}, "hint")
shim.PrefetchObjects(ctx, []object.ID{}, "hint")
shim.UpdateDescription("desc")
shim.NewWriter(ctx, repo.WriteSessionOptions{})
shim.ReplaceManifests(ctx, map[string]string{}, nil)
shim.OnSuccessfulFlush(func(ctx context.Context, w repo.RepositoryWriter) error { return nil })

backupRepo.On("Close", mock.Anything).Return(nil)
NewShimRepo(backupRepo).Close(ctx)

var id udmrepo.ID
backupRepo.On("PutManifest", mock.Anything, mock.Anything).Return(id, nil)
NewShimRepo(backupRepo).PutManifest(ctx, map[string]string{}, nil)

var mf manifest.ID
backupRepo.On("DeleteManifest", mock.Anything, mock.Anything).Return(nil)
NewShimRepo(backupRepo).DeleteManifest(ctx, mf)

backupRepo.On("Flush", mock.Anything).Return(nil)
NewShimRepo(backupRepo).Flush(ctx)

var objID object.ID
backupRepo.On("ConcatenateObjects", mock.Anything, mock.Anything).Return(objID)
NewShimRepo(backupRepo).ConcatenateObjects(ctx, []object.ID{})

backupRepo.On("NewObjectWriter", mock.Anything, mock.Anything).Return(nil)
NewShimRepo(backupRepo).NewObjectWriter(ctx, object.WriterOptions{})
}

func TestOpenObject(t *testing.T) {
tests := []struct {
name string
backupRepo *mocks.BackupRepo
isOpenObjectError bool
isReaderNil bool
}{
{
name: "Success",
backupRepo: func() *mocks.BackupRepo {
backupRepo := &mocks.BackupRepo{}
backupRepo.On("OpenObject", mock.Anything, mock.Anything).Return(&shimObjectReader{}, nil)
return backupRepo
}(),
},
{
name: "Open object error",
backupRepo: func() *mocks.BackupRepo {
backupRepo := &mocks.BackupRepo{}
backupRepo.On("OpenObject", mock.Anything, mock.Anything).Return(&shimObjectReader{}, errors.New("Error open object"))
return backupRepo
}(),
isOpenObjectError: true,
},
{
name: "Get nil reader",
backupRepo: func() *mocks.BackupRepo {
backupRepo := &mocks.BackupRepo{}
backupRepo.On("OpenObject", mock.Anything, mock.Anything).Return(nil, nil)
return backupRepo
}(),
isReaderNil: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
reader, err := NewShimRepo(tc.backupRepo).OpenObject(ctx, object.ID{})
if tc.isOpenObjectError {
assert.Contains(t, err.Error(), "failed to open object")
} else if tc.isReaderNil {
assert.Nil(t, reader)
} else {
assert.NotNil(t, reader)
assert.Nil(t, err)
}
})
}
}

func TestFindManifests(t *testing.T) {
meta := []*udmrepo.ManifestEntryMetadata{}
tests := []struct {
name string
backupRepo *mocks.BackupRepo
isGetManifestError bool
}{
{
name: "Success",
backupRepo: func() *mocks.BackupRepo {
backupRepo := &mocks.BackupRepo{}
backupRepo.On("FindManifests", mock.Anything, mock.Anything).Return(meta, nil)
return backupRepo
}(),
},
{
name: "Failed to find manifest",
isGetManifestError: true,
backupRepo: func() *mocks.BackupRepo {
backupRepo := &mocks.BackupRepo{}
backupRepo.On("FindManifests", mock.Anything, mock.Anything).Return(meta,
errors.New("failed to find manifest"))
return backupRepo
}(),
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
_, err := NewShimRepo(tc.backupRepo).FindManifests(ctx, map[string]string{})
if tc.isGetManifestError {
assert.Contains(t, err.Error(), "failed")
} else {
assert.Nil(t, err)
}
})
}
}

func TestShimObjReader(t *testing.T) {
reader := new(shimObjectReader)
objReader := &mocks.ObjectReader{}
reader.repoReader = objReader
// All below calls put together for the implementation are empty or just very simple, and just want to cover testing
// If wanting to write unit tests for some functions could remove it and with writing new function alone
objReader.On("Seek", mock.Anything, mock.Anything).Return(int64(0), nil)
reader.Seek(int64(0), 0)

objReader.On("Read", mock.Anything).Return(0, nil)
reader.Read(nil)

objReader.On("Close").Return(nil)
reader.Close()

objReader.On("Length").Return(int64(0))
reader.Length()
}

func TestShimObjWriter(t *testing.T) {
writer := new(shimObjectWriter)
objWriter := &mocks.ObjectWriter{}
writer.repoWriter = objWriter
// All below calls put together for the implementation are empty or just very simple, and just want to cover testing
// If wanting to write unit tests for some functions could remove it and with writing new function alone
var id udmrepo.ID
objWriter.On("Checkpoint").Return(id, nil)
writer.Checkpoint()

objWriter.On("Result").Return(id, nil)
writer.Result()

objWriter.On("Write", mock.Anything).Return(0, nil)
writer.Write(nil)

objWriter.On("Close").Return(nil)
writer.Close()
}
11 changes: 7 additions & 4 deletions pkg/uploader/kopia/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ import (
var applyRetentionPolicyFunc = policy.ApplyRetentionPolicy
var saveSnapshotFunc = snapshot.SaveSnapshot
var loadSnapshotFunc = snapshot.LoadSnapshot
var listSnapshotsFunc = snapshot.ListSnapshots
var filesystemEntryFunc = snapshotfs.FilesystemEntryFromIDWithPath
var restoreEntryFunc = restore.Entry

// SnapshotUploader which mainly used for UT test that could overwrite Upload interface
type SnapshotUploader interface {
Expand Down Expand Up @@ -84,7 +87,7 @@ func setupDefaultPolicy() *policy.Tree {
}

// Backup backup specific sourcePath and update progress
func Backup(ctx context.Context, fsUploader *snapshotfs.Uploader, repoWriter repo.RepositoryWriter, sourcePath string,
func Backup(ctx context.Context, fsUploader SnapshotUploader, repoWriter repo.RepositoryWriter, sourcePath string,
forceFull bool, parentSnapshot string, tags map[string]string, log logrus.FieldLogger) (*uploader.SnapshotInfo, bool, error) {
if fsUploader == nil {
return nil, false, errors.New("get empty kopia uploader")
Expand Down Expand Up @@ -238,7 +241,7 @@ func reportSnapshotStatus(manifest *snapshot.Manifest, policyTree *policy.Tree)
// findPreviousSnapshotManifest returns the list of previous snapshots for a given source, including
// last complete snapshot following it.
func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, snapshotTags map[string]string, noLaterThan *fs.UTCTimestamp) ([]*snapshot.Manifest, error) {
man, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
man, err := listSnapshotsFunc(ctx, rep, sourceInfo)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -294,7 +297,7 @@ func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *Progress,

log.Infof("Restore from snapshot %s, description %s, created time %v, tags %v", snapshotID, snapshot.Description, snapshot.EndTime.ToTime(), snapshot.Tags)

rootEntry, err := snapshotfs.FilesystemEntryFromIDWithPath(kopiaCtx, rep, snapshotID, false)
rootEntry, err := filesystemEntryFunc(kopiaCtx, rep, snapshotID, false)
if err != nil {
return 0, 0, errors.Wrapf(err, "Unable to get filesystem entry for snapshot %v", snapshotID)
}
Expand All @@ -317,7 +320,7 @@ func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *Progress,
return 0, 0, errors.Wrap(err, "error to init output")
}

stat, err := restore.Entry(kopiaCtx, rep, output, rootEntry, restore.Options{
stat, err := restoreEntryFunc(kopiaCtx, rep, output, rootEntry, restore.Options{
Parallel: runtime.NumCPU(),
RestoreDirEntryAtDepth: math.MaxInt32,
Cancel: cancleCh,
Expand Down

0 comments on commit caa5c6e

Please sign in to comment.