@@ -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