add ut for pkg/repository

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
pull/6432/head
Lyndon-Li 2023-06-29 11:17:40 +08:00
parent de83980a05
commit 38d5003c6b
8 changed files with 1566 additions and 6 deletions

View File

@ -887,3 +887,467 @@ func TestForget(t *testing.T) {
}) })
} }
} }
func TestInitRepo(t *testing.T) {
testCases := []struct {
name string
funcTable localFuncTable
getter *credmock.SecretStore
repoService *reposervicenmocks.BackupRepoService
retFuncInit interface{}
credStoreReturn string
credStoreError error
expectedErr string
}{
{
name: "get repo option fail",
expectedErr: "error to get repo options: error to get repo password: invalid credentials interface",
},
{
name: "repo init fail",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return errors.New("fake-error-1")
},
expectedErr: "error to init backup repo: fake-error-1",
},
{
name: "succeed",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return nil
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
funcTable = tc.funcTable
var secretStore velerocredentials.SecretStore
if tc.getter != nil {
tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError)
secretStore = tc.getter
}
urp := unifiedRepoProvider{
credentialGetter: velerocredentials.CredentialGetter{
FromSecret: secretStore,
},
repoService: tc.repoService,
log: velerotest.NewLogger(),
}
if tc.repoService != nil {
tc.repoService.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(tc.retFuncInit)
}
err := urp.InitRepo(context.Background(), RepoParam{
BackupLocation: &velerov1api.BackupStorageLocation{},
BackupRepo: &velerov1api.BackupRepository{},
})
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestConnectToRepo(t *testing.T) {
testCases := []struct {
name string
funcTable localFuncTable
getter *credmock.SecretStore
repoService *reposervicenmocks.BackupRepoService
retFuncInit interface{}
credStoreReturn string
credStoreError error
expectedErr string
}{
{
name: "get repo option fail",
expectedErr: "error to get repo options: error to get repo password: invalid credentials interface",
},
{
name: "repo init fail",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return errors.New("fake-error-1")
},
expectedErr: "error to connect backup repo: fake-error-1",
},
{
name: "succeed",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return nil
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
funcTable = tc.funcTable
var secretStore velerocredentials.SecretStore
if tc.getter != nil {
tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError)
secretStore = tc.getter
}
urp := unifiedRepoProvider{
credentialGetter: velerocredentials.CredentialGetter{
FromSecret: secretStore,
},
repoService: tc.repoService,
log: velerotest.NewLogger(),
}
if tc.repoService != nil {
tc.repoService.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(tc.retFuncInit)
}
err := urp.ConnectToRepo(context.Background(), RepoParam{
BackupLocation: &velerov1api.BackupStorageLocation{},
BackupRepo: &velerov1api.BackupRepository{},
})
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestBoostRepoConnect(t *testing.T) {
var backupRepo *reposervicenmocks.BackupRepo
testCases := []struct {
name string
funcTable localFuncTable
getter *credmock.SecretStore
repoService *reposervicenmocks.BackupRepoService
backupRepo *reposervicenmocks.BackupRepo
retFuncInit interface{}
retFuncOpen []interface{}
credStoreReturn string
credStoreError error
expectedErr string
}{
{
name: "get repo option fail",
expectedErr: "error to get repo options: error to get repo password: invalid credentials interface",
},
{
name: "repo not opened and connect fail",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncOpen: []interface{}{
func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo {
return backupRepo
},
func(context.Context, udmrepo.RepoOptions) error {
return errors.New("fake-error-1")
},
},
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return errors.New("fake-error-2")
},
expectedErr: "error to connect backup repo: fake-error-2",
},
{
name: "repo not opened and connect succeed",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncOpen: []interface{}{
func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo {
return backupRepo
},
func(context.Context, udmrepo.RepoOptions) error {
return errors.New("fake-error-1")
},
},
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return nil
},
},
{
name: "repo is opened",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
backupRepo: new(reposervicenmocks.BackupRepo),
retFuncOpen: []interface{}{
func(context.Context, udmrepo.RepoOptions) udmrepo.BackupRepo {
return backupRepo
},
func(context.Context, udmrepo.RepoOptions) error {
return nil
},
},
retFuncInit: func(context.Context, udmrepo.RepoOptions, bool) error {
return nil
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
funcTable = tc.funcTable
var secretStore velerocredentials.SecretStore
if tc.getter != nil {
tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError)
secretStore = tc.getter
}
urp := unifiedRepoProvider{
credentialGetter: velerocredentials.CredentialGetter{
FromSecret: secretStore,
},
repoService: tc.repoService,
log: velerotest.NewLogger(),
}
backupRepo = tc.backupRepo
if tc.repoService != nil {
tc.repoService.On("Open", mock.Anything, mock.Anything).Return(tc.retFuncOpen[0], tc.retFuncOpen[1])
tc.repoService.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(tc.retFuncInit)
}
if tc.backupRepo != nil {
backupRepo.On("Close", mock.Anything).Return(nil)
}
err := urp.BoostRepoConnect(context.Background(), RepoParam{
BackupLocation: &velerov1api.BackupStorageLocation{},
BackupRepo: &velerov1api.BackupRepository{},
})
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestPruneRepo(t *testing.T) {
testCases := []struct {
name string
funcTable localFuncTable
getter *credmock.SecretStore
repoService *reposervicenmocks.BackupRepoService
retFuncMaintain interface{}
credStoreReturn string
credStoreError error
expectedErr string
}{
{
name: "get repo option fail",
expectedErr: "error to get repo options: error to get repo password: invalid credentials interface",
},
{
name: "repo maintain fail",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncMaintain: func(context.Context, udmrepo.RepoOptions) error {
return errors.New("fake-error-1")
},
expectedErr: "error to prune backup repo: fake-error-1",
},
{
name: "succeed",
getter: new(credmock.SecretStore),
credStoreReturn: "fake-password",
funcTable: localFuncTable{
getStorageVariables: func(*velerov1api.BackupStorageLocation, string, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, nil
},
},
repoService: new(reposervicenmocks.BackupRepoService),
retFuncMaintain: func(context.Context, udmrepo.RepoOptions) error {
return nil
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
funcTable = tc.funcTable
var secretStore velerocredentials.SecretStore
if tc.getter != nil {
tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError)
secretStore = tc.getter
}
urp := unifiedRepoProvider{
credentialGetter: velerocredentials.CredentialGetter{
FromSecret: secretStore,
},
repoService: tc.repoService,
log: velerotest.NewLogger(),
}
if tc.repoService != nil {
tc.repoService.On("Maintain", mock.Anything, mock.Anything).Return(tc.retFuncMaintain)
}
err := urp.PruneRepo(context.Background(), RepoParam{
BackupLocation: &velerov1api.BackupStorageLocation{},
BackupRepo: &velerov1api.BackupRepository{},
})
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestGetStorageType(t *testing.T) {
testCases := []struct {
name string
backupLocation *velerov1api.BackupStorageLocation
expectedRet string
}{
{
name: "wrong backend type",
backupLocation: &velerov1api.BackupStorageLocation{},
},
{
name: "aws provider",
backupLocation: &velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "velero.io/aws",
},
},
expectedRet: "s3",
},
{
name: "azure provider",
backupLocation: &velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "velero.io/azure",
},
},
expectedRet: "azure",
},
{
name: "gcp provider",
backupLocation: &velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "velero.io/gcp",
},
},
expectedRet: "gcs",
},
{
name: "fs provider",
backupLocation: &velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "velero.io/fs",
},
},
expectedRet: "filesystem",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ret := getStorageType(tc.backupLocation)
assert.Equal(t, tc.expectedRet, ret)
})
}
}

