package util_test import ( "database/sql" "reflect" "strings" "sync" "testing" "time" "github.com/doug-martin/goqu/v9/internal/util" "github.com/stretchr/testify/suite" ) var ( uints = []interface{}{ uint(10), uint8(10), uint16(10), uint32(10), uint64(10), } ints = []interface{}{ int(10), int8(10), int16(10), int32(10), int64(10), } floats = []interface{}{ float32(3.14), float64(3.14), } strs = []interface{}{ "abc", "", } bools = []interface{}{ true, false, } structs = []interface{}{ sql.NullString{}, } invalids = []interface{}{ nil, } pointers = []interface{}{ &sql.NullString{}, } ) type ( TestInterface interface { A() string } TestInterfaceImpl struct { str string } TestStruct struct { arr [0]string slc []string mp map[string]interface{} str string bl bool i int i8 int8 i16 int16 i32 int32 i64 int64 ui uint ui8 uint8 ui16 uint16 ui32 uint32 ui64 uint64 f32 float32 f64 float64 intr TestInterface ptr *sql.NullString } ) func (t TestInterfaceImpl) A() string { return t.str } type reflectTest struct { suite.Suite } func (rt *reflectTest) TestIsUint() { for _, v := range uints { rt.True(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsUint(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsInt() { for _, v := range ints { rt.True(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsInt(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsFloat() { for _, v := range floats { rt.True(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsFloat(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsString() { for _, v := range strs { rt.True(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsString(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsBool() { for _, v := range bools { rt.True(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsBool(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsStruct() { for _, v := range structs { rt.True(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsStruct(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsSlice() { rt.True(util.IsSlice(reflect.ValueOf(uints).Kind())) rt.True(util.IsSlice(reflect.ValueOf(ints).Kind())) rt.True(util.IsSlice(reflect.ValueOf(floats).Kind())) rt.True(util.IsSlice(reflect.ValueOf(structs).Kind())) rt.False(util.IsSlice(reflect.ValueOf(structs[0]).Kind())) } func (rt *reflectTest) TestIsInvalid() { for _, v := range invalids { rt.True(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } for _, v := range pointers { rt.False(util.IsInvalid(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsPointer() { for _, v := range pointers { rt.True(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range uints { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range ints { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range floats { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range bools { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range strs { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range structs { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } for _, v := range invalids { rt.False(util.IsPointer(reflect.ValueOf(v).Kind())) } } func (rt *reflectTest) TestIsEmptyValue_emptyValues() { ts := TestStruct{} rt.True(util.IsEmptyValue(reflect.ValueOf(ts.arr))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.slc))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.mp))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.str))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.bl))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.i))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.i8))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.i16))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.i32))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.i64))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ui))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ui8))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ui16))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ui32))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ui64))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.f32))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.f64))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.intr))) rt.True(util.IsEmptyValue(reflect.ValueOf(ts.ptr))) } func (rt *reflectTest) TestIsEmptyValue_validValues() { ts := TestStruct{intr: TestInterfaceImpl{"hello"}} rt.False(util.IsEmptyValue(reflect.ValueOf([1]string{"a"}))) rt.False(util.IsEmptyValue(reflect.ValueOf([]string{"a"}))) rt.False(util.IsEmptyValue(reflect.ValueOf(map[string]interface{}{"a": true}))) rt.False(util.IsEmptyValue(reflect.ValueOf("str"))) rt.False(util.IsEmptyValue(reflect.ValueOf(true))) rt.False(util.IsEmptyValue(reflect.ValueOf(int(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(int8(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(int16(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(int32(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(int64(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(uint(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(uint8(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(uint16(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(uint32(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(uint64(1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(float32(0.1)))) rt.False(util.IsEmptyValue(reflect.ValueOf(float64(0.2)))) rt.False(util.IsEmptyValue(reflect.ValueOf(ts.intr))) rt.False(util.IsEmptyValue(reflect.ValueOf(&TestStruct{str: "a"}))) } func (rt *reflectTest) TestColumnRename() { // different key names are used each time to circumvent the caching that happens // it seems like a solid assumption that when people use this feature, // they would simply set a renaming function once at startup, // and not change between requests like this lowerAnon := struct { FirstLower string LastLower string }{} lowerColumnMap, lowerErr := util.GetColumnMap(&lowerAnon) rt.NoError(lowerErr) lowerKeys := make([]string, 0, len(lowerColumnMap)) for key := range lowerColumnMap { lowerKeys = append(lowerKeys, key) } rt.Contains(lowerKeys, "firstlower") rt.Contains(lowerKeys, "lastlower") // changing rename function util.SetColumnRenameFunction(strings.ToUpper) upperAnon := struct { FirstUpper string LastUpper string }{} upperColumnMap, upperErr := util.GetColumnMap(&upperAnon) rt.NoError(upperErr) upperKeys := make([]string, 0, len(upperColumnMap)) for key := range upperColumnMap { upperKeys = append(upperKeys, key) } rt.Contains(upperKeys, "FIRSTUPPER") rt.Contains(upperKeys, "LASTUPPER") util.SetColumnRenameFunction(util.DefaultColumnRenameFunction) } func (rt *reflectTest) TestParallelGetColumnMap() { type item struct { id uint name string } wg := sync.WaitGroup{} wg.Add(1) go func() { i := item{id: 1, name: "bob"} m, err := util.GetColumnMap(i) rt.NoError(err) rt.NotNil(m) wg.Done() }() wg.Add(1) go func() { i := item{id: 2, name: "sally"} m, err := util.GetColumnMap(i) rt.NoError(err) rt.NotNil(m) wg.Done() }() wg.Wait() } func (rt *reflectTest) TestAssignStructVals_withStruct() { type TestStruct struct { Str string Int int64 Bool bool Valuer sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) data := map[string]interface{}{ "str": "string", "int": int64(10), "bool": true, "valuer": sql.NullString{String: "null_str", Valid: true}, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ Str: "string", Int: 10, Bool: true, Valuer: sql.NullString{String: "null_str", Valid: true}, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithPointerVals() { type TestStruct struct { Str string Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ Str: "string", Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithEmbeddedStruct() { type EmbeddedStruct struct { Str string } type TestStruct struct { EmbeddedStruct Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ EmbeddedStruct: EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithEmbeddedStructPointer() { type EmbeddedStruct struct { Str string } type TestStruct struct { *EmbeddedStruct Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ EmbeddedStruct: &EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithTaggedEmbeddedStruct() { type EmbeddedStruct struct { Str string } type TestStruct struct { EmbeddedStruct `db:"embedded"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "embedded.str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ EmbeddedStruct: EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithTaggedEmbeddedPointer() { type EmbeddedStruct struct { Str string } type TestStruct struct { *EmbeddedStruct `db:"embedded"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "embedded.str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ EmbeddedStruct: &EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithTaggedStructField() { type EmbeddedStruct struct { Str string } type TestStruct struct { Embedded EmbeddedStruct `db:"embedded"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "embedded.str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ Embedded: EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestAssignStructVals_withStructWithTaggedPointerField() { type EmbeddedStruct struct { Str string } type TestStruct struct { Embedded *EmbeddedStruct `db:"embedded"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) ns := &sql.NullString{String: "null_str1", Valid: true} data := map[string]interface{}{ "embedded.str": "string", "int": int64(10), "bool": true, "valuer": &ns, } util.AssignStructVals(&ts, data, cm) rt.Equal(ts, TestStruct{ Embedded: &EmbeddedStruct{Str: "string"}, Int: 10, Bool: true, Valuer: ns, }) } func (rt *reflectTest) TestGetColumnMap_withStruct() { type TestStruct struct { Str string Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructGoquTags() { type TestStruct struct { Str string `goqu:"skipinsert,skipupdate"` Int int64 `goqu:"skipinsert"` Bool bool `goqu:"skipupdate"` Empty bool `goqu:"defaultifempty"` Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0}, ShouldInsert: false, ShouldUpdate: false, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: false, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: false, GoType: reflect.TypeOf(true)}, "empty": { ColumnName: "empty", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, DefaultIfEmpty: true, GoType: reflect.TypeOf(true), }, "valuer": {ColumnName: "valuer", FieldIndex: []int{4}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructWithIgnoreUntagged() { defer util.SetIgnoreUntaggedFields(false) util.SetIgnoreUntaggedFields(true) type EmbeddedStruct struct { Float float64 `db:"f"` Rune rune // Ignored } type TestStruct struct { EmbeddedStruct Str string `db:"s"` Int int64 `db:"i"` Bool bool // Ignored } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "f": {ColumnName: "f", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(float64(1))}, "s": {ColumnName: "s", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "i": {ColumnName: "i", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructWithTag() { type TestStruct struct { Str string `db:"s"` Int int64 `db:"i"` Bool bool `db:"b"` Valuer *sql.NullString `db:"v"` Ignored string `db:"-"` } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "s": {ColumnName: "s", FieldIndex: []int{0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "i": {ColumnName: "i", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "b": {ColumnName: "b", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "v": {ColumnName: "v", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructWithTagAndGoquTag() { type TestStruct struct { Str string `db:"s" goqu:"skipinsert,skipupdate"` Int int64 `db:"i" goqu:"skipinsert"` Bool bool `db:"b" goqu:"skipupdate"` Valuer *sql.NullString `db:"v"` } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "s": {ColumnName: "s", FieldIndex: []int{0}, ShouldInsert: false, ShouldUpdate: false, GoType: reflect.TypeOf("")}, "i": {ColumnName: "i", FieldIndex: []int{1}, ShouldInsert: false, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "b": {ColumnName: "b", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: false, GoType: reflect.TypeOf(true)}, "v": {ColumnName: "v", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructWithTransientFields() { type TestStruct struct { Str string Int int64 Bool bool Valuer *sql.NullString `db:"-"` } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withSliceOfStructs() { type TestStruct struct { Str string Int int64 Bool bool Valuer *sql.NullString } var ts []TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withNonStruct() { var v int64 _, err := util.GetColumnMap(&v) rt.EqualError(err, "goqu: cannot scan into this type: int64") } func (rt *reflectTest) TestGetColumnMap_withStructWithEmbeddedStruct() { type EmbeddedStruct struct { Str string } type TestStruct struct { EmbeddedStruct Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withStructWithEmbeddedStructPointer() { type EmbeddedStruct struct { Str string } type TestStruct struct { *EmbeddedStruct Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "str": {ColumnName: "str", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf("")}, "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withIgnoredEmbeddedStruct() { type EmbeddedStruct struct { Str string } type TestStruct struct { EmbeddedStruct `db:"-"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withIgnoredEmbeddedPointerStruct() { type EmbeddedStruct struct { Str string } type TestStruct struct { *EmbeddedStruct `db:"-"` Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withPrivateFields() { type TestStruct struct { str string // nolint:structcheck,unused // not used directly but needed for test Int int64 Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "int": {ColumnName: "int", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{3}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withPrivateEmbeddedFields() { type TestEmbedded struct { str string // nolint:structcheck,unused // not used directly but need for test Int int64 } type TestStruct struct { TestEmbedded Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "int": {ColumnName: "int", FieldIndex: []int{0, 1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(int64(1))}, "bool": {ColumnName: "bool", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true)}, "valuer": {ColumnName: "valuer", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{})}, }, cm) } func (rt *reflectTest) TestGetColumnMap_withEmbeddedTaggedStruct() { type TestEmbedded struct { Bool bool Valuer *sql.NullString } type TestStruct struct { TestEmbedded `db:"test_embedded"` Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "test_embedded.bool": { ColumnName: "test_embedded.bool", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "test_embedded.valuer": { ColumnName: "test_embedded.valuer", FieldIndex: []int{0, 1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, "bool": { ColumnName: "bool", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "valuer": { ColumnName: "valuer", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, }, cm) } func (rt *reflectTest) TestGetColumnMap_withEmbeddedTaggedStructPointer() { type TestEmbedded struct { Bool bool Valuer *sql.NullString } type TestStruct struct { *TestEmbedded `db:"test_embedded"` Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "test_embedded.bool": { ColumnName: "test_embedded.bool", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "test_embedded.valuer": { ColumnName: "test_embedded.valuer", FieldIndex: []int{0, 1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, "bool": { ColumnName: "bool", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "valuer": { ColumnName: "valuer", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, }, cm) } func (rt *reflectTest) TestGetColumnMap_withTaggedStructField() { type TestEmbedded struct { Bool bool Valuer *sql.NullString } type TestStruct struct { Embedded TestEmbedded `db:"test_embedded"` Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "test_embedded.bool": { ColumnName: "test_embedded.bool", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "test_embedded.valuer": { ColumnName: "test_embedded.valuer", FieldIndex: []int{0, 1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, "bool": { ColumnName: "bool", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "valuer": { ColumnName: "valuer", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, }, cm) } func (rt *reflectTest) TestGetColumnMap_withTaggedStructPointerField() { type TestEmbedded struct { Bool bool Valuer *sql.NullString } type TestStruct struct { Embedded *TestEmbedded `db:"test_embedded"` Bool bool Valuer *sql.NullString } var ts TestStruct cm, err := util.GetColumnMap(&ts) rt.NoError(err) rt.Equal(util.ColumnMap{ "test_embedded.bool": { ColumnName: "test_embedded.bool", FieldIndex: []int{0, 0}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "test_embedded.valuer": { ColumnName: "test_embedded.valuer", FieldIndex: []int{0, 1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, "bool": { ColumnName: "bool", FieldIndex: []int{1}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(true), }, "valuer": { ColumnName: "valuer", FieldIndex: []int{2}, ShouldInsert: true, ShouldUpdate: true, GoType: reflect.TypeOf(&sql.NullString{}), }, }, cm) } func (rt *reflectTest) TestGetTypeInfo() { var a int64 var b []int64 var c []*time.Time t, k := util.GetTypeInfo(&a, reflect.ValueOf(a)) rt.Equal(reflect.TypeOf(a), t) rt.Equal(reflect.Int64, k) t, k = util.GetTypeInfo(&b, reflect.ValueOf(a)) rt.Equal(reflect.TypeOf(a), t) rt.Equal(reflect.Int64, k) t, k = util.GetTypeInfo(c, reflect.ValueOf(c)) rt.Equal(reflect.TypeOf(time.Time{}), t) rt.Equal(reflect.Struct, k) } func (rt *reflectTest) TestSafeGetFieldByIndex() { type TestEmbedded struct { FieldA int } type TestEmbeddedPointerStruct struct { *TestEmbedded FieldB string } type TestEmbeddedStruct struct { TestEmbedded FieldB string } v := reflect.ValueOf(TestEmbeddedPointerStruct{}) f, isAvailable := util.SafeGetFieldByIndex(v, []int{0, 0}) rt.False(isAvailable) rt.False(f.IsValid()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{1}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.String, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{}) rt.True(isAvailable) rt.Equal(v, f) v = reflect.ValueOf(TestEmbeddedPointerStruct{TestEmbedded: &TestEmbedded{}}) f, isAvailable = util.SafeGetFieldByIndex(v, []int{0, 0}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.Int, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{1}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.String, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{}) rt.True(isAvailable) rt.Equal(v, f) v = reflect.ValueOf(TestEmbeddedStruct{}) f, isAvailable = util.SafeGetFieldByIndex(v, []int{0, 0}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.Int, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{1}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.String, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{}) rt.True(isAvailable) rt.Equal(v, f) v = reflect.ValueOf(TestEmbeddedStruct{TestEmbedded: TestEmbedded{}}) f, isAvailable = util.SafeGetFieldByIndex(v, []int{0, 0}) rt.True(isAvailable) rt.True(f.IsValid()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{1}) rt.True(isAvailable) rt.True(f.IsValid()) rt.Equal(reflect.String, f.Type().Kind()) f, isAvailable = util.SafeGetFieldByIndex(v, []int{}) rt.True(isAvailable) rt.Equal(v, f) } func (rt *reflectTest) TestSafeSetFieldByIndex() { type TestEmbedded struct { FieldA int } type TestEmbeddedPointerStruct struct { *TestEmbedded FieldB string } type TestEmbeddedStruct struct { TestEmbedded FieldB string } var teps TestEmbeddedPointerStruct v := reflect.ValueOf(&teps) f := util.SafeSetFieldByIndex(v, []int{}, nil) rt.Equal(TestEmbeddedPointerStruct{}, f.Interface()) f = util.SafeSetFieldByIndex(v, []int{0, 0}, 1) rt.Equal(TestEmbeddedPointerStruct{ TestEmbedded: &TestEmbedded{FieldA: 1}, }, f.Interface()) f = util.SafeSetFieldByIndex(v, []int{1}, "hello") rt.Equal(TestEmbeddedPointerStruct{ TestEmbedded: &TestEmbedded{FieldA: 1}, FieldB: "hello", }, f.Interface()) rt.Equal(TestEmbeddedPointerStruct{ TestEmbedded: &TestEmbedded{FieldA: 1}, FieldB: "hello", }, teps) var tes TestEmbeddedStruct v = reflect.ValueOf(&tes) f = util.SafeSetFieldByIndex(v, []int{}, nil) rt.Equal(TestEmbeddedStruct{}, f.Interface()) f = util.SafeSetFieldByIndex(v, []int{0, 0}, 1) rt.Equal(TestEmbeddedStruct{ TestEmbedded: TestEmbedded{FieldA: 1}, }, f.Interface()) f = util.SafeSetFieldByIndex(v, []int{1}, "hello") rt.Equal(TestEmbeddedStruct{ TestEmbedded: TestEmbedded{FieldA: 1}, FieldB: "hello", }, f.Interface()) rt.Equal(TestEmbeddedStruct{ TestEmbedded: TestEmbedded{FieldA: 1}, FieldB: "hello", }, tes) } func (rt *reflectTest) TestGetSliceElementType() { type MyStruct struct{} tests := []struct { slice interface{} want reflect.Type }{ { slice: []int{}, want: reflect.TypeOf(1), }, { slice: []*int{}, want: reflect.TypeOf(1), }, { slice: []MyStruct{}, want: reflect.TypeOf(MyStruct{}), }, { slice: []*MyStruct{}, want: reflect.TypeOf(MyStruct{}), }, } for _, tt := range tests { sliceVal := reflect.ValueOf(tt.slice) elementType := util.GetSliceElementType(sliceVal) rt.Equal(tt.want, elementType) } } func (rt *reflectTest) TestAppendSliceElement() { type MyStruct struct{} sliceVal := reflect.Indirect(reflect.ValueOf(&[]MyStruct{})) util.AppendSliceElement(sliceVal, reflect.ValueOf(&MyStruct{})) rt.Equal([]MyStruct{{}}, sliceVal.Interface()) sliceVal = reflect.Indirect(reflect.ValueOf(&[]*MyStruct{})) util.AppendSliceElement(sliceVal, reflect.ValueOf(&MyStruct{})) rt.Equal([]*MyStruct{{}}, sliceVal.Interface()) } func TestReflectSuite(t *testing.T) { suite.Run(t, new(reflectTest)) }