295 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2024 The Karmada Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package federatedresourcequota
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	corev1 "k8s.io/api/core/v1"
 | |
| 	"k8s.io/apimachinery/pkg/api/resource"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/runtime"
 | |
| 	"k8s.io/client-go/tools/record"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 | |
| 
 | |
| 	clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
 | |
| 	policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
 | |
| 	workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
 | |
| 	"github.com/karmada-io/karmada/pkg/util"
 | |
| )
 | |
| 
 | |
| // setupTest initializes a test environment with the given runtime objects
 | |
| // It returns a fake client and a SyncController for use in tests
 | |
| func setupTest(t *testing.T, objs ...runtime.Object) (client.Client, *SyncController) {
 | |
| 	scheme := runtime.NewScheme()
 | |
| 	assert.NoError(t, policyv1alpha1.Install(scheme))
 | |
| 	assert.NoError(t, workv1alpha1.Install(scheme))
 | |
| 	assert.NoError(t, clusterv1alpha1.Install(scheme))
 | |
| 	assert.NoError(t, corev1.AddToScheme(scheme))
 | |
| 
 | |
| 	fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objs...).Build()
 | |
| 	controller := &SyncController{
 | |
| 		Client:        fakeClient,
 | |
| 		EventRecorder: record.NewFakeRecorder(100),
 | |
| 	}
 | |
| 	return fakeClient, controller
 | |
| }
 | |
| 
 | |
| // TestCleanUpWorks tests the cleanUpWorks function of the SyncController
 | |
