From 21930bd90dff7e974300d1cff37194f3e7add37d Mon Sep 17 00:00:00 2001 From: Yi Tao Date: Wed, 11 Sep 2024 14:36:17 +0800 Subject: [PATCH 1/3] set mock SDK to test creating control plane --- controller/konnect/ops/sdkfactory_mock.go | 20 +++++-- .../reconciler_setupwithmanager_test.go | 57 ++++++++++++++++--- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/controller/konnect/ops/sdkfactory_mock.go b/controller/konnect/ops/sdkfactory_mock.go index 80487d8eb..a4c49b0ff 100644 --- a/controller/konnect/ops/sdkfactory_mock.go +++ b/controller/konnect/ops/sdkfactory_mock.go @@ -12,6 +12,18 @@ type MockSDKWrapper struct { var _ SDKWrapper = MockSDKWrapper{} +func NewMockSDKWrapper() *MockSDKWrapper { + return &MockSDKWrapper{ + ControlPlaneSDK: &MockControlPlaneSDK{}, + ServicesSDK: &MockServicesSDK{}, + RoutesSDK: &MockRoutesSDK{}, + ConsumersSDK: &MockConsumersSDK{}, + ConsumerGroupSDK: &MockConsumerGroupSDK{}, + PluginSDK: &MockPluginSDK{}, + MeSDK: &MockMeSDK{}, + } +} + func (m MockSDKWrapper) GetControlPlaneSDK() ControlPlaneSDK { return m.ControlPlaneSDK } @@ -41,16 +53,16 @@ func (m MockSDKWrapper) GetMeSDK() MeSDK { } type MockSDKFactory struct { - w *MockSDKWrapper + Wapper *MockSDKWrapper } var _ SDKFactory = MockSDKFactory{} func (m MockSDKFactory) NewKonnectSDK(_ string, _ SDKToken) SDKWrapper { - if m.w != nil { - return *m.w + if m.Wapper != nil { + return *m.Wapper } - return &MockSDKWrapper{ + return MockSDKWrapper{ ControlPlaneSDK: &MockControlPlaneSDK{}, ServicesSDK: &MockServicesSDK{}, RoutesSDK: &MockRoutesSDK{}, diff --git a/test/envtest/reconciler_setupwithmanager_test.go b/test/envtest/reconciler_setupwithmanager_test.go index f47a2391a..f393cb345 100644 --- a/test/envtest/reconciler_setupwithmanager_test.go +++ b/test/envtest/reconciler_setupwithmanager_test.go @@ -5,7 +5,11 @@ import ( "testing" "time" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + "github.com/go-logr/logr/testr" "github.com/samber/lo" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -42,12 +46,13 @@ func TestNewKonnectEntityReconciler(t *testing.T) { type konnectEntityReconcilerTestCase struct { name string objectOps func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) + mockSDKSettings func(t *testing.T, m *ops.MockSDKWrapper, ns *corev1.Namespace) eventuallyPredicate func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) bool } var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ { - name: "should resolve auth", + name: "should create control plane successfully", objectOps: func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) { auth := &konnectv1alpha1.KonnectAPIAuthConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -55,11 +60,28 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ Namespace: ns.Name, }, Spec: konnectv1alpha1.KonnectAPIAuthConfigurationSpec{ - Type: konnectv1alpha1.KonnectAPIAuthTypeToken, - Token: "kpat_test", + Type: konnectv1alpha1.KonnectAPIAuthTypeToken, + Token: "kpat_test", + ServerURL: "127.0.0.1", }, } require.NoError(t, cl.Create(ctx, auth)) + // We cannot create KonnectAPIAuthConfiguration with specified status, so we update the status after creating it. + auth.Status = konnectv1alpha1.KonnectAPIAuthConfigurationStatus{ + OrganizationID: "org-1", + ServerURL: "127.0.0.1", + Conditions: []metav1.Condition{ + { + Type: conditions.KonnectEntityAPIAuthConfigurationValidConditionType, + ObservedGeneration: auth.GetGeneration(), + Status: metav1.ConditionTrue, + Reason: conditions.KonnectEntityAPIAuthConfigurationReasonValid, + LastTransitionTime: metav1.Now(), + }, + }, + } + require.NoError(t, cl.Status().Update(ctx, auth)) + // Create KonnectGatewayControlPlane. cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", @@ -71,10 +93,24 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ Name: "auth", }, }, + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + Description: lo.ToPtr("test control plane 1"), + }, }, } require.NoError(t, cl.Create(ctx, cp)) }, + mockSDKSettings: func(t *testing.T, m *ops.MockSDKWrapper, ns *corev1.Namespace) { + m.ControlPlaneSDK.EXPECT().CreateControlPlane(mock.Anything, sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + Description: lo.ToPtr("test control plane 1"), + }).Return(&sdkkonnectops.CreateControlPlaneResponse{ + ControlPlane: &sdkkonnectcomp.ControlPlane{ + ID: "12345", + }, + }, nil) + }, eventuallyPredicate: func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) bool { cp := &konnectv1alpha1.KonnectGatewayControlPlane{} require.NoError(t, @@ -86,10 +122,10 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ cp, ), ) - // TODO: setup mock Konnect SDK and verify that Konnect CP is "Created". - return lo.ContainsBy(cp.Status.Conditions, func(condition metav1.Condition) bool { - return condition.Type == conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType && condition.Status == metav1.ConditionTrue - }) + return cp.Status.ID == "12345" && // Call of creating control plane should be OK + lo.ContainsBy(cp.Status.Conditions, func(condition metav1.Condition) bool { // "Programmed" condition should be true + return condition.Type == conditions.KonnectEntityProgrammedConditionType && condition.Status == metav1.ConditionTrue + }) }, }, } @@ -116,6 +152,7 @@ func testNewKonnectEntityReconciler[ mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Get(), + Logger: testr.New(t).V(1), Metrics: metricsserver.Options{ // We do not need metrics server so we set BindAddress to 0 to disable it. BindAddress: "0", @@ -124,7 +161,10 @@ func testNewKonnectEntityReconciler[ require.NoError(t, err) cl := mgr.GetClient() - reconciler := konnect.NewKonnectEntityReconciler[T, TEnt](&ops.MockSDKFactory{}, false, cl) + wrapper := ops.NewMockSDKWrapper() + reconciler := konnect.NewKonnectEntityReconciler[T, TEnt](&ops.MockSDKFactory{ + Wapper: wrapper, + }, false, cl) require.NoError(t, reconciler.SetupWithManager(ctx, mgr)) ns := &corev1.Namespace{ @@ -148,6 +188,7 @@ func testNewKonnectEntityReconciler[ for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.objectOps(ctx, t, cl, ns) + tc.mockSDKSettings(t, wrapper, ns) require.Eventually(t, func() bool { return tc.eventuallyPredicate(ctx, t, cl, ns) }, wait, tick) From 80f261bf6e87bcac5230f2df0aed096ce2d66a00 Mon Sep 17 00:00:00 2001 From: Yi Tao Date: Wed, 11 Sep 2024 16:43:39 +0800 Subject: [PATCH 2/3] rename wrapper to sdk --- controller/konnect/ops/sdkfactory_mock.go | 6 +++--- test/envtest/reconciler_setupwithmanager_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/controller/konnect/ops/sdkfactory_mock.go b/controller/konnect/ops/sdkfactory_mock.go index a4c49b0ff..49b8b1be5 100644 --- a/controller/konnect/ops/sdkfactory_mock.go +++ b/controller/konnect/ops/sdkfactory_mock.go @@ -53,14 +53,14 @@ func (m MockSDKWrapper) GetMeSDK() MeSDK { } type MockSDKFactory struct { - Wapper *MockSDKWrapper + SDK *MockSDKWrapper } var _ SDKFactory = MockSDKFactory{} func (m MockSDKFactory) NewKonnectSDK(_ string, _ SDKToken) SDKWrapper { - if m.Wapper != nil { - return *m.Wapper + if m.SDK != nil { + return *m.SDK } return MockSDKWrapper{ ControlPlaneSDK: &MockControlPlaneSDK{}, diff --git a/test/envtest/reconciler_setupwithmanager_test.go b/test/envtest/reconciler_setupwithmanager_test.go index f393cb345..9dd1261a5 100644 --- a/test/envtest/reconciler_setupwithmanager_test.go +++ b/test/envtest/reconciler_setupwithmanager_test.go @@ -46,7 +46,7 @@ func TestNewKonnectEntityReconciler(t *testing.T) { type konnectEntityReconcilerTestCase struct { name string objectOps func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) - mockSDKSettings func(t *testing.T, m *ops.MockSDKWrapper, ns *corev1.Namespace) + mockExpectations func(sdk *ops.MockSDKWrapper, ns *corev1.Namespace) eventuallyPredicate func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) bool } @@ -101,8 +101,8 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ } require.NoError(t, cl.Create(ctx, cp)) }, - mockSDKSettings: func(t *testing.T, m *ops.MockSDKWrapper, ns *corev1.Namespace) { - m.ControlPlaneSDK.EXPECT().CreateControlPlane(mock.Anything, sdkkonnectcomp.CreateControlPlaneRequest{ + mockExpectations: func(sdk *ops.MockSDKWrapper, ns *corev1.Namespace) { + sdk.ControlPlaneSDK.EXPECT().CreateControlPlane(mock.Anything, sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", Description: lo.ToPtr("test control plane 1"), }).Return(&sdkkonnectops.CreateControlPlaneResponse{ @@ -161,9 +161,9 @@ func testNewKonnectEntityReconciler[ require.NoError(t, err) cl := mgr.GetClient() - wrapper := ops.NewMockSDKWrapper() + sdk := ops.NewMockSDKWrapper() reconciler := konnect.NewKonnectEntityReconciler[T, TEnt](&ops.MockSDKFactory{ - Wapper: wrapper, + SDK: sdk, }, false, cl) require.NoError(t, reconciler.SetupWithManager(ctx, mgr)) @@ -188,7 +188,7 @@ func testNewKonnectEntityReconciler[ for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.objectOps(ctx, t, cl, ns) - tc.mockSDKSettings(t, wrapper, ns) + tc.mockExpectations(sdk, ns) require.Eventually(t, func() bool { return tc.eventuallyPredicate(ctx, t, cl, ns) }, wait, tick) From 6ac573026f512951c875746c2ecf8ec69cd92302 Mon Sep 17 00:00:00 2001 From: Yi Tao Date: Wed, 11 Sep 2024 18:10:18 +0800 Subject: [PATCH 3/3] add assertExpctations to tests --- controller/konnect/ops/sdkfactory_mock.go | 10 +--------- test/envtest/reconciler_setupwithmanager_test.go | 11 ++++++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/controller/konnect/ops/sdkfactory_mock.go b/controller/konnect/ops/sdkfactory_mock.go index 49b8b1be5..7b4d6f259 100644 --- a/controller/konnect/ops/sdkfactory_mock.go +++ b/controller/konnect/ops/sdkfactory_mock.go @@ -62,13 +62,5 @@ func (m MockSDKFactory) NewKonnectSDK(_ string, _ SDKToken) SDKWrapper { if m.SDK != nil { return *m.SDK } - return MockSDKWrapper{ - ControlPlaneSDK: &MockControlPlaneSDK{}, - ServicesSDK: &MockServicesSDK{}, - RoutesSDK: &MockRoutesSDK{}, - ConsumersSDK: &MockConsumersSDK{}, - ConsumerGroupSDK: &MockConsumerGroupSDK{}, - PluginSDK: &MockPluginSDK{}, - MeSDK: &MockMeSDK{}, - } + return NewMockSDKWrapper() } diff --git a/test/envtest/reconciler_setupwithmanager_test.go b/test/envtest/reconciler_setupwithmanager_test.go index 9dd1261a5..4ff39b947 100644 --- a/test/envtest/reconciler_setupwithmanager_test.go +++ b/test/envtest/reconciler_setupwithmanager_test.go @@ -46,7 +46,7 @@ func TestNewKonnectEntityReconciler(t *testing.T) { type konnectEntityReconcilerTestCase struct { name string objectOps func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) - mockExpectations func(sdk *ops.MockSDKWrapper, ns *corev1.Namespace) + mockExpectations func(t *testing.T, sdk *ops.MockSDKWrapper, ns *corev1.Namespace) eventuallyPredicate func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) bool } @@ -101,7 +101,7 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ } require.NoError(t, cl.Create(ctx, cp)) }, - mockExpectations: func(sdk *ops.MockSDKWrapper, ns *corev1.Namespace) { + mockExpectations: func(t *testing.T, sdk *ops.MockSDKWrapper, ns *corev1.Namespace) { sdk.ControlPlaneSDK.EXPECT().CreateControlPlane(mock.Anything, sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", Description: lo.ToPtr("test control plane 1"), @@ -110,6 +110,10 @@ var konnectGatewayControlPlaneTestCases = []konnectEntityReconcilerTestCase{ ID: "12345", }, }, nil) + // verify that mock SDK is called as expected. + t.Cleanup(func() { + require.True(t, sdk.ControlPlaneSDK.AssertExpectations(t)) + }) }, eventuallyPredicate: func(ctx context.Context, t *testing.T, cl client.Client, ns *corev1.Namespace) bool { cp := &konnectv1alpha1.KonnectGatewayControlPlane{} @@ -188,10 +192,11 @@ func testNewKonnectEntityReconciler[ for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.objectOps(ctx, t, cl, ns) - tc.mockExpectations(sdk, ns) + tc.mockExpectations(t, sdk, ns) require.Eventually(t, func() bool { return tc.eventuallyPredicate(ctx, t, cl, ns) }, wait, tick) + require.True(t, sdk.ControlPlaneSDK.AssertExpectations(t)) }) } })