Skip to content

Commit

Permalink
Merge pull request #377 from kubeslice/add-UTs
Browse files Browse the repository at this point in the history
fix(AM-13331): add UTs for slice & cluster reconciler
  • Loading branch information
mridulgain authored May 7, 2024
2 parents 82803e4 + c9ecf2e commit 0d2fbf3
Show file tree
Hide file tree
Showing 2 changed files with 354 additions and 0 deletions.
198 changes: 198 additions & 0 deletions controllers/slice/reconciler_unit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package slice

import (
"context"
"errors"
"testing"

mevents "github.com/kubeslice/kubeslice-monitoring/pkg/events"
kubeslicev1beta1 "github.com/kubeslice/worker-operator/api/v1beta1"
ossEvents "github.com/kubeslice/worker-operator/events"
utilmock "github.com/kubeslice/worker-operator/pkg/mocks"
corev1 "k8s.io/api/core/v1"

"github.com/stretchr/testify/mock"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type MockClientCall func(*utilmock.MockClient, context.Context)

var testSliceObj = &kubeslicev1beta1.Slice{}

func Test_SliceReconciler(t *testing.T) {
tests := []struct {
name string
// params
ctx context.Context
req reconcile.Request
// mocked calls
loadMocks MockClientCall
// returns
res reconcile.Result
err error
}{
{
"When status.SliceConfig is not set",
context.WithValue(context.Background(), types.NamespacedName{}, testSliceObj),
reconcile.Request{NamespacedName: types.NamespacedName{}},
func(clientMock *utilmock.MockClient, ctx context.Context) {
// mock k8s client calls
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&kubeslicev1beta1.Slice{}),
).Return(nil)
// Get(*context.valueCtx,types.NamespacedName,*v1.Namespace)
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&corev1.Namespace{}),
).Return(nil)
clientMock.On("Update",
mock.IsType(ctx),
mock.IsType(&corev1.Namespace{}),
mock.Anything,
).Return(nil)
// Update(*context.valueCtx,*v1beta1.Slice,[]client.UpdateOption)
clientMock.On("Update",
mock.IsType(ctx),
mock.IsType(&kubeslicev1beta1.Slice{}),
mock.Anything,
).Return(nil)
},
reconcile.Result{},
errors.New("slice not reconciled from hub"),
},
{
"when slice obj is not found",
context.WithValue(context.Background(), types.NamespacedName{}, testSliceObj),
reconcile.Request{
NamespacedName: types.NamespacedName{}},
func(clientMock *utilmock.MockClient, ctx context.Context) {
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&kubeslicev1beta1.Slice{}),
).Return(errors.New("slice not found"))
},
reconcile.Result{},
errors.New("slice not found"),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// init
client := utilmock.NewClient()
test.loadMocks(client, test.ctx)
mockEventRecorder := mevents.NewEventRecorder(client,
runtime.NewScheme(), ossEvents.EventsMap, mevents.EventRecorderOptions{})
reconciler := &SliceReconciler{
Client: client,
EventRecorder: &mockEventRecorder,
}
// call function
result, err := reconciler.Reconcile(test.ctx, test.req)
// assert re
if test.res != result {
t.Error("Expected response :", test.res, " but got ", result)
}
if test.err != nil && err != nil {
if test.err.Error() != err.Error() {
t.Error("Expected error:", test.err, " but got ", err)
}
} else if test.err != err {
t.Error("Expected error:", test.err, " but got ", err)
}
})
}
}

func TestHandleDnsSvc(t *testing.T) {
tests := []struct {
name string
// input parameters
ctx context.Context
sliceObj *kubeslicev1beta1.Slice
// mocked calls
loadMocks MockClientCall
// return values
expectedRequeue bool
expectedRes reconcile.Result
expectedErr error
}{
{
name: "DNS service not found",
ctx: context.WithValue(context.Background(), types.NamespacedName{}, testSliceObj),
sliceObj: testSliceObj,
expectedRequeue: true,
expectedRes: reconcile.Result{},
expectedErr: errors.New("DNS service not found"),
loadMocks: func(mc *utilmock.MockClient, ctx context.Context) {
mc.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&corev1.Service{}),
).Return(errors.New("DNS service not found"))
},
},
{
name: "DNS service found",
ctx: context.WithValue(context.Background(), types.NamespacedName{}, testSliceObj),
sliceObj: testSliceObj,
loadMocks: func(mc *utilmock.MockClient, ctx context.Context) {
mc.On("Get",
mock.Anything,
mock.Anything,
mock.IsType(&corev1.Service{}),
).Return(nil)
mc.On("Get",
mock.Anything,
mock.Anything,
mock.IsType(&kubeslicev1beta1.Slice{}),
).Return(nil)
statusClient := mc.StatusMock
statusClient.On("Update",
mock.Anything,
mock.Anything,
mock.Anything,
).Return(nil)
},
expectedRequeue: true,
expectedRes: reconcile.Result{},
expectedErr: nil,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// init
client := utilmock.NewClient()
mockEventRecorder := mevents.NewEventRecorder(client,
runtime.NewScheme(), ossEvents.EventsMap, mevents.EventRecorderOptions{})
r := &SliceReconciler{
Client: client,
EventRecorder: &mockEventRecorder,
}
test.loadMocks(client, test.ctx)
// call function
requeue, result, err := r.handleDnsSvc(test.ctx, test.sliceObj)
// check return values
if requeue != test.expectedRequeue {
t.Errorf("Expected requeue: %v, got: %v", test.expectedRequeue, result.Requeue)
}
if test.expectedRes != result {
t.Error("Expected response :", test.expectedRes, " but got ", result)
}
if test.expectedErr != nil && err != nil {
if test.expectedErr.Error() != err.Error() {
t.Error("Expected error:", test.expectedErr, " but got ", err)
}
} else if test.expectedErr != err {
t.Error("Expected error:", test.expectedErr, " but got ", err)
}
})
}
}
156 changes: 156 additions & 0 deletions pkg/hub/controllers/cluster/reconciler_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
utilmock "github.com/kubeslice/worker-operator/pkg/mocks"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/mock"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand Down Expand Up @@ -308,3 +309,158 @@ func TestReconcilerToFailWhileCallingCreateDeregisterJob(t *testing.T) {
t.Error("Expected error:", expected.errMsg, " but got ", err.Error())
}
}

