@@ -20,6 +20,8 @@ limitations under the License.
2020package gce
2121
2222import (
23+ "maps"
24+ "reflect"
2325 "testing"
2426
2527 "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
@@ -119,10 +121,199 @@ func TestMergeMap(t *testing.T) {
119121 updates : map [string ]string {},
120122 expectedResult : map [string ]string {"key1" : "val1" },
121123 },
124+ {
125+ desc : "empty updates should result in no change" ,
126+ existing : nil ,
127+ updates : map [string ]string {},
128+ expectedResult : nil ,
129+ },
130+ {
131+ desc : "nil existing map should be initialized and updated" ,
132+ existing : nil ,
133+ updates : map [string ]string {"key" : "val" },
134+ expectedResult : map [string ]string {
135+ "key" : "val" ,
136+ },
137+ },
122138 } {
123139 t .Run (tc .desc , func (t * testing.T ) {
124- mergeMap (tc .existing , tc .updates )
125- assert .Equal (t , tc .expectedResult , tc .existing )
140+ originalExisting := maps .Clone (tc .existing )
141+
142+ result := mergeMap (tc .existing , tc .updates )
143+ assert .Equal (t , tc .expectedResult , result )
144+
145+ if tc .existing != nil {
146+ assert .Equal (t , originalExisting , tc .existing , "Input map should not be modified" )
147+ }
148+ })
149+ }
150+ }
151+
152+ func TestComputeNewAnnotationsIfNeeded (t * testing.T ) {
153+ testCases := []struct {
154+ desc string
155+ svc * v1.Service
156+ newAnnotations map [string ]string
157+ expectUpdate bool
158+ expectedAnns map [string ]string
159+ }{
160+ {
161+ desc : "nil annotations in svc, new annotations added" ,
162+ svc : & v1.Service {
163+ ObjectMeta : metav1.ObjectMeta {
164+ Name : "test-svc" ,
165+ Namespace : "test-ns" ,
166+ Annotations : nil ,
167+ },
168+ },
169+ newAnnotations : map [string ]string {"key1" : "val1" },
170+ expectUpdate : true ,
171+ expectedAnns : map [string ]string {"key1" : "val1" },
172+ },
173+ {
174+ desc : "empty annotations in svc, new annotations added" ,
175+ svc : & v1.Service {
176+ ObjectMeta : metav1.ObjectMeta {
177+ Name : "test-svc" ,
178+ Namespace : "test-ns" ,
179+ Annotations : map [string ]string {},
180+ },
181+ },
182+ newAnnotations : map [string ]string {"key1" : "val1" },
183+ expectUpdate : true ,
184+ expectedAnns : map [string ]string {"key1" : "val1" },
185+ },
186+ {
187+ desc : "existing annotations, new annotations merged" ,
188+ svc : & v1.Service {
189+ ObjectMeta : metav1.ObjectMeta {
190+ Name : "test-svc" ,
191+ Namespace : "test-ns" ,
192+ Annotations : map [string ]string {
193+ "existing1" : "old1" ,
194+ "key1" : "oldVal1" ,
195+ },
196+ },
197+ },
198+ newAnnotations : map [string ]string {"key1" : "val1" , "key2" : "val2" },
199+ expectUpdate : true ,
200+ expectedAnns : map [string ]string {
201+ "existing1" : "old1" ,
202+ "key1" : "val1" ,
203+ "key2" : "val2" ,
204+ },
205+ },
206+ {
207+ desc : "existing annotations, key removed" ,
208+ svc : & v1.Service {
209+ ObjectMeta : metav1.ObjectMeta {
210+ Name : "test-svc" ,
211+ Namespace : "test-ns" ,
212+ Annotations : map [string ]string {
213+ "key1" : "val1" ,
214+ "key2" : "val2" ,
215+ },
216+ },
217+ },
218+ newAnnotations : map [string ]string {"key1" : "" },
219+ expectUpdate : true ,
220+ expectedAnns : map [string ]string {"key2" : "val2" },
221+ },
222+ {
223+ desc : "no changes to annotations" ,
224+ svc : & v1.Service {
225+ ObjectMeta : metav1.ObjectMeta {
226+ Name : "test-svc" ,
227+ Namespace : "test-ns" ,
228+ Annotations : map [string ]string {
229+ "key1" : "val1" ,
230+ },
231+ },
232+ },
233+ newAnnotations : map [string ]string {"key1" : "val1" },
234+ expectUpdate : false ,
235+ expectedAnns : nil ,
236+ },
237+ {
238+ desc : "nil newAnnotations" ,
239+ svc : & v1.Service {
240+ ObjectMeta : metav1.ObjectMeta {
241+ Name : "test-svc" ,
242+ Namespace : "test-ns" ,
243+ Annotations : map [string ]string {
244+ "key1" : "val1" ,
245+ },
246+ },
247+ },
248+ newAnnotations : nil ,
249+ expectUpdate : false ,
250+ expectedAnns : nil ,
251+ },
252+ {
253+ desc : "empty newAnnotations" ,
254+ svc : & v1.Service {
255+ ObjectMeta : metav1.ObjectMeta {
256+ Name : "test-svc" ,
257+ Namespace : "test-ns" ,
258+ Annotations : map [string ]string {
259+ "key1" : "val1" ,
260+ },
261+ },
262+ },
263+ newAnnotations : map [string ]string {},
264+ expectUpdate : false ,
265+ expectedAnns : nil ,
266+ },
267+ {
268+ desc : "nil annotations in svc, empty new annotations" ,
269+ svc : & v1.Service {
270+ ObjectMeta : metav1.ObjectMeta {
271+ Name : "test-svc" ,
272+ Namespace : "test-ns" ,
273+ Annotations : nil ,
274+ },
275+ },
276+ newAnnotations : map [string ]string {},
277+ expectUpdate : false ,
278+ expectedAnns : nil ,
279+ },
280+ }
281+
282+ for _ , tc := range testCases {
283+ t .Run (tc .desc , func (t * testing.T ) {
284+ // Capture original annotations to check for unintended modifications
285+ originalAnnotations := make (map [string ]string )
286+ if tc .svc .Annotations != nil {
287+ for k , v := range tc .svc .Annotations {
288+ originalAnnotations [k ] = v
289+ }
290+ }
291+
292+ newMeta , needsUpdate := computeNewAnnotationsIfNeeded (tc .svc , tc .newAnnotations )
293+
294+ assert .Equal (t , tc .expectUpdate , needsUpdate )
295+
296+ if tc .expectUpdate {
297+ assert .NotNil (t , newMeta )
298+ assert .True (t , reflect .DeepEqual (tc .expectedAnns , newMeta .Annotations ), "Expected annotations: %v, got: %v" , tc .expectedAnns , newMeta .Annotations )
299+ // Ensure the map was actually changed from the original
300+ assert .False (t , reflect .DeepEqual (originalAnnotations , newMeta .Annotations ), "Annotations should have changed, but match original" )
301+ } else {
302+ assert .Nil (t , newMeta )
303+ // Ensure original service annotations are not modified
304+ if len (originalAnnotations ) == 0 && tc .svc .Annotations == nil {
305+ // Special case: nil to nil is fine
306+ } else {
307+ assert .True (t , reflect .DeepEqual (originalAnnotations , tc .svc .Annotations ), "Original svc.Annotations should not be modified on no update" )
308+ }
309+ }
310+
311+ // Always check that the original service object's annotations map instance is not a different instance if no update was needed.
312+ if ! needsUpdate {
313+ if len (originalAnnotations ) > 0 || (len (originalAnnotations ) == 0 && tc .svc .Annotations != nil ) {
314+ assert .True (t , reflect .DeepEqual (originalAnnotations , tc .svc .Annotations ), "Original svc.Annotations instance should not change when no update is needed" )
315+ }
316+ }
126317 })
127318 }
128319}
0 commit comments