deepEqual recursively verifies that all values are equal but pointers are different except for the Expires field which is explicitly allowed to be shared
(t *testing.T, left, right reflect.Value, fieldPath string)
| 22 | // deepEqual recursively verifies that all values are equal but pointers are different |
| 23 | // except for the Expires field which is explicitly allowed to be shared |
| 24 | func deepEqual(t *testing.T, left, right reflect.Value, fieldPath string) { |
| 25 | t.Helper() |
| 26 | |
| 27 | require.True(t, left.IsValid() && right.IsValid(), "invalid value at %s", fieldPath) |
| 28 | require.Equal(t, left.Type(), right.Type(), "types are not equal at %s", fieldPath) |
| 29 | |
| 30 | switch left.Kind() { |
| 31 | case reflect.Pointer: |
| 32 | // Pointers should not be nil and must point to different objects |
| 33 | require.False(t, left.IsNil(), "nil pointer at %s (left)", fieldPath) |
| 34 | require.False(t, right.IsNil(), "nil pointer at %s (right)", fieldPath) |
| 35 | require.NotSame(t, left.Interface(), right.Interface(), "shared pointer at %s", fieldPath) |
| 36 | |
| 37 | deepEqual(t, left.Elem(), right.Elem(), fieldPath) |
| 38 | |
| 39 | case reflect.Slice: |
| 40 | // Slices should contain some elements and must not share the same underlying array |
| 41 | require.Equal(t, left.Len(), right.Len(), "slice length mismatch at %s", fieldPath) |
| 42 | require.NotEmpty(t, left.Len(), "slice must not be empty %s (left)", fieldPath) |
| 43 | require.NotEmpty(t, right.Len(), "slice must not be empty %s (right)", fieldPath) |
| 44 | require.NotEqual(t, left.Pointer(), right.Pointer(), "shared slices at %s", fieldPath) |
| 45 | |
| 46 | // Recursively verify slice elements |
| 47 | for i := range left.Len() { |
| 48 | elemPath := buildPath(fieldPath, "[", anyToString(i), "]") |
| 49 | deepEqual(t, left.Index(i), right.Index(i), elemPath) |
| 50 | } |
| 51 | |
| 52 | case reflect.Map: |
| 53 | // Maps should contain some elements and must not share the same underlying map |
| 54 | require.Equal(t, left.Len(), right.Len(), "map length mismatch at %s", fieldPath) |
| 55 | require.NotEmpty(t, left.Len(), "map must not be empty %s (left)", fieldPath) |
| 56 | require.NotEmpty(t, right.Len(), "map must not be empty %s (right)", fieldPath) |
| 57 | require.NotEqual(t, left.Pointer(), right.Pointer(), "shared maps at %s", fieldPath) |
| 58 | |
| 59 | // Recursively verify map values |
| 60 | for _, key := range left.MapKeys() { |
| 61 | keyStr := anyToString(key.Interface()) |
| 62 | keyPath := buildPath(fieldPath, "[", keyStr, "]") |
| 63 | originalMapVal := left.MapIndex(key) |
| 64 | clonedMapVal := right.MapIndex(key) |
| 65 | deepEqual(t, originalMapVal, clonedMapVal, keyPath) |
| 66 | } |
| 67 | |
| 68 | case reflect.Struct: |
| 69 | require.Equal(t, left.Interface(), right.Interface(), "structs are not equal at %s", fieldPath) |
| 70 | |
| 71 | // Fallback to recursive field-by-field comparison |
| 72 | for i := range left.NumField() { |
| 73 | field := left.Type().Field(i) |
| 74 | if !field.IsExported() { |
| 75 | continue // Skip unexported fields |
| 76 | } |
| 77 | |
| 78 | nestedPath := buildPath(fieldPath, ".", field.Name, "") |
| 79 | originalFieldVal := left.Field(i) |
| 80 | clonedFieldVal := right.Field(i) |
| 81 | deepEqual(t, originalFieldVal, clonedFieldVal, nestedPath) |
no test coverage detected