feat(reflection): add tests for standard SQL null types

* Implement tests for mapping standard library sql.Null* types to struct.
* Verify handling of valid and nil values for sql.NullInt64, sql.NullString, sql.NullFloat64, sql.NullBool, and sql.NullTime.
* Ensure correct error handling and type conversion in MapToStruct function.
This commit is contained in:
Hein
2026-01-13 12:18:13 +02:00
parent 0ac207d80f
commit cf6a81e805
2 changed files with 129 additions and 9 deletions

View File

@@ -1102,6 +1102,12 @@ func setFieldValue(field reflect.Value, value interface{}) error {
}
}
// If we can convert the type, do it
if valueReflect.Type().ConvertibleTo(field.Type()) {
field.Set(valueReflect.Convert(field.Type()))
return nil
}
// Handle struct types (like SqlTimeStamp, SqlDate, SqlTime which wrap SqlNull[time.Time])
if field.Kind() == reflect.Struct {
@@ -1113,9 +1119,9 @@ func setFieldValue(field reflect.Value, value interface{}) error {
// Call the Scan method with the value
results := scanMethod.Call([]reflect.Value{reflect.ValueOf(value)})
if len(results) > 0 {
// Check if there was an error
if err, ok := results[0].Interface().(error); ok && err != nil {
return err
// The Scan method returns error - check if it's nil
if !results[0].IsNil() {
return results[0].Interface().(error)
}
return nil
}
@@ -1170,12 +1176,6 @@ func setFieldValue(field reflect.Value, value interface{}) error {
}
// If we can convert the type, do it
if valueReflect.Type().ConvertibleTo(field.Type()) {
field.Set(valueReflect.Convert(field.Type()))
return nil
}
return fmt.Errorf("cannot convert %v to %v", valueReflect.Type(), field.Type())
}

View File

@@ -0,0 +1,120 @@
package reflection_test
import (
"database/sql"
"testing"
"time"
"github.com/bitechdev/ResolveSpec/pkg/reflection"
)
func TestMapToStruct_StandardSqlNullTypes(t *testing.T) {
// Test model with standard library sql.Null* types
type TestModel struct {
ID int64 `bun:"id,pk" json:"id"`
Age sql.NullInt64 `bun:"age" json:"age"`
Name sql.NullString `bun:"name" json:"name"`
Score sql.NullFloat64 `bun:"score" json:"score"`
Active sql.NullBool `bun:"active" json:"active"`
UpdatedAt sql.NullTime `bun:"updated_at" json:"updated_at"`
}
now := time.Now()
dataMap := map[string]any{
"id": int64(100),
"age": int64(25),
"name": "John Doe",
"score": 95.5,
"active": true,
"updated_at": now,
}
var result TestModel
err := reflection.MapToStruct(dataMap, &result)
if err != nil {
t.Fatalf("MapToStruct() error = %v", err)
}
// Verify ID
if result.ID != 100 {
t.Errorf("ID = %v, want 100", result.ID)
}
// Verify Age (sql.NullInt64)
if !result.Age.Valid {
t.Error("Age.Valid = false, want true")
}
if result.Age.Int64 != 25 {
t.Errorf("Age.Int64 = %v, want 25", result.Age.Int64)
}
// Verify Name (sql.NullString)
if !result.Name.Valid {
t.Error("Name.Valid = false, want true")
}
if result.Name.String != "John Doe" {
t.Errorf("Name.String = %v, want 'John Doe'", result.Name.String)
}
// Verify Score (sql.NullFloat64)
if !result.Score.Valid {
t.Error("Score.Valid = false, want true")
}
if result.Score.Float64 != 95.5 {
t.Errorf("Score.Float64 = %v, want 95.5", result.Score.Float64)
}
// Verify Active (sql.NullBool)
if !result.Active.Valid {
t.Error("Active.Valid = false, want true")
}
if !result.Active.Bool {
t.Error("Active.Bool = false, want true")
}
// Verify UpdatedAt (sql.NullTime)
if !result.UpdatedAt.Valid {
t.Error("UpdatedAt.Valid = false, want true")
}
if !result.UpdatedAt.Time.Equal(now) {
t.Errorf("UpdatedAt.Time = %v, want %v", result.UpdatedAt.Time, now)
}
t.Log("All standard library sql.Null* types handled correctly!")
}
func TestMapToStruct_StandardSqlNullTypes_WithNil(t *testing.T) {
// Test nil handling for standard library sql.Null* types
type TestModel struct {
ID int64 `bun:"id,pk" json:"id"`
Age sql.NullInt64 `bun:"age" json:"age"`
Name sql.NullString `bun:"name" json:"name"`
}
dataMap := map[string]any{
"id": int64(200),
"age": int64(30),
"name": nil, // Explicitly nil
}
var result TestModel
err := reflection.MapToStruct(dataMap, &result)
if err != nil {
t.Fatalf("MapToStruct() error = %v", err)
}
// Age should be valid
if !result.Age.Valid {
t.Error("Age.Valid = false, want true")
}
if result.Age.Int64 != 30 {
t.Errorf("Age.Int64 = %v, want 30", result.Age.Int64)
}
// Name should be invalid (null)
if result.Name.Valid {
t.Error("Name.Valid = true, want false (null)")
}
t.Log("Nil handling for sql.Null* types works correctly!")
}