Skip to content

Commit 34c032d

Browse files
committed
add +k8s:immutable tests
1 parent 802b122 commit 34c032d

File tree

4 files changed

+876
-2
lines changed

4 files changed

+876
-2
lines changed

staging/src/k8s.io/apimachinery/pkg/api/validate/immutable_test.go

Lines changed: 312 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func TestFrozenByCompare(t *testing.T) {
109109
}} {
110110
t.Run(tc.name, func(t *testing.T) {
111111
errs := tc.fn(operation.Operation{Type: operation.Create}, field.NewPath(""))
112-
if len(errs) != 0 { // Create should always succeed
112+
if len(errs) != 0 {
113113
t.Errorf("case %q (create): expected success: %v", tc.name, errs)
114114
}
115115
errs = tc.fn(operation.Operation{Type: operation.Update}, field.NewPath(""))
@@ -236,7 +236,317 @@ func TestFrozenByReflect(t *testing.T) {
236236
}} {
237237
t.Run(tc.name, func(t *testing.T) {
238238
errs := tc.fn(operation.Operation{Type: operation.Create}, field.NewPath(""))
239-
if len(errs) != 0 { // Create should always succeed
239+
if len(errs) != 0 {
240+
t.Errorf("case %q (create): expected success: %v", tc.name, errs)
241+
}
242+
errs = tc.fn(operation.Operation{Type: operation.Update}, field.NewPath(""))
243+
if tc.fail && len(errs) == 0 {
244+
t.Errorf("case %q (update): expected failure", tc.name)
245+
} else if !tc.fail && len(errs) != 0 {
246+
t.Errorf("case %q (update): expected success: %v", tc.name, errs)
247+
}
248+
})
249+
}
250+
}
251+
252+
func TestImmutableValueByCompare(t *testing.T) {
253+
for _, tc := range []struct {
254+
name string
255+
fn func(operation.Operation, *field.Path) field.ErrorList
256+
fail bool
257+
}{{
258+
name: "nil both values",
259+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
260+
return ImmutableValueByCompare[int](context.Background(), op, fld, nil, nil)
261+
},
262+
}, {
263+
name: "nil value pointer",
264+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
265+
return ImmutableValueByCompare(context.Background(), op, fld, nil, ptr.To(123))
266+
},
267+
fail: true,
268+
}, {
269+
name: "nil oldValue pointer",
270+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
271+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(123), nil)
272+
},
273+
}, {
274+
name: "int zero to non-zero (unset to set)",
275+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
276+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(123), ptr.To(0))
277+
},
278+
}, {
279+
name: "int non-zero to zero (clear)",
280+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
281+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(0), ptr.To(123))
282+
},
283+
fail: true,
284+
}, {
285+
name: "int modify",
286+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
287+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(456), ptr.To(123))
288+
},
289+
fail: true,
290+
}, {
291+
name: "int same value",
292+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
293+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(123), ptr.To(123))
294+
},
295+
}, {
296+
name: "string empty to non-empty (unset to set)",
297+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
298+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To("abc"), ptr.To(""))
299+
},
300+
}, {
301+
name: "string non-empty to empty (clear)",
302+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
303+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(""), ptr.To("abc"))
304+
},
305+
fail: true,
306+
}, {
307+
name: "string modify",
308+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
309+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To("xyz"), ptr.To("abc"))
310+
},
311+
fail: true,
312+
}, {
313+
name: "bool false to true (unset to set)",
314+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
315+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(true), ptr.To(false))
316+
},
317+
}, {
318+
name: "bool true to false (clear)",
319+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
320+
return ImmutableValueByCompare(context.Background(), op, fld, ptr.To(false), ptr.To(true))
321+
},
322+
fail: true,
323+
}} {
324+
t.Run(tc.name, func(t *testing.T) {
325+
errs := tc.fn(operation.Operation{Type: operation.Create}, field.NewPath(""))
326+
if len(errs) != 0 {
327+
t.Errorf("case %q (create): expected success: %v", tc.name, errs)
328+
}
329+
errs = tc.fn(operation.Operation{Type: operation.Update}, field.NewPath(""))
330+
if tc.fail && len(errs) == 0 {
331+
t.Errorf("case %q (update): expected failure", tc.name)
332+
} else if !tc.fail && len(errs) != 0 {
333+
t.Errorf("case %q (update): expected success: %v", tc.name, errs)
334+
}
335+
})
336+
}
337+
}
338+
339+
func TestImmutablePointerByCompare(t *testing.T) {
340+
for _, tc := range []struct {
341+
name string
342+
fn func(operation.Operation, *field.Path) field.ErrorList
343+
fail bool
344+
}{{
345+
name: "nil both values",
346+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
347+
return ImmutablePointerByCompare[int](context.Background(), op, fld, nil, nil)
348+
},
349+
}, {
350+
name: "nil to non-nil (unset to set)",
351+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
352+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(123), nil)
353+
},
354+
}, {
355+
name: "non-nil to nil (clear)",
356+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
357+
return ImmutablePointerByCompare(context.Background(), op, fld, nil, ptr.To(123))
358+
},
359+
fail: true,
360+
}, {
361+
name: "int pointer same value",
362+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
363+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(123), ptr.To(123))
364+
},
365+
}, {
366+
name: "int pointer modify",
367+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
368+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(456), ptr.To(123))
369+
},
370+
fail: true,
371+
}, {
372+
name: "string pointer nil to empty string (unset to set)",
373+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
374+
var oldVal *string = nil
375+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(""), oldVal)
376+
},
377+
}, {
378+
name: "string pointer empty to non-empty (modify)",
379+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
380+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To("abc"), ptr.To(""))
381+
},
382+
fail: true,
383+
}, {
384+
name: "bool pointer nil to false (unset to set)",
385+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
386+
var oldVal *bool = nil
387+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(false), oldVal)
388+
},
389+
}, {
390+
name: "bool pointer false to true (modify)",
391+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
392+
return ImmutablePointerByCompare(context.Background(), op, fld, ptr.To(true), ptr.To(false))
393+
},
394+
fail: true,
395+
}} {
396+
t.Run(tc.name, func(t *testing.T) {
397+
errs := tc.fn(operation.Operation{Type: operation.Create}, field.NewPath(""))
398+
if len(errs) != 0 {
399+
t.Errorf("case %q (create): expected success: %v", tc.name, errs)
400+
}
401+
errs = tc.fn(operation.Operation{Type: operation.Update}, field.NewPath(""))
402+
if tc.fail && len(errs) == 0 {
403+
t.Errorf("case %q (update): expected failure", tc.name)
404+
} else if !tc.fail && len(errs) != 0 {
405+
t.Errorf("case %q (update): expected success: %v", tc.name, errs)
406+
}
407+
})
408+
}
409+
}
410+
411+
func TestImmutableByReflect(t *testing.T) {
412+
emptySlice := []string{}
413+
nonEmptySlice := []string{"a", "b", "c"}
414+
emptyMap := map[string]string{}
415+
nonEmptyMap := map[string]string{"key": "value"}
416+
417+
structA := StructNonComparable{
418+
S: "abc",
419+
SP: ptr.To("abc"),
420+
I: 123,
421+
IP: ptr.To(123),
422+
B: true,
423+
BP: ptr.To(true),
424+
SS: []string{"a", "b", "c"},
425+
MSS: map[string]string{"a": "b", "c": "d"},
426+
}
427+
428+
structB := StructNonComparable{
429+
S: "xyz",
430+
SP: ptr.To("xyz"),
431+
I: 456,
432+
IP: ptr.To(456),
433+
B: false,
434+
BP: ptr.To(false),
435+
SS: []string{"x", "y", "z"},
436+
MSS: map[string]string{"x": "X", "y": "Y"},
437+
}
438+
439+
structZero := StructNonComparable{}
440+
441+
for _, tc := range []struct {
442+
name string
443+
fn func(operation.Operation, *field.Path) field.ErrorList
444+
fail bool
445+
}{{
446+
name: "slice nil to empty (both unset)",
447+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
448+
var nilSlice []string
449+
return ImmutableByReflect(context.Background(), op, fld, emptySlice, nilSlice)
450+
},
451+
}, {
452+
name: "slice empty to nil (both unset)",
453+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
454+
var nilSlice []string
455+
return ImmutableByReflect(context.Background(), op, fld, nilSlice, emptySlice)
456+
},
457+
}, {
458+
name: "slice nil to non-empty (unset to set)",
459+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
460+
var nilSlice []string
461+
return ImmutableByReflect(context.Background(), op, fld, nonEmptySlice, nilSlice)
462+
},
463+
}, {
464+
name: "slice non-empty to nil (clear)",
465+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
466+
var nilSlice []string
467+
return ImmutableByReflect(context.Background(), op, fld, nilSlice, nonEmptySlice)
468+
},
469+
fail: true,
470+
}, {
471+
name: "slice modify",
472+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
473+
slice1 := []string{"a", "b"}
474+
slice2 := []string{"x", "y"}
475+
return ImmutableByReflect(context.Background(), op, fld, slice2, slice1)
476+
},
477+
fail: true,
478+
}, {
479+
name: "map nil to empty (both unset)",
480+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
481+
var nilMap map[string]string
482+
return ImmutableByReflect(context.Background(), op, fld, emptyMap, nilMap)
483+
},
484+
}, {
485+
name: "map nil to non-empty (unset to set)",
486+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
487+
var nilMap map[string]string
488+
return ImmutableByReflect(context.Background(), op, fld, nonEmptyMap, nilMap)
489+
},
490+
}, {
491+
name: "map non-empty to nil (clear)",
492+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
493+
var nilMap map[string]string
494+
return ImmutableByReflect(context.Background(), op, fld, nilMap, nonEmptyMap)
495+
},
496+
fail: true,
497+
}, {
498+
name: "struct zero to non-zero (unset to set)",
499+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
500+
return ImmutableByReflect(context.Background(), op, fld, structA, structZero)
501+
},
502+
}, {
503+
name: "struct non-zero to zero (clear)",
504+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
505+
return ImmutableByReflect(context.Background(), op, fld, structZero, structA)
506+
},
507+
fail: true,
508+
}, {
509+
name: "struct modify",
510+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
511+
return ImmutableByReflect(context.Background(), op, fld, structB, structA)
512+
},
513+
fail: true,
514+
}, {
515+
name: "struct same value",
516+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
517+
structA2 := structA
518+
return ImmutableByReflect(context.Background(), op, fld, structA2, structA)
519+
},
520+
}, {
521+
name: "pointer to struct - nil to non-nil (unset to set)",
522+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
523+
return ImmutableByReflect(context.Background(), op, fld, &structA, (*StructNonComparable)(nil))
524+
},
525+
}, {
526+
name: "pointer to struct - non-nil to nil (clear)",
527+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
528+
return ImmutableByReflect(context.Background(), op, fld, (*StructNonComparable)(nil), &structA)
529+
},
530+
fail: true,
531+
}, {
532+
name: "int value zero to non-zero (unset to set)",
533+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
534+
return ImmutableByReflect(context.Background(), op, fld, 123, 0)
535+
},
536+
}, {
537+
name: "string empty to non-empty (unset to set)",
538+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
539+
return ImmutableByReflect(context.Background(), op, fld, "hello", "")
540+
},
541+
}, {
542+
name: "bool false to true (unset to set)",
543+
fn: func(op operation.Operation, fld *field.Path) field.ErrorList {
544+
return ImmutableByReflect(context.Background(), op, fld, true, false)
545+
},
546+
}} {
547+
t.Run(tc.name, func(t *testing.T) {
548+
errs := tc.fn(operation.Operation{Type: operation.Create}, field.NewPath(""))
549+
if len(errs) != 0 {
240550
t.Errorf("case %q (create): expected success: %v", tc.name, errs)
241551
}
242552
errs = tc.fn(operation.Operation{Type: operation.Update}, field.NewPath(""))

0 commit comments

Comments
 (0)