|
4 | 4 | "context" |
5 | 5 | "database/sql" |
6 | 6 | "errors" |
| 7 | + "reflect" |
7 | 8 | "testing" |
8 | 9 | "time" |
9 | 10 |
|
@@ -2205,6 +2206,145 @@ func TestUnmarshalRowsSqlNullStringEmptyVsNull(t *testing.T) { |
2205 | 2206 | }) |
2206 | 2207 | } |
2207 | 2208 |
|
| 2209 | +func TestGetValueInterface(t *testing.T) { |
| 2210 | + t.Run("non_pointer_field", func(t *testing.T) { |
| 2211 | + type testStruct struct { |
| 2212 | + Name string |
| 2213 | + Age int |
| 2214 | + } |
| 2215 | + s := testStruct{} |
| 2216 | + v := reflect.ValueOf(&s).Elem() |
| 2217 | + |
| 2218 | + nameField := v.Field(0) |
| 2219 | + result, err := getValueInterface(nameField) |
| 2220 | + assert.NoError(t, err) |
| 2221 | + assert.NotNil(t, result) |
| 2222 | + |
| 2223 | + // Should return pointer to the field |
| 2224 | + ptr, ok := result.(*string) |
| 2225 | + assert.True(t, ok) |
| 2226 | + *ptr = "test" |
| 2227 | + assert.Equal(t, "test", s.Name) |
| 2228 | + }) |
| 2229 | + |
| 2230 | + t.Run("pointer_field_nil", func(t *testing.T) { |
| 2231 | + type testStruct struct { |
| 2232 | + NamePtr *string |
| 2233 | + AgePtr *int64 |
| 2234 | + } |
| 2235 | + s := testStruct{} |
| 2236 | + v := reflect.ValueOf(&s).Elem() |
| 2237 | + |
| 2238 | + // Test with nil pointer field |
| 2239 | + namePtrField := v.Field(0) |
| 2240 | + assert.True(t, namePtrField.IsNil(), "initial pointer should be nil") |
| 2241 | + |
| 2242 | + result, err := getValueInterface(namePtrField) |
| 2243 | + assert.NoError(t, err) |
| 2244 | + assert.NotNil(t, result) |
| 2245 | + |
| 2246 | + // Should have allocated the pointer |
| 2247 | + assert.False(t, namePtrField.IsNil(), "pointer should be allocated after getValueInterface") |
| 2248 | + |
| 2249 | + // Should return pointer to pointer field |
| 2250 | + ptrPtr, ok := result.(**string) |
| 2251 | + assert.True(t, ok) |
| 2252 | + testValue := "initialized" |
| 2253 | + *ptrPtr = &testValue |
| 2254 | + assert.NotNil(t, s.NamePtr) |
| 2255 | + assert.Equal(t, "initialized", *s.NamePtr) |
| 2256 | + }) |
| 2257 | + |
| 2258 | + t.Run("pointer_field_already_allocated", func(t *testing.T) { |
| 2259 | + type testStruct struct { |
| 2260 | + NamePtr *string |
| 2261 | + } |
| 2262 | + initial := "existing" |
| 2263 | + s := testStruct{NamePtr: &initial} |
| 2264 | + v := reflect.ValueOf(&s).Elem() |
| 2265 | + |
| 2266 | + namePtrField := v.Field(0) |
| 2267 | + assert.False(t, namePtrField.IsNil(), "pointer should not be nil initially") |
| 2268 | + |
| 2269 | + result, err := getValueInterface(namePtrField) |
| 2270 | + assert.NoError(t, err) |
| 2271 | + assert.NotNil(t, result) |
| 2272 | + |
| 2273 | + // Should return pointer to pointer field |
| 2274 | + ptrPtr, ok := result.(**string) |
| 2275 | + assert.True(t, ok) |
| 2276 | + |
| 2277 | + // Verify it points to the existing value |
| 2278 | + assert.Equal(t, "existing", **ptrPtr) |
| 2279 | + |
| 2280 | + // Modify through the returned pointer |
| 2281 | + newValue := "modified" |
| 2282 | + *ptrPtr = &newValue |
| 2283 | + assert.Equal(t, "modified", *s.NamePtr) |
| 2284 | + }) |
| 2285 | + |
| 2286 | + t.Run("pointer_field_zero_value", func(t *testing.T) { |
| 2287 | + type testStruct struct { |
| 2288 | + IntPtr *int |
| 2289 | + } |
| 2290 | + s := testStruct{} |
| 2291 | + v := reflect.ValueOf(&s).Elem() |
| 2292 | + |
| 2293 | + intPtrField := v.Field(0) |
| 2294 | + result, err := getValueInterface(intPtrField) |
| 2295 | + assert.NoError(t, err) |
| 2296 | + |
| 2297 | + // After calling getValueInterface, nil pointer should be allocated |
| 2298 | + assert.NotNil(t, s.IntPtr) |
| 2299 | + |
| 2300 | + // Set zero value through returned interface |
| 2301 | + ptrPtr, ok := result.(**int) |
| 2302 | + assert.True(t, ok) |
| 2303 | + zero := 0 |
| 2304 | + *ptrPtr = &zero |
| 2305 | + assert.Equal(t, 0, *s.IntPtr) |
| 2306 | + }) |
| 2307 | + |
| 2308 | + t.Run("not_addressable_value", func(t *testing.T) { |
| 2309 | + type testStruct struct { |
| 2310 | + Name string |
| 2311 | + } |
| 2312 | + s := testStruct{Name: "test"} |
| 2313 | + v := reflect.ValueOf(s) // Non-pointer, not addressable |
| 2314 | + |
| 2315 | + nameField := v.Field(0) |
| 2316 | + result, err := getValueInterface(nameField) |
| 2317 | + assert.Error(t, err) |
| 2318 | + assert.Equal(t, ErrNotReadableValue, err) |
| 2319 | + assert.Nil(t, result) |
| 2320 | + }) |
| 2321 | + |
| 2322 | + t.Run("multiple_pointer_types", func(t *testing.T) { |
| 2323 | + type testStruct struct { |
| 2324 | + StringPtr *string |
| 2325 | + IntPtr *int |
| 2326 | + Int64Ptr *int64 |
| 2327 | + FloatPtr *float64 |
| 2328 | + BoolPtr *bool |
| 2329 | + } |
| 2330 | + s := testStruct{} |
| 2331 | + v := reflect.ValueOf(&s).Elem() |
| 2332 | + |
| 2333 | + // Test each pointer type gets properly initialized |
| 2334 | + for i := 0; i < v.NumField(); i++ { |
| 2335 | + field := v.Field(i) |
| 2336 | + assert.True(t, field.IsNil(), "field %d should start as nil", i) |
| 2337 | + |
| 2338 | + result, err := getValueInterface(field) |
| 2339 | + assert.NoError(t, err, "field %d should not error", i) |
| 2340 | + assert.NotNil(t, result, "field %d result should not be nil", i) |
| 2341 | + |
| 2342 | + // After getValueInterface, pointer should be allocated |
| 2343 | + assert.False(t, field.IsNil(), "field %d should be allocated", i) |
| 2344 | + } |
| 2345 | + }) |
| 2346 | +} |
| 2347 | + |
2208 | 2348 | func stringPtr(s string) *string { |
2209 | 2349 | return &s |
2210 | 2350 | } |
|
0 commit comments