@@ -23,6 +23,19 @@ import (
2323 "testing"
2424 "time"
2525
26+ clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
27+ workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
28+ workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
29+ "github.com/karmada-io/karmada/pkg/events"
30+ "github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native"
31+ "github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
32+ "github.com/karmada-io/karmada/pkg/util"
33+ "github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
34+ "github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
35+ "github.com/karmada-io/karmada/pkg/util/gclient"
36+ "github.com/karmada-io/karmada/pkg/util/helper"
37+ "github.com/karmada-io/karmada/pkg/util/objectwatcher"
38+ testhelper "github.com/karmada-io/karmada/test/helper"
2639 "github.com/stretchr/testify/assert"
2740 corev1 "k8s.io/api/core/v1"
2841 rbacv1 "k8s.io/api/rbac/v1"
@@ -36,25 +49,12 @@ import (
3649 "k8s.io/apimachinery/pkg/util/uuid"
3750 dynamicfake "k8s.io/client-go/dynamic/fake"
3851 "k8s.io/client-go/kubernetes/scheme"
52+ "k8s.io/client-go/tools/cache"
3953 "k8s.io/client-go/tools/record"
4054 "k8s.io/utils/ptr"
4155 controllerruntime "sigs.k8s.io/controller-runtime"
4256 "sigs.k8s.io/controller-runtime/pkg/client"
4357 "sigs.k8s.io/controller-runtime/pkg/client/fake"
44-
45- clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
46- workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
47- workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
48- "github.com/karmada-io/karmada/pkg/events"
49- "github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native"
50- "github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
51- "github.com/karmada-io/karmada/pkg/util"
52- "github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
53- "github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
54- "github.com/karmada-io/karmada/pkg/util/gclient"
55- "github.com/karmada-io/karmada/pkg/util/helper"
56- "github.com/karmada-io/karmada/pkg/util/objectwatcher"
57- testhelper "github.com/karmada-io/karmada/test/helper"
5858)
5959
6060func newCluster (name string , clusterType string , clusterStatus metav1.ConditionStatus ) * clusterv1alpha1.Cluster {
@@ -1096,3 +1096,113 @@ func TestWorkStatusController_interpretHealth(t *testing.T) {
10961096 })
10971097 }
10981098}
1099+
1100+ type TestObject struct {
1101+ metav1.TypeMeta
1102+ metav1.ObjectMeta
1103+ Spec string
1104+ }
1105+
1106+ func (in * TestObject ) DeepCopyObject () runtime.Object {
1107+ return & TestObject {
1108+ TypeMeta : in .TypeMeta ,
1109+ ObjectMeta : in .ObjectMeta ,
1110+ Spec : in .Spec ,
1111+ }
1112+ }
1113+
1114+ // mockAsyncWorker implements util.AsyncPriorityWorker for testing onAdd/onUpdate/onDelete behavior.
1115+ type mockAsyncWorker struct {
1116+ receivedObjs []any // objects passed via EnqueueWithOpts (onAdd)
1117+ receivedPriorities []* int // priorities captured from EnqueueWithOpts
1118+ enqueuedObjs []any // objects passed via Enqueue (onUpdate/onDelete)
1119+ }
1120+
1121+ func (m * mockAsyncWorker ) EnqueueWithOpts (opts util.AddOpts , obj any ) { // capture inputs for onAdd
1122+ m .receivedObjs = append (m .receivedObjs , obj )
1123+ m .receivedPriorities = append (m .receivedPriorities , opts .Priority )
1124+ }
1125+ func (m * mockAsyncWorker ) AddWithOpts (_ util.AddOpts , _ ... any ) {}
1126+ func (m * mockAsyncWorker ) Add (_ interface {}) {}
1127+ func (m * mockAsyncWorker ) AddAfter (_ interface {}, _ time.Duration ) {}
1128+ func (m * mockAsyncWorker ) Enqueue (obj interface {}) { // capture inputs for onUpdate/onDelete
1129+ m .enqueuedObjs = append (m .enqueuedObjs , obj )
1130+ }
1131+ func (m * mockAsyncWorker ) Run (_ context.Context , _ int ) {}
1132+
1133+ func TestWorkStatusController_onAdd (t * testing.T ) {
1134+ cluster := newCluster ("cluster" , clusterv1alpha1 .ClusterConditionReady , metav1 .ConditionTrue )
1135+ c := newWorkStatusController (cluster )
1136+ mockWorker := & mockAsyncWorker {}
1137+ c .worker = mockWorker
1138+
1139+ obj := & TestObject {ObjectMeta : metav1.ObjectMeta {Name : "test-obj" }}
1140+
1141+ t .Run ("in initial list -> low priority" , func (t * testing.T ) {
1142+ c .onAdd (obj , true )
1143+ assert .Equal (t , 1 , len (mockWorker .receivedObjs ))
1144+ assert .Equal (t , obj , mockWorker .receivedObjs [0 ])
1145+ if assert .NotNil (t , mockWorker .receivedPriorities [0 ]) {
1146+ assert .Equal (t , util .LowPriority , * mockWorker .receivedPriorities [0 ])
1147+ }
1148+ })
1149+
1150+ t .Run ("not in initial list -> nil priority" , func (t * testing.T ) {
1151+ c .onAdd (obj , false )
1152+ assert .Equal (t , 2 , len (mockWorker .receivedObjs ))
1153+ assert .Equal (t , obj , mockWorker .receivedObjs [1 ])
1154+ assert .Nil (t , mockWorker .receivedPriorities [1 ])
1155+ })
1156+ }
1157+
1158+ func TestWorkStatusController_onUpdate (t * testing.T ) {
1159+ cluster := newCluster ("cluster" , clusterv1alpha1 .ClusterConditionReady , metav1 .ConditionTrue )
1160+ c := newWorkStatusController (cluster )
1161+ mockWorker := & mockAsyncWorker {}
1162+ c .worker = mockWorker
1163+
1164+ oldObj := & TestObject {ObjectMeta : metav1.ObjectMeta {Name : "test-obj" }, Spec : "same" }
1165+ // Deep copy same content
1166+ curSame := oldObj .DeepCopyObject ().(* TestObject )
1167+ // Different spec
1168+ curDiff := & TestObject {ObjectMeta : metav1.ObjectMeta {Name : "test-obj" }, Spec : "different" }
1169+
1170+ t .Run ("objects equal -> no enqueue" , func (t * testing.T ) {
1171+ c .onUpdate (oldObj , curSame )
1172+ assert .Equal (t , 0 , len (mockWorker .enqueuedObjs ))
1173+ })
1174+
1175+ t .Run ("objects differ -> enqueue" , func (t * testing.T ) {
1176+ c .onUpdate (oldObj , curDiff )
1177+ assert .Equal (t , 1 , len (mockWorker .enqueuedObjs ))
1178+ assert .Equal (t , curDiff , mockWorker .enqueuedObjs [0 ])
1179+ })
1180+ }
1181+
1182+ func TestWorkStatusController_onDelete (t * testing.T ) {
1183+ cluster := newCluster ("cluster" , clusterv1alpha1 .ClusterConditionReady , metav1 .ConditionTrue )
1184+ c := newWorkStatusController (cluster )
1185+ mockWorker := & mockAsyncWorker {}
1186+ c .worker = mockWorker
1187+
1188+ obj := & TestObject {ObjectMeta : metav1.ObjectMeta {Name : "to-delete" }}
1189+
1190+ t .Run ("direct object -> enqueue" , func (t * testing.T ) {
1191+ c .onDelete (obj )
1192+ assert .Equal (t , 1 , len (mockWorker .enqueuedObjs ))
1193+ assert .Equal (t , obj , mockWorker .enqueuedObjs [0 ])
1194+ })
1195+
1196+ t .Run ("DeletedFinalStateUnknown wrapper -> enqueue" , func (t * testing.T ) {
1197+ wrapped := cache.DeletedFinalStateUnknown {Obj : & TestObject {ObjectMeta : metav1.ObjectMeta {Name : "wrapped" }}}
1198+ c .onDelete (wrapped )
1199+ assert .Equal (t , 2 , len (mockWorker .enqueuedObjs ))
1200+ assert .Equal (t , "wrapped" , mockWorker .enqueuedObjs [1 ].(* TestObject ).Name )
1201+ })
1202+
1203+ t .Run ("DeletedFinalStateUnknown with nil Obj -> no additional enqueue" , func (t * testing.T ) {
1204+ wrappedNil := cache.DeletedFinalStateUnknown {Obj : nil }
1205+ c .onDelete (wrappedNil )
1206+ assert .Equal (t , 2 , len (mockWorker .enqueuedObjs )) // unchanged
1207+ })
1208+ }
0 commit comments