Added tests for pkg/scheduler/event_handler.go
Signed-off-by: Anuj Agrawal <anujagrawal380@gmail.com>
This commit is contained in:
parent
6b18b6e120
commit
1b177e5fe6
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
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 scheduler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||
)
|
||||
|
||||
func TestResourceBindingEventFilter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
schedulerName string
|
||||
obj interface{}
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "ResourceBinding: Matching scheduler name, no labels",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "test-scheduler", nil),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "ResourceBinding: Non-matching scheduler name",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "other-scheduler", nil),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "ResourceBinding: Matching scheduler name, with PropagationPolicyPermanentIDLabel",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "test-scheduler", map[string]string{
|
||||
policyv1alpha1.PropagationPolicyPermanentIDLabel: "test-id",
|
||||
}),
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "ResourceBinding: Matching scheduler name, with ClusterPropagationPolicyPermanentIDLabel",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "test-scheduler", map[string]string{
|
||||
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "test-id",
|
||||
}),
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "ResourceBinding: Matching scheduler name, with BindingManagedByLabel",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "test-scheduler", map[string]string{
|
||||
workv1alpha2.BindingManagedByLabel: "test-manager",
|
||||
}),
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "ResourceBinding: Matching scheduler name, with empty PropagationPolicyPermanentIDLabel",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createResourceBinding("test-rb", "test-scheduler", map[string]string{
|
||||
policyv1alpha1.PropagationPolicyPermanentIDLabel: "",
|
||||
}),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "ClusterResourceBinding: Matching scheduler name, no labels",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createClusterResourceBinding("test-crb", "test-scheduler", nil),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "ClusterResourceBinding: Non-matching scheduler name",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createClusterResourceBinding("test-crb", "other-scheduler", nil),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "ClusterResourceBinding: Matching scheduler name, with ClusterPropagationPolicyPermanentIDLabel",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: createClusterResourceBinding("test-crb", "test-scheduler", map[string]string{
|
||||
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "test-id",
|
||||
}),
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "Nil object",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: nil,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid object type",
|
||||
schedulerName: "test-scheduler",
|
||||
obj: "not-a-valid-object",
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := &Scheduler{
|
||||
schedulerName: tc.schedulerName,
|
||||
}
|
||||
result := s.resourceBindingEventFilter(tc.obj)
|
||||
assert.Equal(t, tc.expectedResult, result, "Test case: %s", tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCluster(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
enableSchedulerEstimator bool
|
||||
obj interface{}
|
||||
expectedAdded bool
|
||||
expectedClusterName string
|
||||
}{
|
||||
{
|
||||
name: "valid cluster object with estimator enabled",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: createCluster("test-cluster", 0, nil),
|
||||
expectedAdded: true,
|
||||
expectedClusterName: "test-cluster",
|
||||
},
|
||||
{
|
||||
name: "valid cluster object with estimator disabled",
|
||||
enableSchedulerEstimator: false,
|
||||
obj: createCluster("test-cluster-2", 0, nil),
|
||||
expectedAdded: false,
|
||||
expectedClusterName: "",
|
||||
},
|
||||
{
|
||||
name: "invalid object type",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: &corev1.Pod{},
|
||||
expectedAdded: false,
|
||||
expectedClusterName: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockWorker := &mockAsyncWorker{}
|
||||
s := &Scheduler{
|
||||
enableSchedulerEstimator: tt.enableSchedulerEstimator,
|
||||
schedulerEstimatorWorker: mockWorker,
|
||||
}
|
||||
|
||||
s.addCluster(tt.obj)
|
||||
|
||||
if tt.expectedAdded {
|
||||
assert.Equal(t, 1, mockWorker.addCount, "Worker Add should have been called once")
|
||||
assert.Equal(t, tt.expectedClusterName, mockWorker.lastAdded, "Incorrect cluster name added")
|
||||
} else {
|
||||
assert.Equal(t, 0, mockWorker.addCount, "Worker Add should not have been called")
|
||||
assert.Nil(t, mockWorker.lastAdded, "No cluster name should have been added")
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, mockWorker.enqueueCount, "Worker Enqueue should not have been called")
|
||||
assert.Nil(t, mockWorker.lastEnqueued, "No item should have been enqueued")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCluster(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
enableSchedulerEstimator bool
|
||||
oldObj interface{}
|
||||
newObj interface{}
|
||||
expectedEstimatorAdded bool
|
||||
expectedReconcileAdded int
|
||||
}{
|
||||
{
|
||||
name: "valid cluster update with generation change",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: createCluster("test-cluster", 1, nil),
|
||||
newObj: createCluster("test-cluster", 2, nil),
|
||||
expectedEstimatorAdded: true,
|
||||
expectedReconcileAdded: 2,
|
||||
},
|
||||
{
|
||||
name: "valid cluster update with label change",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: createCluster("test-cluster", 0, map[string]string{"old": "label"}),
|
||||
newObj: createCluster("test-cluster", 0, map[string]string{"new": "label"}),
|
||||
expectedEstimatorAdded: true,
|
||||
expectedReconcileAdded: 2,
|
||||
},
|
||||
{
|
||||
name: "valid cluster update without changes",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: createCluster("test-cluster", 0, nil),
|
||||
newObj: createCluster("test-cluster", 0, nil),
|
||||
expectedEstimatorAdded: true,
|
||||
expectedReconcileAdded: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid old object type",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: &corev1.Pod{},
|
||||
newObj: createCluster("test-cluster", 0, nil),
|
||||
expectedEstimatorAdded: false,
|
||||
expectedReconcileAdded: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid new object type",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: createCluster("test-cluster", 0, nil),
|
||||
newObj: &corev1.Pod{},
|
||||
expectedEstimatorAdded: false,
|
||||
expectedReconcileAdded: 0,
|
||||
},
|
||||
{
|
||||
name: "both objects invalid",
|
||||
enableSchedulerEstimator: true,
|
||||
oldObj: &corev1.Pod{},
|
||||
newObj: &corev1.Pod{},
|
||||
expectedEstimatorAdded: false,
|
||||
expectedReconcileAdded: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
estimatorWorker := &mockAsyncWorker{}
|
||||
reconcileWorker := &mockAsyncWorker{}
|
||||
s := &Scheduler{
|
||||
enableSchedulerEstimator: tt.enableSchedulerEstimator,
|
||||
schedulerEstimatorWorker: estimatorWorker,
|
||||
clusterReconcileWorker: reconcileWorker,
|
||||
}
|
||||
|
||||
s.updateCluster(tt.oldObj, tt.newObj)
|
||||
|
||||
// Check schedulerEstimatorWorker
|
||||
if tt.expectedEstimatorAdded {
|
||||
assert.Equal(t, 1, estimatorWorker.addCount, "Estimator worker Add should have been called once")
|
||||
if cluster, ok := tt.newObj.(*clusterv1alpha1.Cluster); ok {
|
||||
assert.Equal(t, cluster.Name, estimatorWorker.lastAdded, "Incorrect cluster name added to estimator worker")
|
||||
} else {
|
||||
t.Errorf("Expected newObj to be a Cluster, but it wasn't")
|
||||
}
|
||||
} else {
|
||||
assert.Equal(t, 0, estimatorWorker.addCount, "Estimator worker Add should not have been called")
|
||||
assert.Nil(t, estimatorWorker.lastAdded, "No cluster should have been added to estimator worker")
|
||||
}
|
||||
|
||||
// Check clusterReconcileWorker
|
||||
assert.Equal(t, tt.expectedReconcileAdded, reconcileWorker.addCount, "Reconcile worker Add called unexpected number of times")
|
||||
|
||||
if tt.expectedReconcileAdded > 0 {
|
||||
lastAdded, ok := reconcileWorker.lastAdded.(*clusterv1alpha1.Cluster)
|
||||
assert.True(t, ok, "Last added item is not a Cluster object")
|
||||
if ok {
|
||||
newCluster, newOk := tt.newObj.(*clusterv1alpha1.Cluster)
|
||||
assert.True(t, newOk, "newObj is not a Cluster object")
|
||||
if newOk {
|
||||
assert.Equal(t, newCluster.Name, lastAdded.Name, "Incorrect cluster added to reconcile worker")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, reconcileWorker.lastAdded, "No cluster should have been added to reconcile worker")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCluster(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
enableSchedulerEstimator bool
|
||||
obj interface{}
|
||||
expectedAdded bool
|
||||
expectedClusterName string
|
||||
}{
|
||||
{
|
||||
name: "valid cluster object with estimator enabled",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: createCluster("test-cluster", 0, nil),
|
||||
expectedAdded: true,
|
||||
expectedClusterName: "test-cluster",
|
||||
},
|
||||
{
|
||||
name: "valid cluster object with estimator disabled",
|
||||
enableSchedulerEstimator: false,
|
||||
obj: createCluster("test-cluster", 0, nil),
|
||||
expectedAdded: false,
|
||||
expectedClusterName: "",
|
||||
},
|
||||
{
|
||||
name: "deleted final state unknown with valid cluster",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: cache.DeletedFinalStateUnknown{
|
||||
Key: "test-cluster",
|
||||
Obj: createCluster("test-cluster", 0, nil),
|
||||
},
|
||||
expectedAdded: true,
|
||||
expectedClusterName: "test-cluster",
|
||||
},
|
||||
{
|
||||
name: "deleted final state unknown with invalid object",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: cache.DeletedFinalStateUnknown{
|
||||
Key: "test-pod",
|
||||
Obj: &corev1.Pod{},
|
||||
},
|
||||
expectedAdded: false,
|
||||
expectedClusterName: "",
|
||||
},
|
||||
{
|
||||
name: "invalid object type",
|
||||
enableSchedulerEstimator: true,
|
||||
obj: &corev1.Pod{},
|
||||
expectedAdded: false,
|
||||
expectedClusterName: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
worker := &mockAsyncWorker{}
|
||||
s := &Scheduler{
|
||||
enableSchedulerEstimator: tt.enableSchedulerEstimator,
|
||||
schedulerEstimatorWorker: worker,
|
||||
}
|
||||
|
||||
s.deleteCluster(tt.obj)
|
||||
|
||||
if tt.expectedAdded {
|
||||
assert.Equal(t, 1, worker.addCount, "Worker Add should have been called once")
|
||||
assert.Equal(t, tt.expectedClusterName, worker.lastAdded, "Incorrect cluster name added to worker")
|
||||
} else {
|
||||
assert.Equal(t, 0, worker.addCount, "Worker Add should not have been called")
|
||||
assert.Nil(t, worker.lastAdded, "No cluster name should have been added")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchedulerNameFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
schedulerNameFromOptions string
|
||||
schedulerName string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "matching scheduler names",
|
||||
schedulerNameFromOptions: "test-scheduler",
|
||||
schedulerName: "test-scheduler",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "non-matching scheduler names",
|
||||
schedulerNameFromOptions: "test-scheduler",
|
||||
schedulerName: "other-scheduler",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty scheduler name defaults to DefaultScheduler",
|
||||
schedulerNameFromOptions: DefaultScheduler,
|
||||
schedulerName: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := schedulerNameFilter(tt.schedulerNameFromOptions, tt.schedulerName)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func createCluster(name string, generation int64, labels map[string]string) *clusterv1alpha1.Cluster {
|
||||
return &clusterv1alpha1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Generation: generation,
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createResourceBinding(name, schedulerName string, labels map[string]string) *workv1alpha2.ResourceBinding {
|
||||
return &workv1alpha2.ResourceBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: workv1alpha2.ResourceBindingSpec{
|
||||
SchedulerName: schedulerName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createClusterResourceBinding(name, schedulerName string, labels map[string]string) *workv1alpha2.ClusterResourceBinding {
|
||||
return &workv1alpha2.ClusterResourceBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: workv1alpha2.ResourceBindingSpec{
|
||||
SchedulerName: schedulerName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Mock Implementations
|
||||
|
||||
// mockAsyncWorker is a mock implementation of util.AsyncWorker
|
||||
type mockAsyncWorker struct {
|
||||
addCount int
|
||||
enqueueCount int
|
||||
lastAdded interface{}
|
||||
lastEnqueued interface{}
|
||||
}
|
||||
|
||||
func (m *mockAsyncWorker) Add(item interface{}) {
|
||||
m.addCount++
|
||||
m.lastAdded = item
|
||||
}
|
||||
|
||||
func (m *mockAsyncWorker) Enqueue(item interface{}) {
|
||||
m.enqueueCount++
|
||||
m.lastEnqueued = item
|
||||
}
|
||||
|
||||
func (m *mockAsyncWorker) AddAfter(_ interface{}, _ time.Duration) {}
|
||||
|
||||
func (m *mockAsyncWorker) Run(_ int, _ <-chan struct{}) {}
|
Loading…
Reference in New Issue