@@ -30,6 +30,7 @@ import (
3030 clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
3131 policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
3232 workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
33+ internalqueue "github.com/karmada-io/karmada/pkg/scheduler/internal/queue"
3334)
3435
3536func TestResourceBindingEventFilter (t * testing.T ) {
@@ -146,55 +147,84 @@ func TestResourceBindingEventFilter(t *testing.T) {
146147
147148func TestAddCluster (t * testing.T ) {
148149 tests := []struct {
149- name string
150- enableSchedulerEstimator bool
151- obj any
152- expectedAdded bool
153- expectedClusterName string
150+ name string
151+ enableSchedulerEstimator bool
152+ obj any
153+ priorityQueue internalqueue.SchedulingQueue
154+ expectedEstimatorAdded bool
155+ expectedEstimatorClusterName string
156+ expectedMoveAllToActive bool
154157 }{
155158 {
156- name : "valid cluster object with estimator enabled" ,
157- enableSchedulerEstimator : true ,
158- obj : createCluster ("test-cluster" , 0 , nil ),
159- expectedAdded : true ,
160- expectedClusterName : "test-cluster" ,
159+ name : "valid cluster, estimator enabled, no priority queue" ,
160+ enableSchedulerEstimator : true ,
161+ obj : createCluster ("test-cluster" , 0 , nil ),
162+ priorityQueue : nil ,
163+ expectedEstimatorAdded : true ,
164+ expectedEstimatorClusterName : "test-cluster" ,
165+ expectedMoveAllToActive : false ,
161166 },
162167 {
163- name : "valid cluster object with estimator disabled" ,
168+ name : "valid cluster, estimator enabled, queue with no unschedulable bindings" ,
169+ enableSchedulerEstimator : true ,
170+ obj : createCluster ("test-cluster" , 0 , nil ),
171+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : false },
172+ expectedEstimatorAdded : true ,
173+ expectedEstimatorClusterName : "test-cluster" ,
174+ expectedMoveAllToActive : false ,
175+ },
176+ {
177+ name : "valid cluster, estimator enabled, queue with unschedulable bindings" ,
178+ enableSchedulerEstimator : true ,
179+ obj : createCluster ("test-cluster" , 0 , nil ),
180+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
181+ expectedEstimatorAdded : true ,
182+ expectedEstimatorClusterName : "test-cluster" ,
183+ expectedMoveAllToActive : true ,
184+ },
185+ {
186+ name : "valid cluster, estimator disabled, queue with unschedulable bindings" ,
164187 enableSchedulerEstimator : false ,
165- obj : createCluster ("test-cluster-2" , 0 , nil ),
166- expectedAdded : false ,
167- expectedClusterName : "" ,
188+ obj : createCluster ("test-cluster-2" , 0 , nil ),
189+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
190+ expectedEstimatorAdded : false ,
191+ expectedMoveAllToActive : true ,
168192 },
169193 {
170194 name : "invalid object type" ,
171195 enableSchedulerEstimator : true ,
172196 obj : & corev1.Pod {},
173- expectedAdded : false ,
174- expectedClusterName : "" ,
197+ expectedEstimatorAdded : false ,
198+ expectedMoveAllToActive : false ,
175199 },
176200 }
177201
178202 for _ , tt := range tests {
179203 t .Run (tt .name , func (t * testing.T ) {
180- mockWorker := & mockAsyncWorker {}
204+ estimatorWorker := & mockAsyncWorker {}
181205 s := & Scheduler {
182206 enableSchedulerEstimator : tt .enableSchedulerEstimator ,
183- schedulerEstimatorWorker : mockWorker ,
207+ schedulerEstimatorWorker : estimatorWorker ,
208+ priorityQueue : tt .priorityQueue ,
184209 }
185210
186211 s .addCluster (tt .obj )
187212
188- if tt .expectedAdded {
189- assert .Equal (t , 1 , mockWorker .addCount , "Worker Add should have been called once" )
190- assert .Equal (t , tt .expectedClusterName , mockWorker .lastAdded , "Incorrect cluster name added" )
213+ if tt .expectedEstimatorAdded {
214+ assert .Equal (t , 1 , estimatorWorker .addCount , "Estimator worker Add should have been called once" )
215+ assert .Equal (t , tt .expectedEstimatorClusterName , estimatorWorker .lastAdded , "Incorrect cluster name added to estimator worker " )
191216 } else {
192- assert .Equal (t , 0 , mockWorker .addCount , "Worker Add should not have been called" )
193- assert .Nil (t , mockWorker .lastAdded , "No cluster name should have been added" )
217+ assert .Equal (t , 0 , estimatorWorker .addCount , "Estimator worker Add should not have been called" )
218+ assert .Nil (t , estimatorWorker .lastAdded , "No cluster name should have been added to estimator worker" )
219+ }
220+
221+ if tt .priorityQueue != nil {
222+ mock := tt .priorityQueue .(* mockSchedulingQueue )
223+ assert .Equal (t , tt .expectedMoveAllToActive , mock .moveAllToActiveCalled , "MoveAllToActive called state mismatch" )
194224 }
195225
196- assert .Equal (t , 0 , mockWorker .enqueueCount , "Worker Enqueue should not have been called" )
197- assert .Nil (t , mockWorker .lastEnqueued , "No item should have been enqueued" )
226+ assert .Equal (t , 0 , estimatorWorker .enqueueCount , "Estimator worker Enqueue should not have been called" )
227+ assert .Nil (t , estimatorWorker .lastEnqueued , "No item should have been enqueued" )
198228 })
199229 }
200230}
@@ -205,8 +235,10 @@ func TestUpdateCluster(t *testing.T) {
205235 enableSchedulerEstimator bool
206236 oldObj any
207237 newObj any
238+ priorityQueue internalqueue.SchedulingQueue
208239 expectedEstimatorAdded bool
209240 expectedReconcileAdded int
241+ expectedMoveAllToActive bool
210242 }{
211243 {
212244 name : "valid cluster update with generation change" ,
@@ -256,6 +288,141 @@ func TestUpdateCluster(t *testing.T) {
256288 expectedEstimatorAdded : false ,
257289 expectedReconcileAdded : 0 ,
258290 },
291+ {
292+ name : "cluster update with ResourceSummary change, priorityQueue nil" ,
293+ enableSchedulerEstimator : true ,
294+ oldObj : createCluster ("test-cluster" , 0 , nil ),
295+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
296+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
297+ }),
298+ expectedEstimatorAdded : true ,
299+ expectedReconcileAdded : 0 ,
300+ },
301+ {
302+ name : "cluster update with ResourceSummary change, no unschedulable bindings" ,
303+ enableSchedulerEstimator : true ,
304+ oldObj : createCluster ("test-cluster" , 0 , nil ),
305+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
306+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
307+ }),
308+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : false },
309+ expectedEstimatorAdded : true ,
310+ expectedReconcileAdded : 0 ,
311+ },
312+ {
313+ name : "cluster update with ResourceSummary change, has unschedulable bindings" ,
314+ enableSchedulerEstimator : true ,
315+ oldObj : createCluster ("test-cluster" , 0 , nil ),
316+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
317+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
318+ }),
319+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
320+ expectedEstimatorAdded : true ,
321+ expectedReconcileAdded : 0 ,
322+ expectedMoveAllToActive : true ,
323+ },
324+ {
325+ name : "ResourceSummary and Conditions change simultaneously — Conditions case takes precedence" ,
326+ enableSchedulerEstimator : false ,
327+ oldObj : createCluster ("test-cluster" , 0 , nil ),
328+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
329+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
330+ Conditions : []metav1.Condition {
331+ {Type : clusterv1alpha1 .ClusterConditionReady , Status : metav1 .ConditionTrue },
332+ },
333+ }),
334+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
335+ expectedEstimatorAdded : false ,
336+ // Conditions case fires first via MoveAllToActive; ResourceSummary case is skipped
337+ expectedReconcileAdded : 0 ,
338+ expectedMoveAllToActive : true ,
339+ },
340+ {
341+ name : "generation and status change simultaneously — generation case takes precedence" ,
342+ enableSchedulerEstimator : false ,
343+ oldObj : createCluster ("test-cluster" , 1 , nil ),
344+ newObj : createClusterWithStatus ("test-cluster" , 2 , nil , clusterv1alpha1.ClusterStatus {
345+ Conditions : []metav1.Condition {
346+ {Type : clusterv1alpha1 .ClusterConditionReady , Status : metav1 .ConditionTrue },
347+ },
348+ }),
349+ expectedEstimatorAdded : false ,
350+ // Generation case fires and adds both old+new → 2, not 3
351+ expectedReconcileAdded : 2 ,
352+ },
353+ {
354+ name : "DeletionTimestamp and status change simultaneously — deletion case takes precedence" ,
355+ enableSchedulerEstimator : false ,
356+ oldObj : createCluster ("test-cluster" , 0 , nil ),
357+ newObj : func () * clusterv1alpha1.Cluster {
358+ c := createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
359+ Conditions : []metav1.Condition {
360+ {Type : clusterv1alpha1 .ClusterConditionReady , Status : metav1 .ConditionTrue },
361+ },
362+ })
363+ now := metav1 .Now ()
364+ c .DeletionTimestamp = & now
365+ return c
366+ }(),
367+ expectedEstimatorAdded : false ,
368+ // Deletion case fires → only 1 add, status case is skipped
369+ expectedReconcileAdded : 1 ,
370+ },
371+ {
372+ name : "identical non-nil ResourceSummary — DeepEqual true, no reconcile triggered" ,
373+ enableSchedulerEstimator : false ,
374+ oldObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
375+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
376+ }),
377+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
378+ ResourceSummary : & clusterv1alpha1.ResourceSummary {},
379+ }),
380+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
381+ expectedEstimatorAdded : false ,
382+ expectedReconcileAdded : 0 ,
383+ },
384+ {
385+ name : "cluster update with Conditions change, has unschedulable bindings" ,
386+ enableSchedulerEstimator : true ,
387+ oldObj : createCluster ("test-cluster" , 0 , nil ),
388+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
389+ Conditions : []metav1.Condition {
390+ {Type : clusterv1alpha1 .ClusterConditionReady , Status : metav1 .ConditionTrue },
391+ },
392+ }),
393+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
394+ expectedEstimatorAdded : true ,
395+ expectedReconcileAdded : 0 ,
396+ expectedMoveAllToActive : true ,
397+ },
398+ {
399+ name : "cluster update with Conditions change, no unschedulable bindings" ,
400+ enableSchedulerEstimator : false ,
401+ oldObj : createCluster ("test-cluster" , 0 , nil ),
402+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
403+ Conditions : []metav1.Condition {
404+ {Type : clusterv1alpha1 .ClusterConditionReady , Status : metav1 .ConditionTrue },
405+ },
406+ }),
407+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : false },
408+ expectedEstimatorAdded : false ,
409+ expectedReconcileAdded : 0 ,
410+ expectedMoveAllToActive : false ,
411+ },
412+ {
413+ name : "cluster update with APIEnablements change, has unschedulable bindings" ,
414+ enableSchedulerEstimator : true ,
415+ oldObj : createCluster ("test-cluster" , 0 , nil ),
416+ newObj : createClusterWithStatus ("test-cluster" , 0 , nil , clusterv1alpha1.ClusterStatus {
417+ APIEnablements : []clusterv1alpha1.APIEnablement {
418+ {GroupVersion : "apps/v1" },
419+ },
420+ }),
421+ priorityQueue : & mockSchedulingQueue {hasUnschedulable : true },
422+ expectedEstimatorAdded : true ,
423+ expectedReconcileAdded : 0 ,
424+ expectedMoveAllToActive : true ,
425+ },
259426 }
260427
261428 for _ , tt := range tests {
@@ -266,6 +433,7 @@ func TestUpdateCluster(t *testing.T) {
266433 enableSchedulerEstimator : tt .enableSchedulerEstimator ,
267434 schedulerEstimatorWorker : estimatorWorker ,
268435 clusterReconcileWorker : reconcileWorker ,
436+ priorityQueue : tt .priorityQueue ,
269437 }
270438
271439 s .updateCluster (tt .oldObj , tt .newObj )
@@ -299,6 +467,12 @@ func TestUpdateCluster(t *testing.T) {
299467 } else {
300468 assert .Nil (t , reconcileWorker .lastAdded , "No cluster should have been added to reconcile worker" )
301469 }
470+
471+ // Check MoveAllToActive
472+ if tt .priorityQueue != nil {
473+ mock := tt .priorityQueue .(* mockSchedulingQueue )
474+ assert .Equal (t , tt .expectedMoveAllToActive , mock .moveAllToActiveCalled , "MoveAllToActive called state mismatch" )
475+ }
302476 })
303477 }
304478}
@@ -422,6 +596,17 @@ func createCluster(name string, generation int64, labels map[string]string) *clu
422596 }
423597}
424598
599+ func createClusterWithStatus (name string , generation int64 , labels map [string ]string , status clusterv1alpha1.ClusterStatus ) * clusterv1alpha1.Cluster {
600+ return & clusterv1alpha1.Cluster {
601+ ObjectMeta : metav1.ObjectMeta {
602+ Name : name ,
603+ Generation : generation ,
604+ Labels : labels ,
605+ },
606+ Status : status ,
607+ }
608+ }
609+
425610func createResourceBinding (name , schedulerName string , labels map [string ]string , suspension * workv1alpha2.Suspension ) * workv1alpha2.ResourceBinding {
426611 return & workv1alpha2.ResourceBinding {
427612 ObjectMeta : metav1.ObjectMeta {
0 commit comments