View File

@ -0,0 +1,198 @@
/*
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 backend
import (
"context"
"testing"
"time"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/content"
"github.com/kopia/kopia/repo/encryption"
"github.com/kopia/kopia/repo/format"
"github.com/kopia/kopia/repo/hashing"
"github.com/kopia/kopia/repo/splitter"
"github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
)
func TestSetupNewRepositoryOptions(t *testing.T) {
testCases := []struct {
name string
flags map[string]string
expected repo.NewRepositoryOptions
}{
{
name: "with hash algo",
flags: map[string]string{
udmrepo.StoreOptionGenHashAlgo: "fake-hash",
},
expected: repo.NewRepositoryOptions{
BlockFormat: format.ContentFormat{
Hash: "fake-hash",
Encryption: encryption.DefaultAlgorithm,
},
ObjectFormat: format.ObjectFormat{
Splitter: splitter.DefaultAlgorithm,
},
},
},
{
name: "with encrypt algo",
flags: map[string]string{
udmrepo.StoreOptionGenEncryptAlgo: "fake-encrypt",
},
expected: repo.NewRepositoryOptions{
BlockFormat: format.ContentFormat{
Hash: hashing.DefaultAlgorithm,
Encryption: "fake-encrypt",
},
ObjectFormat: format.ObjectFormat{
Splitter: splitter.DefaultAlgorithm,
},
},
},
{
name: "with splitter algo",
flags: map[string]string{
udmrepo.StoreOptionGenSplitAlgo: "fake-splitter",
},
expected: repo.NewRepositoryOptions{
BlockFormat: format.ContentFormat{
Hash: hashing.DefaultAlgorithm,
Encryption: encryption.DefaultAlgorithm,
},
ObjectFormat: format.ObjectFormat{
Splitter: "fake-splitter",
},
},
},
{
name: "with retention algo",
flags: map[string]string{
udmrepo.StoreOptionGenRetentionMode: "fake-retention-mode",
},
expected: repo.NewRepositoryOptions{
BlockFormat: format.ContentFormat{
Hash: hashing.DefaultAlgorithm,
Encryption: encryption.DefaultAlgorithm,
},
ObjectFormat: format.ObjectFormat{
Splitter: splitter.DefaultAlgorithm,
},
RetentionMode: "fake-retention-mode",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ret := SetupNewRepositoryOptions(context.Background(), tc.flags)
assert.Equal(t, tc.expected, ret)
})
}
}
func TestSetupConnectOptions(t *testing.T) {
defaultCacheOption := content.CachingOptions{
MaxCacheSizeBytes: 2000 << 20,
MaxMetadataCacheSizeBytes: 2000 << 20,
MaxListCacheDuration: content.DurationSeconds(time.Duration(30) * time.Second),
}
testCases := []struct {
name string
repoOptions udmrepo.RepoOptions
expected repo.ConnectOptions
}{
{
name: "with domain",
repoOptions: udmrepo.RepoOptions{
GeneralOptions: map[string]string{
udmrepo.GenOptionOwnerDomain: "fake-domain",
},
},
expected: repo.ConnectOptions{
CachingOptions: defaultCacheOption,
ClientOptions: repo.ClientOptions{
Hostname: "fake-domain",
},
},
},
{
name: "with username",
repoOptions: udmrepo.RepoOptions{
GeneralOptions: map[string]string{
udmrepo.GenOptionOwnerName: "fake-user",
},
},
expected: repo.ConnectOptions{
CachingOptions: defaultCacheOption,
ClientOptions: repo.ClientOptions{
Username: "fake-user",
},
},
},
{
name: "with wrong readonly",
repoOptions: udmrepo.RepoOptions{
GeneralOptions: map[string]string{
udmrepo.StoreOptionGenReadOnly: "fake-bool",
},
},
expected: repo.ConnectOptions{
CachingOptions: defaultCacheOption,
ClientOptions: repo.ClientOptions{},
},
},
{
name: "with correct readonly",
repoOptions: udmrepo.RepoOptions{
GeneralOptions: map[string]string{
udmrepo.StoreOptionGenReadOnly: "true",
},
},
expected: repo.ConnectOptions{
CachingOptions: defaultCacheOption,
ClientOptions: repo.ClientOptions{
ReadOnly: true,
},
},
},
{
name: "with description",
repoOptions: udmrepo.RepoOptions{
Description: "fake-description",
},
expected: repo.ConnectOptions{
CachingOptions: defaultCacheOption,
ClientOptions: repo.ClientOptions{
Description: "fake-description",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ret := SetupConnectOptions(context.Background(), tc.repoOptions)
assert.Equal(t, tc.expected, ret)
})
}
}

View File

@ -0,0 +1,80 @@
/*
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 backend
import (
"context"
"testing"
"github.com/kopia/kopia/repo/blob/filesystem"
"github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
)
func TestFSSetup(t *testing.T) {
testCases := []struct {
name string
flags map[string]string
expectedOptions filesystem.Options
expectedErr string
}{
{
name: "must have fs path",
flags: map[string]string{},
expectedErr: "key " + udmrepo.StoreOptionFsPath + " not found",
},
{
name: "with fs path only",
flags: map[string]string{
udmrepo.StoreOptionFsPath: "fake/path",
},
expectedOptions: filesystem.Options{
Path: "fake/path",
FileMode: 0o600,
DirectoryMode: 0o700,
},
},
{
name: "with prefix",
flags: map[string]string{
udmrepo.StoreOptionFsPath: "fake/path",
udmrepo.StoreOptionPrefix: "fake-prefix",
},
expectedOptions: filesystem.Options{
Path: "fake/path/fake-prefix",
FileMode: 0o600,
DirectoryMode: 0o700,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fsFlags := FsBackend{}
err := fsFlags.Setup(context.Background(), tc.flags)
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expectedOptions, fsFlags.options)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/kopia/kopia/repo/blob/gcs"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
@ -27,9 +28,10 @@ import (
func TestGcsSetup(t *testing.T) { func TestGcsSetup(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
flags map[string]string flags map[string]string
expectedErr string expectedOptions gcs.Options
expectedErr string
}{ }{
{ {
name: "must have bucket name", name: "must have bucket name",
@ -43,6 +45,44 @@ func TestGcsSetup(t *testing.T) {
}, },
expectedErr: "key " + udmrepo.StoreOptionCredentialFile + " not found", expectedErr: "key " + udmrepo.StoreOptionCredentialFile + " not found",
}, },
{
name: "with prefix",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionCredentialFile: "fake-credential",
udmrepo.StoreOptionPrefix: "fake-prefix",
},
expectedOptions: gcs.Options{
BucketName: "fake-bucket",
ServiceAccountCredentialsFile: "fake-credential",
Prefix: "fake-prefix",
},
},
{
name: "with wrong readonly",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionCredentialFile: "fake-credential",
udmrepo.StoreOptionGcsReadonly: "fake-bool",
},
expectedOptions: gcs.Options{
BucketName: "fake-bucket",
ServiceAccountCredentialsFile: "fake-credential",
},
},
{
name: "with correct readonly",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionCredentialFile: "fake-credential",
udmrepo.StoreOptionGcsReadonly: "true",
},
expectedOptions: gcs.Options{
BucketName: "fake-bucket",
ServiceAccountCredentialsFile: "fake-credential",
ReadOnly: true,
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
@ -53,6 +93,7 @@ func TestGcsSetup(t *testing.T) {
if tc.expectedErr == "" { if tc.expectedErr == "" {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, tc.expectedOptions, gcsFlags.options)
} else { } else {
assert.EqualError(t, err, tc.expectedErr) assert.EqualError(t, err, tc.expectedErr)
} }

View File

@ -0,0 +1,101 @@
// Code generated by mockery v2.22.1. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// Reader is an autogenerated mock type for the Reader type
type Reader struct {
mock.Mock
}
// Close provides a mock function with given fields:
func (_m *Reader) Close() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Length provides a mock function with given fields:
func (_m *Reader) Length() int64 {
ret := _m.Called()
var r0 int64
if rf, ok := ret.Get(0).(func() int64); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int64)
}
return r0
}
// Read provides a mock function with given fields: p
func (_m *Reader) Read(p []byte) (int, error) {
ret := _m.Called(p)
var r0 int
var r1 error
if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok {
return rf(p)
}
if rf, ok := ret.Get(0).(func([]byte) int); ok {
r0 = rf(p)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(p)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Seek provides a mock function with given fields: offset, whence
func (_m *Reader) Seek(offset int64, whence int) (int64, error) {
ret := _m.Called(offset, whence)
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(int64, int) (int64, error)); ok {
return rf(offset, whence)
}
if rf, ok := ret.Get(0).(func(int64, int) int64); ok {
r0 = rf(offset, whence)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(int64, int) error); ok {
r1 = rf(offset, whence)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewReader interface {
mock.TestingT
Cleanup(func())
}
// NewReader creates a new instance of Reader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewReader(t mockConstructorTestingTNewReader) *Reader {
mock := &Reader{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,114 @@
// Code generated by mockery v2.22.1. DO NOT EDIT.
package mocks
import (
object "github.com/kopia/kopia/repo/object"
mock "github.com/stretchr/testify/mock"
)
// Writer is an autogenerated mock type for the Writer type
type Writer struct {
mock.Mock
}
// Checkpoint provides a mock function with given fields:
func (_m *Writer) Checkpoint() (object.ID, error) {
ret := _m.Called()
var r0 object.ID
var r1 error
if rf, ok := ret.Get(0).(func() (object.ID, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() object.ID); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(object.ID)
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Close provides a mock function with given fields:
func (_m *Writer) Close() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Result provides a mock function with given fields:
func (_m *Writer) Result() (object.ID, error) {
ret := _m.Called()
var r0 object.ID
var r1 error
if rf, ok := ret.Get(0).(func() (object.ID, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() object.ID); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(object.ID)
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Write provides a mock function with given fields: p
func (_m *Writer) Write(p []byte) (int, error) {
ret := _m.Called(p)
var r0 int
var r1 error
if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok {
return rf(p)
}
if rf, ok := ret.Get(0).(func([]byte) int); ok {
r0 = rf(p)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(p)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewWriter interface {
mock.TestingT
Cleanup(func())
}
// NewWriter creates a new instance of Writer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewWriter(t mockConstructorTestingTNewWriter) *Writer {
mock := &Writer{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/kopia/kopia/repo/blob/s3"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
@ -27,15 +28,91 @@ import (
func TestS3Setup(t *testing.T) { func TestS3Setup(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
flags map[string]string flags map[string]string
expectedErr string expectedOptions s3.Options
expectedErr string
}{ }{
{ {
name: "must have bucket name", name: "must have bucket name",
flags: map[string]string{}, flags: map[string]string{},
expectedErr: "key " + udmrepo.StoreOptionOssBucket + " not found", expectedErr: "key " + udmrepo.StoreOptionOssBucket + " not found",
}, },
{
name: "with bucket only",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
},
},
{
name: "with others",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionS3KeyID: "fake-ak",
udmrepo.StoreOptionS3SecretKey: "fake-sk",
udmrepo.StoreOptionS3Endpoint: "fake-endpoint",
udmrepo.StoreOptionOssRegion: "fake-region",
udmrepo.StoreOptionPrefix: "fake-prefix",
udmrepo.StoreOptionS3Token: "fake-token",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
AccessKeyID: "fake-ak",
SecretAccessKey: "fake-sk",
Endpoint: "fake-endpoint",
Region: "fake-region",
Prefix: "fake-prefix",
SessionToken: "fake-token",
},
},
{
name: "with wrong tls",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionS3DisableTLS: "fake-bool",
udmrepo.StoreOptionS3DisableTLSVerify: "fake-bool",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
},
},
{
name: "with correct tls",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionS3DisableTLS: "true",
udmrepo.StoreOptionS3DisableTLSVerify: "false",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
DoNotUseTLS: true,
DoNotVerifyTLS: false,
},
},
{
name: "with wrong ca",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionS3CustomCA: "fake-base-64",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
},
},
{
name: "with correct ca",
flags: map[string]string{
udmrepo.StoreOptionOssBucket: "fake-bucket",
udmrepo.StoreOptionS3CustomCA: "ZmFrZS1jYQ==",
},
expectedOptions: s3.Options{
BucketName: "fake-bucket",
RootCA: []byte{'f', 'a', 'k', 'e', '-', 'c', 'a'},
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {

View File

@ -18,12 +18,14 @@ package kopialib
import ( import (
"context" "context"
"math"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/repo/object"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@ -713,3 +715,486 @@ func TestFlush(t *testing.T) {
}) })
} }
} }
func TestNewObjectWriter(t *testing.T) {
rawObjWriter := repomocks.NewWriter(t)
testCases := []struct {
name string
rawWriter *repomocks.DirectRepositoryWriter
rawWriterRet object.Writer
expectedRet udmrepo.ObjectWriter
}{
{
name: "raw writer is nil",
},
{
name: "new object writer fail",
rawWriter: repomocks.NewDirectRepositoryWriter(t),
},
{
name: "succeed",
rawWriter: repomocks.NewDirectRepositoryWriter(t),
rawWriterRet: rawObjWriter,
expectedRet: &kopiaObjectWriter{rawWriter: rawObjWriter},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaRepository{}
if tc.rawWriter != nil {
tc.rawWriter.On("NewObjectWriter", mock.Anything, mock.Anything).Return(tc.rawWriterRet)
kr.rawWriter = tc.rawWriter
}
ret := kr.NewObjectWriter(context.Background(), udmrepo.ObjectWriteOptions{})
assert.Equal(t, tc.expectedRet, ret)
})
}
}
func TestUpdateProgress(t *testing.T) {
testCases := []struct {
name string
progress int64
uploaded int64
throttle logThrottle
logMessage string
}{
{
name: "should not output",
throttle: logThrottle{
lastTime: math.MaxInt64,
},
},
{
name: "should output",
progress: 100,
uploaded: 200,
logMessage: "Repo uploaded 300 bytes.",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
logMessage := ""
kr := &kopiaRepository{
logger: velerotest.NewSingleLogger(&logMessage),
throttle: tc.throttle,
uploaded: tc.uploaded,
}
kr.updateProgress(tc.progress)
if len(tc.logMessage) > 0 {
assert.Contains(t, logMessage, tc.logMessage)
} else {
assert.Equal(t, "", logMessage)
}
})
}
}
func TestReaderRead(t *testing.T) {
testCases := []struct {
name string
rawObjReader *repomocks.Reader
rawReaderRetErr error
expectedErr string
}{
{
name: "raw reader is nil",
expectedErr: "object reader is closed or not open",
},
{
name: "raw read fail",
rawObjReader: repomocks.NewReader(t),
rawReaderRetErr: errors.New("fake-read-error"),
expectedErr: "fake-read-error",
},
{
name: "succeed",
rawObjReader: repomocks.NewReader(t),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectReader{}
if tc.rawObjReader != nil {
tc.rawObjReader.On("Read", mock.Anything).Return(0, tc.rawReaderRetErr)
kr.rawReader = tc.rawObjReader
}
_, err := kr.Read(nil)
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestReaderSeek(t *testing.T) {
testCases := []struct {
name string
rawObjReader *repomocks.Reader
rawReaderRet int64
rawReaderRetErr error
expectedRet int64
expectedErr string
}{
{
name: "raw reader is nil",
expectedErr: "object reader is closed or not open",
},
{
name: "raw seek fail",
rawObjReader: repomocks.NewReader(t),
rawReaderRetErr: errors.New("fake-seek-error"),
expectedErr: "fake-seek-error",
},
{
name: "succeed",
rawObjReader: repomocks.NewReader(t),
rawReaderRet: 100,
expectedRet: 100,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectReader{}
if tc.rawObjReader != nil {
tc.rawObjReader.On("Seek", mock.Anything, mock.Anything).Return(tc.rawReaderRet, tc.rawReaderRetErr)
kr.rawReader = tc.rawObjReader
}
ret, err := kr.Seek(0, 0)
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expectedRet, ret)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestReaderClose(t *testing.T) {
testCases := []struct {
name string
rawObjReader *repomocks.Reader
rawReaderRetErr error
expectedErr string
}{
{
name: "raw reader is nil",
},
{
name: "raw close fail",
rawObjReader: repomocks.NewReader(t),
rawReaderRetErr: errors.New("fake-close-error"),
expectedErr: "fake-close-error",
},
{
name: "succeed",
rawObjReader: repomocks.NewReader(t),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectReader{}
if tc.rawObjReader != nil {
tc.rawObjReader.On("Close").Return(tc.rawReaderRetErr)
kr.rawReader = tc.rawObjReader
}
err := kr.Close()
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestReaderLength(t *testing.T) {
testCases := []struct {
name string
rawObjReader *repomocks.Reader
rawReaderRet int64
expectedRet int64
}{
{
name: "raw reader is nil",
expectedRet: -1,
},
{
name: "raw length fail",
rawObjReader: repomocks.NewReader(t),
rawReaderRet: 0,
expectedRet: 0,
},
{
name: "succeed",
rawObjReader: repomocks.NewReader(t),
rawReaderRet: 200,
expectedRet: 200,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectReader{}
if tc.rawObjReader != nil {
tc.rawObjReader.On("Length").Return(tc.rawReaderRet)
kr.rawReader = tc.rawObjReader
}
ret := kr.Length()
assert.Equal(t, tc.expectedRet, ret)
})
}
}
func TestWriterWrite(t *testing.T) {
testCases := []struct {
name string
rawObjWriter *repomocks.Writer
rawWrtierRet int
rawWriterRetErr error
expectedRet int
expectedErr string
}{
{
name: "raw writer is nil",
expectedErr: "object writer is closed or not open",
},
{
name: "raw read fail",
rawObjWriter: repomocks.NewWriter(t),
rawWriterRetErr: errors.New("fake-write-error"),
expectedErr: "fake-write-error",
},
{
name: "succeed",
rawObjWriter: repomocks.NewWriter(t),
rawWrtierRet: 200,
expectedRet: 200,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectWriter{}
if tc.rawObjWriter != nil {
tc.rawObjWriter.On("Write", mock.Anything).Return(tc.rawWrtierRet, tc.rawWriterRetErr)
kr.rawWriter = tc.rawObjWriter
}
ret, err := kr.Write(nil)
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expectedRet, ret)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestWriterCheckpoint(t *testing.T) {
testCases := []struct {
name string
rawObjWriter *repomocks.Writer
rawWrtierRet object.ID
rawWriterRetErr error
expectedRet udmrepo.ID
expectedErr string
}{
{
name: "raw writer is nil",
expectedErr: "object writer is closed or not open",
},
{
name: "raw checkpoint fail",
rawObjWriter: repomocks.NewWriter(t),
rawWriterRetErr: errors.New("fake-checkpoint-error"),
expectedErr: "error to checkpoint object: fake-checkpoint-error",
},
{
name: "succeed",
rawObjWriter: repomocks.NewWriter(t),
rawWrtierRet: object.ID{},
expectedRet: udmrepo.ID(""),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectWriter{}
if tc.rawObjWriter != nil {
tc.rawObjWriter.On("Checkpoint").Return(tc.rawWrtierRet, tc.rawWriterRetErr)
kr.rawWriter = tc.rawObjWriter
}
ret, err := kr.Checkpoint()
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expectedRet, ret)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestWriterResult(t *testing.T) {
testCases := []struct {
name string
rawObjWriter *repomocks.Writer
rawWrtierRet object.ID
rawWriterRetErr error
expectedRet udmrepo.ID
expectedErr string
}{
{
name: "raw writer is nil",
expectedErr: "object writer is closed or not open",
},
{
name: "raw result fail",
rawObjWriter: repomocks.NewWriter(t),
rawWriterRetErr: errors.New("fake-result-error"),
expectedErr: "error to wait object: fake-result-error",
},
{
name: "succeed",
rawObjWriter: repomocks.NewWriter(t),
rawWrtierRet: object.ID{},
expectedRet: udmrepo.ID(""),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectWriter{}
if tc.rawObjWriter != nil {
tc.rawObjWriter.On("Result").Return(tc.rawWrtierRet, tc.rawWriterRetErr)
kr.rawWriter = tc.rawObjWriter
}
ret, err := kr.Result()
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expectedRet, ret)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestWriterClose(t *testing.T) {
testCases := []struct {
name string
rawObjWriter *repomocks.Writer
rawWriterRetErr error
expectedErr string
}{
{
name: "raw writer is nil",
},
{
name: "raw close fail",
rawObjWriter: repomocks.NewWriter(t),
rawWriterRetErr: errors.New("fake-close-error"),
expectedErr: "fake-close-error",
},
{
name: "succeed",
rawObjWriter: repomocks.NewWriter(t),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaObjectWriter{}
if tc.rawObjWriter != nil {
tc.rawObjWriter.On("Close").Return(tc.rawWriterRetErr)
kr.rawWriter = tc.rawObjWriter
}
err := kr.Close()
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestMaintainProgress(t *testing.T) {
testCases := []struct {
name string
progress int64
uploaded int64
throttle logThrottle
logMessage string
}{
{
name: "should not output",
throttle: logThrottle{
lastTime: math.MaxInt64,
},
},
{
name: "should output",
progress: 100,
uploaded: 200,
logMessage: "Repo maintenance uploaded 300 bytes.",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
logMessage := ""
km := &kopiaMaintenance{
logger: velerotest.NewSingleLogger(&logMessage),
throttle: tc.throttle,
uploaded: tc.uploaded,
}
km.maintainProgress(tc.progress)
if len(tc.logMessage) > 0 {
assert.Contains(t, logMessage, tc.logMessage)
} else {
assert.Equal(t, "", logMessage)
}
})
}
}