Improve the unit test coverage of pkg/cmd/server package (#6342)

Improve the unit test coverage of pkg/cmd/server package

Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
This commit is contained in:
Wenkai Yin(尹文开)
2023-06-05 23:15:24 +08:00
committed by GitHub
parent ebe064f693
commit 914ccdf4c6
6 changed files with 567 additions and 0 deletions

View File

@@ -353,3 +353,6 @@ gen-docs:
.PHONY: test-e2e
test-e2e: local
$(MAKE) -e VERSION=$(VERSION) -C test/e2e run
go-generate:
go generate ./pkg/...

View File

@@ -36,6 +36,8 @@ import (
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
)
//go:generate mockery --name Factory
// Factory knows how to create a VeleroClient and Kubernetes client.
type Factory interface {
// BindFlags binds common flags (--kubeconfig, --namespace) to the passed-in FlagSet.

202
pkg/client/mocks/Factory.go Normal file
View File

@@ -0,0 +1,202 @@
// Code generated by mockery v2.28.1. DO NOT EDIT.
package mocks
import (
dynamic "k8s.io/client-go/dynamic"
kubernetes "k8s.io/client-go/kubernetes"
mock "github.com/stretchr/testify/mock"
pflag "github.com/spf13/pflag"
pkgclient "sigs.k8s.io/controller-runtime/pkg/client"
rest "k8s.io/client-go/rest"
versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
)
// Factory is an autogenerated mock type for the Factory type
type Factory struct {
mock.Mock
}
// BindFlags provides a mock function with given fields: flags
func (_m *Factory) BindFlags(flags *pflag.FlagSet) {
_m.Called(flags)
}
// Client provides a mock function with given fields:
func (_m *Factory) Client() (versioned.Interface, error) {
ret := _m.Called()
var r0 versioned.Interface
var r1 error
if rf, ok := ret.Get(0).(func() (versioned.Interface, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() versioned.Interface); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(versioned.Interface)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ClientConfig provides a mock function with given fields:
func (_m *Factory) ClientConfig() (*rest.Config, error) {
ret := _m.Called()
var r0 *rest.Config
var r1 error
if rf, ok := ret.Get(0).(func() (*rest.Config, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() *rest.Config); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*rest.Config)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DynamicClient provides a mock function with given fields:
func (_m *Factory) DynamicClient() (dynamic.Interface, error) {
ret := _m.Called()
var r0 dynamic.Interface
var r1 error
if rf, ok := ret.Get(0).(func() (dynamic.Interface, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() dynamic.Interface); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(dynamic.Interface)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// KubeClient provides a mock function with given fields:
func (_m *Factory) KubeClient() (kubernetes.Interface, error) {
ret := _m.Called()
var r0 kubernetes.Interface
var r1 error
if rf, ok := ret.Get(0).(func() (kubernetes.Interface, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() kubernetes.Interface); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(kubernetes.Interface)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// KubebuilderClient provides a mock function with given fields:
func (_m *Factory) KubebuilderClient() (pkgclient.Client, error) {
ret := _m.Called()
var r0 pkgclient.Client
var r1 error
if rf, ok := ret.Get(0).(func() (pkgclient.Client, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() pkgclient.Client); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(pkgclient.Client)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Namespace provides a mock function with given fields:
func (_m *Factory) Namespace() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// SetBasename provides a mock function with given fields: _a0
func (_m *Factory) SetBasename(_a0 string) {
_m.Called(_a0)
}
// SetClientBurst provides a mock function with given fields: _a0
func (_m *Factory) SetClientBurst(_a0 int) {
_m.Called(_a0)
}
// SetClientQPS provides a mock function with given fields: _a0
func (_m *Factory) SetClientQPS(_a0 float32) {
_m.Called(_a0)
}
type mockConstructorTestingTNewFactory interface {
mock.TestingT
Cleanup(func())
}
// NewFactory creates a new instance of Factory. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFactory(t mockConstructorTestingTNewFactory) *Factory {
mock := &Factory{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -17,14 +17,28 @@ limitations under the License.
package server
import (
"context"
"errors"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubefake "k8s.io/client-go/kubernetes/fake"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client/mocks"
"github.com/vmware-tanzu/velero/pkg/controller"
discovery_mocks "github.com/vmware-tanzu/velero/pkg/discovery/mocks"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/uploader"
)
func TestVeleroResourcesExist(t *testing.T) {
@@ -151,3 +165,191 @@ func TestRemoveControllers(t *testing.T) {
})
}
}
func TestNewCommand(t *testing.T) {
assert.NotNil(t, NewCommand(nil))
}
func Test_newServer(t *testing.T) {
factory := &mocks.Factory{}
logger := logrus.New()
// invalid uploader type
_, err := newServer(factory, serverConfig{
uploaderType: "invalid",
}, logger)
assert.NotNil(t, err)
// invalid clientQPS
_, err = newServer(factory, serverConfig{
uploaderType: uploader.KopiaType,
clientQPS: -1,
}, logger)
assert.NotNil(t, err)
// invalid clientBurst
factory.On("SetClientQPS", mock.Anything).Return()
_, err = newServer(factory, serverConfig{
uploaderType: uploader.KopiaType,
clientQPS: 1,
clientBurst: -1,
}, logger)
assert.NotNil(t, err)
// invalid clientBclientPageSizeurst
factory.On("SetClientQPS", mock.Anything).Return().
On("SetClientBurst", mock.Anything).Return()
_, err = newServer(factory, serverConfig{
uploaderType: uploader.KopiaType,
clientQPS: 1,
clientBurst: 1,
clientPageSize: -1,
}, logger)
assert.NotNil(t, err)
// got error when creating client
factory.On("SetClientQPS", mock.Anything).Return().
On("SetClientBurst", mock.Anything).Return().
On("KubeClient").Return(nil, nil).
On("Client").Return(nil, nil).
On("DynamicClient").Return(nil, errors.New("error"))
_, err = newServer(factory, serverConfig{
uploaderType: uploader.KopiaType,
clientQPS: 1,
clientBurst: 1,
clientPageSize: 100,
}, logger)
assert.NotNil(t, err)
}
func Test_namespaceExists(t *testing.T) {
client := kubefake.NewSimpleClientset(&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
},
})
server := &server{
kubeClient: client,
logger: logrus.New(),
}
// namespace doesn't exist
assert.NotNil(t, server.namespaceExists("not-exist"))
// namespace exists
assert.Nil(t, server.namespaceExists("velero"))
}
func Test_veleroResourcesExist(t *testing.T) {
helper := &discovery_mocks.Helper{}
server := &server{
discoveryHelper: helper,
logger: logrus.New(),
}
// velero resources don't exist
helper.On("Resources").Return(nil)
assert.NotNil(t, server.veleroResourcesExist())
// velero resources exist
helper.On("Resources").Unset()
helper.On("Resources").Return([]*metav1.APIResourceList{
{
GroupVersion: velerov1api.SchemeGroupVersion.String(),
APIResources: []metav1.APIResource{
{Kind: "Backup"},
{Kind: "Restore"},
{Kind: "Schedule"},
{Kind: "DownloadRequest"},
{Kind: "DeleteBackupRequest"},
{Kind: "PodVolumeBackup"},
{Kind: "PodVolumeRestore"},
{Kind: "BackupRepository"},
{Kind: "BackupStorageLocation"},
{Kind: "VolumeSnapshotLocation"},
{Kind: "ServerStatusRequest"},
},
},
})
assert.Nil(t, server.veleroResourcesExist())
}
func Test_markInProgressBackupsFailed(t *testing.T) {
scheme := runtime.NewScheme()
velerov1api.AddToScheme(scheme)
c := fake.NewClientBuilder().
WithScheme(scheme).
WithLists(&velerov1api.BackupList{
Items: []velerov1api.Backup{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "velero",
Name: "backup01",
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseInProgress,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "velero",
Name: "backup02",
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted,
},
},
},
}).
Build()
markInProgressBackupsFailed(context.Background(), c, "velero", logrus.New())
backup01 := &velerov1api.Backup{}
require.Nil(t, c.Get(context.Background(), client.ObjectKey{Namespace: "velero", Name: "backup01"}, backup01))
assert.Equal(t, velerov1api.BackupPhaseFailed, backup01.Status.Phase)
backup02 := &velerov1api.Backup{}
require.Nil(t, c.Get(context.Background(), client.ObjectKey{Namespace: "velero", Name: "backup02"}, backup02))
assert.Equal(t, velerov1api.BackupPhaseCompleted, backup02.Status.Phase)
}
func Test_markInProgressRestoresFailed(t *testing.T) {
scheme := runtime.NewScheme()
velerov1api.AddToScheme(scheme)
c := fake.NewClientBuilder().
WithScheme(scheme).
WithLists(&velerov1api.RestoreList{
Items: []velerov1api.Restore{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "velero",
Name: "restore01",
},
Status: velerov1api.RestoreStatus{
Phase: velerov1api.RestorePhaseInProgress,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "velero",
Name: "restore02",
},
Status: velerov1api.RestoreStatus{
Phase: velerov1api.RestorePhaseCompleted,
},
},
},
}).
Build()
markInProgressRestoresFailed(context.Background(), c, "velero", logrus.New())
restore01 := &velerov1api.Restore{}
require.Nil(t, c.Get(context.Background(), client.ObjectKey{Namespace: "velero", Name: "restore01"}, restore01))
assert.Equal(t, velerov1api.RestorePhaseFailed, restore01.Status.Phase)
restore02 := &velerov1api.Restore{}
require.Nil(t, c.Get(context.Background(), client.ObjectKey{Namespace: "velero", Name: "restore02"}, restore02))
assert.Equal(t, velerov1api.RestorePhaseCompleted, restore02.Status.Phase)
}

View File

@@ -34,6 +34,8 @@ import (
kcmdutil "github.com/vmware-tanzu/velero/third_party/kubernetes/pkg/kubectl/cmd/util"
)
//go:generate mockery --name Helper
// Helper exposes functions for interacting with the Kubernetes discovery
// API.
type Helper interface {

View File

@@ -0,0 +1,156 @@
// Code generated by mockery v2.28.1. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
schema "k8s.io/apimachinery/pkg/runtime/schema"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
version "k8s.io/apimachinery/pkg/version"
)
// Helper is an autogenerated mock type for the Helper type
type Helper struct {
mock.Mock
}
// APIGroups provides a mock function with given fields:
func (_m *Helper) APIGroups() []v1.APIGroup {
ret := _m.Called()
var r0 []v1.APIGroup
if rf, ok := ret.Get(0).(func() []v1.APIGroup); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]v1.APIGroup)
}
}
return r0
}
// KindFor provides a mock function with given fields: input
func (_m *Helper) KindFor(input schema.GroupVersionKind) (schema.GroupVersionResource, v1.APIResource, error) {
ret := _m.Called(input)
var r0 schema.GroupVersionResource
var r1 v1.APIResource
var r2 error
if rf, ok := ret.Get(0).(func(schema.GroupVersionKind) (schema.GroupVersionResource, v1.APIResource, error)); ok {
return rf(input)
}
if rf, ok := ret.Get(0).(func(schema.GroupVersionKind) schema.GroupVersionResource); ok {
r0 = rf(input)
} else {
r0 = ret.Get(0).(schema.GroupVersionResource)
}
if rf, ok := ret.Get(1).(func(schema.GroupVersionKind) v1.APIResource); ok {
r1 = rf(input)
} else {
r1 = ret.Get(1).(v1.APIResource)
}
if rf, ok := ret.Get(2).(func(schema.GroupVersionKind) error); ok {
r2 = rf(input)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// Refresh provides a mock function with given fields:
func (_m *Helper) Refresh() 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
}
// ResourceFor provides a mock function with given fields: input
func (_m *Helper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, v1.APIResource, error) {
ret := _m.Called(input)
var r0 schema.GroupVersionResource
var r1 v1.APIResource
var r2 error
if rf, ok := ret.Get(0).(func(schema.GroupVersionResource) (schema.GroupVersionResource, v1.APIResource, error)); ok {
return rf(input)
}
if rf, ok := ret.Get(0).(func(schema.GroupVersionResource) schema.GroupVersionResource); ok {
r0 = rf(input)
} else {
r0 = ret.Get(0).(schema.GroupVersionResource)
}
if rf, ok := ret.Get(1).(func(schema.GroupVersionResource) v1.APIResource); ok {
r1 = rf(input)
} else {
r1 = ret.Get(1).(v1.APIResource)
}
if rf, ok := ret.Get(2).(func(schema.GroupVersionResource) error); ok {
r2 = rf(input)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// Resources provides a mock function with given fields:
func (_m *Helper) Resources() []*v1.APIResourceList {
ret := _m.Called()
var r0 []*v1.APIResourceList
if rf, ok := ret.Get(0).(func() []*v1.APIResourceList); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*v1.APIResourceList)
}
}
return r0
}
// ServerVersion provides a mock function with given fields:
func (_m *Helper) ServerVersion() *version.Info {
ret := _m.Called()
var r0 *version.Info
if rf, ok := ret.Get(0).(func() *version.Info); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*version.Info)
}
}
return r0
}
type mockConstructorTestingTNewHelper interface {
mock.TestingT
Cleanup(func())
}
// NewHelper creates a new instance of Helper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewHelper(t mockConstructorTestingTNewHelper) *Helper {
mock := &Helper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}