| func TestCleanUpWorks(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name          string
 | |
| 		existingWorks []runtime.Object
 | |
| 		namespace     string
 | |
| 		quotaName     string
 | |
| 		expectedError bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Successfully delete works",
 | |
| 			existingWorks: []runtime.Object{
 | |
| 				&workv1alpha1.Work{
 | |
| 					ObjectMeta: metav1.ObjectMeta{
 | |
| 						Name:      "work-1",
 | |
| 						Namespace: "default",
 | |
| 						Labels: map[string]string{
 | |
| 							util.FederatedResourceQuotaNamespaceLabel: "default",
 | |
| 							util.FederatedResourceQuotaNameLabel:      "test-quota",
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				&workv1alpha1.Work{
 | |
| 					ObjectMeta: metav1.ObjectMeta{
 | |
| 						Name:      "work-2",
 | |
| 						Namespace: "default",
 | |
| 						Labels: map[string]string{
 | |
| 							util.FederatedResourceQuotaNamespaceLabel: "default",
 | |
| 							util.FederatedResourceQuotaNameLabel:      "test-quota",
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			namespace:     "default",
 | |
| 			quotaName:     "test-quota",
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:          "No works to delete",
 | |
| 			existingWorks: []runtime.Object{},
 | |
| 			namespace:     "default",
 | |
| 			quotaName:     "test-quota",
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			fakeClient, controller := setupTest(t, tt.existingWorks...)
 | |
| 
 | |
| 			err := controller.cleanUpWorks(context.Background(), tt.namespace, tt.quotaName)
 | |
| 
 | |
| 			if tt.expectedError {
 | |
| 				assert.Error(t, err)
 | |
| 			} else {
 | |
| 				assert.NoError(t, err)
 | |
| 			}
 | |
| 
 | |
| 			// Verify that works are deleted
 | |
| 			workList := &workv1alpha1.WorkList{}
 | |
| 			err = fakeClient.List(context.Background(), workList, client.MatchingLabels{
 | |
| 				util.FederatedResourceQuotaNamespaceLabel: tt.namespace,
 | |
| 				util.FederatedResourceQuotaNameLabel:      tt.quotaName,
 | |
| 			})
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Empty(t, workList.Items)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // It verifies that works are correctly created for the given FederatedResourceQuota and clusters
 | |
| func TestBuildWorks(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name          string
 | |
| 		quota         *policyv1alpha1.FederatedResourceQuota
 | |
| 		clusters      []clusterv1alpha1.Cluster
 | |
| 		expectedError bool
 | |
| 		expectedWorks int
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Successfully build works for all clusters",
 | |
| 			quota: &policyv1alpha1.FederatedResourceQuota{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "test-quota",
 | |
| 					Namespace: "default",
 | |
| 				},
 | |
| 				Spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 					StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
 | |
| 						{
 | |
| 							ClusterName: "cluster1",
 | |
| 							Hard: corev1.ResourceList{
 | |
| 								corev1.ResourceCPU: resource.MustParse("1"),
 | |
| 							},
 | |
| 						},
 | |
| 						{
 | |
| 							ClusterName: "cluster2",
 | |
| 							Hard: corev1.ResourceList{
 | |
| 								corev1.ResourceCPU: resource.MustParse("2"),
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			clusters: []clusterv1alpha1.Cluster{
 | |
| 				{ObjectMeta: metav1.ObjectMeta{Name: "cluster1"}},
 | |
| 				{ObjectMeta: metav1.ObjectMeta{Name: "cluster2"}},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 			expectedWorks: 2,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "No clusters available",
 | |
| 			quota: &policyv1alpha1.FederatedResourceQuota{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "test-quota",
 | |
| 					Namespace: "default",
 | |
| 				},
 | |
| 				Spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 					StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
 | |
| 						{
 | |
| 							ClusterName: "cluster1",
 | |
| 							Hard: corev1.ResourceList{
 | |
| 								corev1.ResourceCPU: resource.MustParse("1"),
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			clusters:      []clusterv1alpha1.Cluster{},
 | |
| 			expectedError: false,
 | |
| 			expectedWorks: 0,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			fakeClient, controller := setupTest(t)
 | |
| 
 | |
| 			err := controller.buildWorks(context.Background(), tt.quota, tt.clusters)
 | |
| 
 | |
| 			if tt.expectedError {
 | |
| 				assert.Error(t, err)
 | |
| 			} else {
 | |
| 				assert.NoError(t, err)
 | |
| 			}
 | |
| 
 | |
| 			// Verify the number of created works
 | |
| 			workList := &workv1alpha1.WorkList{}
 | |
| 			err = fakeClient.List(context.Background(), workList, client.MatchingLabels{
 | |
| 				util.FederatedResourceQuotaNamespaceLabel: tt.quota.Namespace,
 | |
| 				util.FederatedResourceQuotaNameLabel:      tt.quota.Name,
 | |
| 			})
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Len(t, workList.Items, tt.expectedWorks)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestExtractClusterHardResourceList tests the extractClusterHardResourceList function
 | |
| func TestExtractClusterHardResourceList(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name           string
 | |
| 		spec           policyv1alpha1.FederatedResourceQuotaSpec
 | |
| 		clusterName    string
 | |
| 		expectedResult corev1.ResourceList
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Cluster found in static assignments",
 | |
| 			spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 				StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
 | |
| 					{
 | |
| 						ClusterName: "cluster1",
 | |
| 						Hard: corev1.ResourceList{
 | |
| 							corev1.ResourceCPU:    resource.MustParse("1"),
 | |
| 							corev1.ResourceMemory: resource.MustParse("1Gi"),
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			clusterName: "cluster1",
 | |
| 			expectedResult: corev1.ResourceList{
 | |
| 				corev1.ResourceCPU:    resource.MustParse("1"),
 | |
| 				corev1.ResourceMemory: resource.MustParse("1Gi"),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Cluster not found in static assignments",
 | |
| 			spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 				StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
 | |
| 					{
 | |
| 						ClusterName: "cluster1",
 | |
| 						Hard: corev1.ResourceList{
 | |
| 							corev1.ResourceCPU: resource.MustParse("1"),
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			clusterName:    "cluster2",
 | |
| 			expectedResult: nil,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Empty static assignments",
 | |
| 			spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 				StaticAssignments: []policyv1alpha1.StaticClusterAssignment{},
 | |
| 			},
 | |
| 			clusterName:    "cluster1",
 | |
| 			expectedResult: nil,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Multiple static assignments",
 | |
| 			spec: policyv1alpha1.FederatedResourceQuotaSpec{
 | |
| 				StaticAssignments: []policyv1alpha1.StaticClusterAssignment{
 | |
| 					{
 | |
| 						ClusterName: "cluster1",
 | |
| 						Hard: corev1.ResourceList{
 | |
| 							corev1.ResourceCPU: resource.MustParse("1"),
 | |
| 						},
 | |
| 					},
 | |
| 					{
 | |
| 						ClusterName: "cluster2",
 | |
| 						Hard: corev1.ResourceList{
 | |
| 							corev1.ResourceCPU: resource.MustParse("2"),
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			clusterName: "cluster2",
 | |
| 			expectedResult: corev1.ResourceList{
 | |
| 				corev1.ResourceCPU: resource.MustParse("2"),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			result := extractClusterHardResourceList(tt.spec, tt.clusterName)
 | |
| 			assert.Equal(t, tt.expectedResult, result)
 | |
| 		})
 | |
| 	}
 | |
| }
 |