From 4f17294b19e74d3d8fbbd1650dfda81c4f79aa9d Mon Sep 17 00:00:00 2001 From: Mridul Gain Date: Fri, 12 Apr 2024 10:34:56 +0530 Subject: [PATCH] add UTs for slice reconciler Signed-off-by: Mridul Gain --- controllers/slice/reconciler_unit_test.go | 198 ++++++++++++++++++ .../cluster/reconciler_unit_test.go | 156 ++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 controllers/slice/reconciler_unit_test.go diff --git a/controllers/slice/reconciler_unit_test.go b/controllers/slice/reconciler_unit_test.go new file mode 100644 index 000000000..4202e77e0 --- /dev/null +++ b/controllers/slice/reconciler_unit_test.go @@ -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) + } + }) + } +} diff --git a/pkg/hub/controllers/cluster/reconciler_unit_test.go b/pkg/hub/controllers/cluster/reconciler_unit_test.go index 6a98a5371..595341eb2 100644 --- a/pkg/hub/controllers/cluster/reconciler_unit_test.go +++ b/pkg/hub/controllers/cluster/reconciler_unit_test.go @@ -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" @@ -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) + } + }) + } +}