type MockClientCall func(*utilmock.MockClient, context.Context)

func TestUpdateNetworkStatus(t *testing.T) {
tests := []struct {
name string
ctx context.Context
cluster *hubv1alpha1.Cluster
loadMocks MockClientCall
err error
}{
{
"successfully update kubeslice network present status",
context.WithValue(context.Background(),
types.NamespacedName{}, testClusterObj),
testClusterObj,
func(clientMock *utilmock.MockClient, ctx context.Context) {
// mock client calls
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&hubv1alpha1.Cluster{}),
).Return(nil)
clientMock.On("List",
mock.IsType(ctx),
mock.IsType(&appsv1.DaemonSetList{}),
mock.Anything,
).Return(nil)
statusClient := clientMock.StatusMock
statusClient.On("Update",
mock.IsType(ctx),
mock.IsType(&hubv1alpha1.Cluster{}),
mock.Anything,
).Return(nil)
},
nil,
},
{
"handle cluster object not found",
context.WithValue(context.Background(),
types.NamespacedName{Namespace: "kube-slice", Name: "kube-slice"}, testClusterObj),
testClusterObj,
func(clientMock *utilmock.MockClient, ctx context.Context) {
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&hubv1alpha1.Cluster{}),
).Return(errors.New("cluster object not found"))
},
errors.New("cluster object not found"),
},
{
"handle failed to update cluster object",
context.WithValue(context.Background(),
types.NamespacedName{Namespace: "kube-slice", Name: "kube-slice"}, testClusterObj),
testClusterObj,
func(clientMock *utilmock.MockClient, ctx context.Context) {
clientMock.On("Get",
mock.IsType(ctx),
mock.IsType(types.NamespacedName{}),
mock.IsType(&hubv1alpha1.Cluster{}),
).Return(nil)
clientMock.On("List",
mock.IsType(ctx),
mock.IsType(&appsv1.DaemonSetList{}),
mock.Anything,
).Return(nil)
statusClient := clientMock.StatusMock
statusClient.On("Update",
mock.IsType(ctx),
mock.IsType(&hubv1alpha1.Cluster{}),
mock.Anything,
).Return(errors.New("failed to update cluster obj"))
},
errors.New("failed to update cluster obj"),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := test.ctx
clientMock := utilmock.NewClient()
r := &Reconciler{
Client: clientMock,
MeshClient: clientMock,
}
test.loadMocks(clientMock, ctx)
err := r.updateNetworkStatus(ctx, test.cluster)
if test.err != nil && err != nil {
if test.err.Error() != err.Error() {
t.Error("Expected error:", test.err, " but got ", err)
}
} else if test.err != err {
t.Error("Expected error:", test.err, " but got ", err)
}
})
}
}

func Test_isNsmInstalled(t *testing.T) {
tests := []struct {
name string
ctx context.Context
loadMocks MockClientCall
result bool
}{
{
"When nsm is installed",
context.WithValue(context.Background(), types.NamespacedName{}, testClusterObj),
func(clientMock *utilmock.MockClient, ctx context.Context) {
clientMock.On("List",
mock.IsType(ctx),
mock.IsType(&appsv1.DaemonSetList{}),
mock.Anything,
).Return(nil).Run(func(args mock.Arguments) {
arg := args.Get(1).(*appsv1.DaemonSetList)
arg.Items = []appsv1.DaemonSet{{
ObjectMeta: metav1.ObjectMeta{
Name: "nsm",
},
}}
})
},
true,
},
{
"When nsm is not installed",
context.WithValue(context.Background(), types.NamespacedName{}, testClusterObj),
func(clientMock *utilmock.MockClient, ctx context.Context) {
clientMock.On("List",
mock.IsType(ctx),
mock.IsType(&appsv1.DaemonSetList{}),
mock.Anything,
).Return(errors.New("failed listing objects"))
},
false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := test.ctx
clientMock := utilmock.NewClient()
r := &Reconciler{
Client: clientMock,
MeshClient: clientMock,
}
test.loadMocks(clientMock, ctx)
result := r.isNsmInstalled(test.ctx)
if result != test.result {
t.Error("Expected result:", test.result, " but got ", result)
}
})
}
}

0 comments on commit 0d2fbf3

Please sign in to comment.