@@ -24,25 +24,30 @@ import (
2424 "github.com/stretchr/testify/assert"
2525 corev1 "k8s.io/api/core/v1"
2626 apierrors "k8s.io/apimachinery/pkg/api/errors"
27+ "k8s.io/apimachinery/pkg/api/meta"
2728 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829 "k8s.io/apimachinery/pkg/runtime"
2930 "k8s.io/apimachinery/pkg/types"
3031 "sigs.k8s.io/controller-runtime/pkg/client"
3132 "sigs.k8s.io/controller-runtime/pkg/client/fake"
3233 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
34+
35+ workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
3336)
3437
3538func TestUpdateStatus (t * testing.T ) {
3639 scheme := runtime .NewScheme ()
3740 _ = corev1 .AddToScheme (scheme )
41+ _ = workv1alpha1 .Install (scheme )
3842
3943 tests := []struct {
4044 name string
41- setupObj * corev1. Pod
42- mutateStatus func (* corev1. Pod )
45+ setupObj client. Object
46+ mutateStatus func (client. Object )
4347 statusError bool
4448 expectedOp controllerutil.OperationResult
4549 expectedError string
50+ verify func (* testing.T , client.Client )
4651 }{
4752 {
4853 name : "successful status update" ,
@@ -55,12 +60,18 @@ func TestUpdateStatus(t *testing.T) {
5560 Phase : corev1 .PodPending ,
5661 },
5762 },
58- mutateStatus : func (pod * corev1.Pod ) {
63+ mutateStatus : func (obj client.Object ) {
64+ pod := obj .(* corev1.Pod )
5965 pod .Status .Phase = corev1 .PodRunning
6066 },
6167 statusError : false ,
6268 expectedOp : controllerutil .OperationResultUpdatedStatusOnly ,
6369 expectedError : "" ,
70+ verify : func (t * testing.T , c client.Client ) {
71+ pod := & corev1.Pod {}
72+ assert .NoError (t , c .Get (context .TODO (), types.NamespacedName {Name : "test-pod" , Namespace : "default" }, pod ))
73+ assert .Equal (t , corev1 .PodRunning , pod .Status .Phase )
74+ },
6475 },
6576 {
6677 name : "status update error" ,
@@ -73,15 +84,16 @@ func TestUpdateStatus(t *testing.T) {
7384 Phase : corev1 .PodPending ,
7485 },
7586 },
76- mutateStatus : func (pod * corev1.Pod ) {
87+ mutateStatus : func (obj client.Object ) {
88+ pod := obj .(* corev1.Pod )
7789 pod .Status .Phase = corev1 .PodRunning
7890 },
7991 statusError : true ,
8092 expectedOp : controllerutil .OperationResultNone ,
8193 expectedError : "Internal error occurred: status update failed" ,
8294 },
8395 {
84- name : "no changes needed " ,
96+ name : "no changes, should still update " ,
8597 setupObj : & corev1.Pod {
8698 ObjectMeta : metav1.ObjectMeta {
8799 Name : "test-pod" ,
@@ -91,22 +103,54 @@ func TestUpdateStatus(t *testing.T) {
91103 Phase : corev1 .PodRunning ,
92104 },
93105 },
94- mutateStatus : func (_ * corev1. Pod ) {
95- // No changes to status
106+ mutateStatus : func (_ client. Object ) {
107+ // No changes
96108 },
97109 statusError : false ,
98- expectedOp : controllerutil .OperationResultNone ,
110+ expectedOp : controllerutil .OperationResultUpdatedStatusOnly ,
99111 expectedError : "" ,
112+ verify : func (t * testing.T , c client.Client ) {
113+ pod := & corev1.Pod {}
114+ assert .NoError (t , c .Get (context .TODO (), types.NamespacedName {Name : "test-pod" , Namespace : "default" }, pod ))
115+ assert .Equal (t , corev1 .PodRunning , pod .Status .Phase )
116+ },
100117 },
101118 {
102- name : "object not found" ,
103- setupObj : nil , // Not create the object
104- mutateStatus : func (pod * corev1.Pod ) {
105- pod .Status .Phase = corev1 .PodRunning
119+ name : "update with same WorkApplied condition, should still update" ,
120+ setupObj : & workv1alpha1.Work {
121+ ObjectMeta : metav1.ObjectMeta {
122+ Name : "test-work" ,
123+ Namespace : "default" ,
124+ },
125+ Status : workv1alpha1.WorkStatus {
126+ Conditions : []metav1.Condition {
127+ {
128+ Type : workv1alpha1 .WorkApplied ,
129+ Status : metav1 .ConditionTrue ,
130+ Reason : "AppliedSuccessful" ,
131+ Message : "Manifest has been successfully applied" ,
132+ },
133+ },
134+ },
135+ },
136+ mutateStatus : func (obj client.Object ) {
137+ meta .SetStatusCondition (& obj .(* workv1alpha1.Work ).Status .Conditions , metav1.Condition {
138+ Type : workv1alpha1 .WorkApplied ,
139+ Status : metav1 .ConditionTrue ,
140+ Reason : "AppliedSuccessful" ,
141+ Message : "Manifest has been successfully applied" ,
142+ })
143+ },
144+ expectedOp : controllerutil .OperationResultUpdatedStatusOnly ,
145+ verify : func (t * testing.T , c client.Client ) {
146+ work := & workv1alpha1.Work {}
147+ assert .NoError (t , c .Get (context .TODO (), types.NamespacedName {Name : "test-work" , Namespace : "default" }, work ))
148+
149+ cond := meta .FindStatusCondition (work .Status .Conditions , workv1alpha1 .WorkApplied )
150+ assert .NotNil (t , cond )
151+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
152+ assert .Equal (t , "AppliedSuccessful" , cond .Reason )
106153 },
107- statusError : false ,
108- expectedOp : controllerutil .OperationResultNone ,
109- expectedError : "not found" ,
110154 },
111155 {
112156 name : "mutation error" ,
@@ -116,9 +160,9 @@ func TestUpdateStatus(t *testing.T) {
116160 Namespace : "default" ,
117161 },
118162 },
119- mutateStatus : func (pod * corev1. Pod ) {
163+ mutateStatus : func (obj client. Object ) {
120164 // Simulate mutation error by changing name
121- pod . Name = "different-name"
165+ obj . SetName ( "different-name" )
122166 },
123167 statusError : false ,
124168 expectedOp : controllerutil .OperationResultNone ,
@@ -131,31 +175,19 @@ func TestUpdateStatus(t *testing.T) {
131175 clientBuilder := fake .NewClientBuilder ().WithScheme (scheme )
132176 if tt .setupObj != nil {
133177 clientBuilder = clientBuilder .WithObjects (tt .setupObj )
134- }
135- fakeClient := clientBuilder .Build ()
136-
137- var client client.Client
138- if tt .statusError {
139- client = & mockClient {
140- Client : fakeClient ,
141- shouldError : true ,
178+ if _ , ok := tt .setupObj .(* workv1alpha1.Work ); ok {
179+ clientBuilder = clientBuilder .WithStatusSubresource (tt .setupObj )
142180 }
143- } else {
144- client = fakeClient
145181 }
146182
147- // Create a new object for update
148- obj := & corev1.Pod {
149- ObjectMeta : metav1.ObjectMeta {
150- Name : "test-pod" ,
151- Namespace : "default" ,
152- },
183+ c := client .Client (clientBuilder .Build ())
184+ if tt .statusError {
185+ c = & mockClient {Client : c , shouldError : true }
153186 }
154187
155- // Run the update
156- op , err := UpdateStatus (context .TODO (), client , obj , func () error {
188+ op , err := UpdateStatus (context .TODO (), c , tt .setupObj , func () error {
157189 if tt .mutateStatus != nil {
158- tt .mutateStatus (obj )
190+ tt .mutateStatus (tt . setupObj )
159191 }
160192 return nil
161193 })
@@ -171,12 +203,8 @@ func TestUpdateStatus(t *testing.T) {
171203 // Check operation result
172204 assert .Equal (t , tt .expectedOp , op )
173205
174- // If successful update, verify the status was actually changed
175- if tt .expectedOp == controllerutil .OperationResultUpdatedStatusOnly {
176- updatedPod := & corev1.Pod {}
177- err := client .Get (context .TODO (), types.NamespacedName {Name : "test-pod" , Namespace : "default" }, updatedPod )
178- assert .NoError (t , err )
179- assert .Equal (t , corev1 .PodRunning , updatedPod .Status .Phase )
206+ if tt .verify != nil {
207+ tt .verify (t , c )
180208 }
181209 })
182210 }
@@ -359,6 +387,13 @@ func (m *mockStatusWriter) Update(_ context.Context, _ client.Object, _ ...clien
359387 return nil
360388}
361389
390+ func (m * mockStatusWriter ) Patch (_ context.Context , _ client.Object , _ client.Patch , _ ... client.SubResourcePatchOption ) error {
391+ if m .shouldError {
392+ return apierrors .NewInternalError (fmt .Errorf ("status update failed" ))
393+ }
394+ return nil
395+ }
396+
362397// mockClient wraps the fake client and returns our mock status writer
363398type mockClient struct {
364399 client.Client
0 commit comments