From b91000647023e3107757ce1989d4a69d4db587cf Mon Sep 17 00:00:00 2001 From: Yanjun Zhou Date: Thu, 3 Oct 2024 10:28:22 +0800 Subject: [PATCH] Enhance the IPBlocksInfo unit tests (#790) Signed-off-by: Yanjun Zhou --- .../ipblocksinfo/ipblocksinfo_test.go | 107 ++++++++++++++ pkg/nsx/services/ipblocksinfo/store_test.go | 139 ++++++++++++++++-- 2 files changed, 234 insertions(+), 12 deletions(-) diff --git a/pkg/nsx/services/ipblocksinfo/ipblocksinfo_test.go b/pkg/nsx/services/ipblocksinfo/ipblocksinfo_test.go index c13d5d8bc..0abdb424e 100644 --- a/pkg/nsx/services/ipblocksinfo/ipblocksinfo_test.go +++ b/pkg/nsx/services/ipblocksinfo/ipblocksinfo_test.go @@ -5,6 +5,7 @@ import ( "fmt" "reflect" "testing" + "time" "github.com/agiledragon/gomonkey" "github.com/golang/mock/gomock" @@ -12,6 +13,8 @@ import ( "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/nsx-operator/pkg/apis/vpc/v1alpha1" @@ -167,3 +170,107 @@ func TestIPBlocksInfoService_SyncIPBlocksInfo(t *testing.T) { err := service.SyncIPBlocksInfo(context.TODO()) assert.Equal(t, nil, err) } + +func TestIPBlocksInfoService_StartPeriodicSync(t *testing.T) { + ipBlocksInfoService := &IPBlocksInfoService{ + Service: common.Service{}, + SyncTask: NewIPBlocksInfoSyncTask(time.Millisecond*100, time.Millisecond*50), + } + done := make(chan bool) + go func() { + syncIPBlocksInfoPatch := gomonkey.ApplyMethod(reflect.TypeOf(ipBlocksInfoService), "SyncIPBlocksInfo", func(_ *IPBlocksInfoService, cxt context.Context) error { + return fmt.Errorf("mock error") + }) + defer syncIPBlocksInfoPatch.Reset() + ipBlocksInfoService.StartPeriodicSync() + done <- true + }() + + time.Sleep(time.Millisecond * 20) + ipBlocksInfoService.SyncTask.resetChan <- struct{}{} + + select { + case <-done: + t.Error("StartPeriodicSync stop unexpectedly") + case <-time.After(time.Millisecond * 500): + // Stop StartPeriodicSync after some time + } +} + +func TestIPBlocksInfoService_getIPBlockCIDRsFromStore(t *testing.T) { + ipBlockStore := &IPBlockStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{}), + BindingType: model.IpAddressBlockBindingType(), + }} + ipblock1 := model.IpAddressBlock{ + Path: &ipBlocksPath1, + } + ipBlockStore.Apply(&ipblock1) + service := &IPBlocksInfoService{} + + // Fetch non-existed IPBlocks + pathSet := sets.New[string]() + pathSet.Insert(ipBlocksPath2) + _, err := service.getIPBlockCIDRsFromStore(pathSet, ipBlockStore) + assert.ErrorContains(t, err, "failed to get IPBlock") + + // No CIDR in IPBlocks + pathSet = sets.New[string]() + pathSet.Insert(ipBlocksPath1) + _, err = service.getIPBlockCIDRsFromStore(pathSet, ipBlockStore) + assert.ErrorContains(t, err, "failed to get CIDR from ipblock") +} + +func TestIPBlocksInfoService_createOrUpdateIPBlocksInfo(t *testing.T) { + service, mockController, mockK8sClient := createService(t) + defer mockController.Finish() + + ipBlocksInfo := v1alpha1.IPBlocksInfo{} + mockErr := fmt.Errorf("mock error") + + // Fail to get IPBlocksInfo CR + mockK8sClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockErr) + + err := service.createOrUpdateIPBlocksInfo(context.TODO(), &ipBlocksInfo, false) + assert.ErrorIs(t, err, mockErr) + + // Fail to create IPBlocksInfo CR + mockK8sClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(v1alpha1.Resource("IPBlocksInfo"), "ipBlocksInfoName")) + mockK8sClient.EXPECT().Create(gomock.Any(), gomock.Any()).Return(mockErr) + err = service.createOrUpdateIPBlocksInfo(context.TODO(), &ipBlocksInfo, false) + assert.ErrorIs(t, err, mockErr) + + // // Fail to udpate IPBlocksInfo CR + mockK8sClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Do(func(_ context.Context, _ client.ObjectKey, obj client.Object, option ...client.GetOption) error { + ipBlocksInfoCR := obj.(*v1alpha1.IPBlocksInfo) + ipBlocksInfoCR.ExternalIPCIDRs = []string{ipBlocksMap[ipBlocksPath4]} + return nil + }) + mockK8sClient.EXPECT().Update(gomock.Any(), gomock.Any()).Return(mockErr) + err = service.createOrUpdateIPBlocksInfo(context.TODO(), &ipBlocksInfo, false) + assert.ErrorIs(t, err, mockErr) +} + +func TestIsDefaultNetworkConfigCR(t *testing.T) { + testCRD1 := v1alpha1.VPCNetworkConfiguration{} + testCRD1.Name = "test-1" + testCRD2 := v1alpha1.VPCNetworkConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + common.AnnotationDefaultNetworkConfig: "invalid", + }, + }, + } + testCRD2.Name = "test-2" + testCRD3 := v1alpha1.VPCNetworkConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + common.AnnotationDefaultNetworkConfig: "true", + }, + }, + } + testCRD3.Name = "test-3" + assert.Equal(t, isDefaultNetworkConfigCR(testCRD1), false) + assert.Equal(t, isDefaultNetworkConfigCR(testCRD2), false) + assert.Equal(t, isDefaultNetworkConfigCR(testCRD3), true) +} diff --git a/pkg/nsx/services/ipblocksinfo/store_test.go b/pkg/nsx/services/ipblocksinfo/store_test.go index 1159a03a0..23a3ebee6 100644 --- a/pkg/nsx/services/ipblocksinfo/store_test.go +++ b/pkg/nsx/services/ipblocksinfo/store_test.go @@ -5,15 +5,23 @@ import ( "github.com/stretchr/testify/assert" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "k8s.io/client-go/tools/cache" + + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" +) + +var ( + fakeVpcPath = "vpc-path" + fakeVpcProfilePath = "vpc-connectivity-profile-path" + fakeIpBlockPath = "ip-block-path" + fakeDeleted = true ) func Test_KeyFunc(t *testing.T) { - vpcPath := "vpc-path" - vpc := model.Vpc{Path: &vpcPath} - vpcProfilePath := "vpc-connectivity-profile-path" - vpcProfile := model.VpcConnectivityProfile{Path: &vpcProfilePath} - ipBlockPath := "ip-block-path" - ipBlock := model.IpAddressBlock{Path: &ipBlockPath} + vpc := model.Vpc{Path: &fakeVpcPath} + vpcProfile := model.VpcConnectivityProfile{Path: &fakeVpcProfilePath} + ipBlock := model.IpAddressBlock{Path: &fakeIpBlockPath} + notSupported := struct{}{} type args struct { obj interface{} @@ -23,32 +31,139 @@ func Test_KeyFunc(t *testing.T) { name string expectedKey string item args + expectedErr bool }{ { name: "Vpc", item: args{obj: &vpc}, - expectedKey: vpcPath, + expectedKey: fakeVpcPath, }, { name: "VpcConnectivityProfile", item: args{obj: &vpcProfile}, - expectedKey: vpcProfilePath, + expectedKey: fakeVpcProfilePath, }, { name: "IpBlock", item: args{obj: &ipBlock}, - expectedKey: ipBlockPath, + expectedKey: fakeIpBlockPath, + }, + { + name: "NotSupported", + item: args{obj: ¬Supported}, + expectedErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := keyFunc(tt.item.obj) - assert.Nil(t, err) - if got != tt.expectedKey { - t.Errorf("keyFunc() = %v, want %v", got, tt.expectedKey) + if !tt.expectedErr { + assert.Nil(t, err) + if got != tt.expectedKey { + t.Errorf("keyFunc() = %v, want %v", got, tt.expectedKey) + } + } else { + assert.NotNil(t, err) } + + }) + } + +} + +func TestVPCConnectivityProfileStore_Apply(t *testing.T) { + vpcConnectivityProfileStore := &VPCConnectivityProfileStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{}), + BindingType: model.VpcConnectivityProfileBindingType(), + }} + + profile1 := model.VpcConnectivityProfile{ + Path: &fakeVpcProfilePath, + } + profile2 := model.VpcConnectivityProfile{ + Path: &fakeVpcProfilePath, + MarkedForDelete: &fakeDeleted, + } + + type args struct { + i interface{} + } + tests := []struct { + name string + args args + }{ + {"Add", args{i: &profile1}}, + {"Delete", args{i: &profile2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := vpcConnectivityProfileStore.Apply(tt.args.i) + assert.Nil(t, err) }) } +} +func TestVPCStore_Apply(t *testing.T) { + vpcStore := &VPCStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{}), + BindingType: model.VpcBindingType(), + }} + + vpc1 := model.Vpc{ + Path: &fakeVpcPath, + } + vpc2 := model.Vpc{ + Path: &fakeVpcPath, + MarkedForDelete: &fakeDeleted, + } + + type args struct { + i interface{} + } + tests := []struct { + name string + args args + }{ + {"Add", args{i: &vpc1}}, + {"Delete", args{i: &vpc2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := vpcStore.Apply(tt.args.i) + assert.Nil(t, err) + }) + } +} + +func TestIPBlockStore_Apply(t *testing.T) { + ipBlockStore := &IPBlockStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{}), + BindingType: model.IpAddressBlockBindingType(), + }} + + ipblock1 := model.IpAddressBlock{ + Path: &fakeIpBlockPath, + } + ipblock2 := model.IpAddressBlock{ + Path: &fakeIpBlockPath, + MarkedForDelete: &fakeDeleted, + } + + type args struct { + i interface{} + } + tests := []struct { + name string + args args + }{ + {"Add", args{i: &ipblock1}}, + {"Delete", args{i: &ipblock2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ipBlockStore.Apply(tt.args.i) + assert.Nil(t, err) + }) + } }