package structs import ( "fmt" "reflect" "testing" "time" ) func TestMapNonStruct(t *testing.T) { foo := []string{"foo"} defer func() { err := recover() if err == nil { t.Error("Passing a non struct into Map should panic") } }() // this should panic. We are going to recover and and test it _ = Map(foo) } func TestStructIndexes(t *testing.T) { type C struct { something int Props map[string]interface{} } defer func() { err := recover() if err != nil { fmt.Printf("err %+v\n", err) t.Error("Using mixed indexes should not panic") } }() // They should not panic _ = Map(&C{}) _ = Fields(&C{}) _ = Values(&C{}) _ = IsZero(&C{}) _ = HasZero(&C{}) } func TestMap(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } a := Map(T) if typ := reflect.TypeOf(a).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } // we have three fields if len(a) != 3 { t.Errorf("Map should return a map of len 3, got: %d", len(a)) } inMap := func(val interface{}) bool { for _, v := range a { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{"a-value", 2, true} { if !inMap(val) { t.Errorf("Map should have the value %v", val) } } } func TestMap_Tag(t *testing.T) { var T = struct { A string `structs:"x"` B int `structs:"y"` C bool `structs:"z"` }{ A: "a-value", B: 2, C: true, } a := Map(T) inMap := func(key interface{}) bool { for k := range a { if reflect.DeepEqual(k, key) { return true } } return false } for _, key := range []string{"x", "y", "z"} { if !inMap(key) { t.Errorf("Map should have the key %v", key) } } } func TestMap_CustomTag(t *testing.T) { var T = struct { A string `json:"x"` B int `json:"y"` C bool `json:"z"` D struct { E string `json:"jkl"` } `json:"nested"` }{ A: "a-value", B: 2, C: true, } T.D.E = "e-value" s := New(T) s.TagName = "json" a := s.Map() inMap := func(key interface{}) bool { for k := range a { if reflect.DeepEqual(k, key) { return true } } return false } for _, key := range []string{"x", "y", "z"} { if !inMap(key) { t.Errorf("Map should have the key %v", key) } } nested, ok := a["nested"].(map[string]interface{}) if !ok { t.Fatalf("Map should contain the D field that is tagged as 'nested'") } e, ok := nested["jkl"].(string) if !ok { t.Fatalf("Map should contain the D.E field that is tagged as 'jkl'") } if e != "e-value" { t.Errorf("D.E field should be equal to 'e-value', got: '%v'", e) } } func TestMap_MultipleCustomTag(t *testing.T) { var A = struct { X string `aa:"ax"` }{"a_value"} aStruct := New(A) aStruct.TagName = "aa" var B = struct { X string `bb:"bx"` }{"b_value"} bStruct := New(B) bStruct.TagName = "bb" a, b := aStruct.Map(), bStruct.Map() if !reflect.DeepEqual(a, map[string]interface{}{"ax": "a_value"}) { t.Error("Map should have field ax with value a_value") } if !reflect.DeepEqual(b, map[string]interface{}{"bx": "b_value"}) { t.Error("Map should have field bx with value b_value") } } func TestMap_OmitEmpty(t *testing.T) { type A struct { Name string Value string `structs:",omitempty"` Time time.Time `structs:",omitempty"` } a := A{} m := Map(a) _, ok := m["Value"].(map[string]interface{}) if ok { t.Error("Map should not contain the Value field that is tagged as omitempty") } _, ok = m["Time"].(map[string]interface{}) if ok { t.Error("Map should not contain the Time field that is tagged as omitempty") } } func TestMap_OmitNested(t *testing.T) { type A struct { Name string Value string Time time.Time `structs:",omitnested"` } a := A{Time: time.Now()} type B struct { Desc string A A } b := &B{A: a} m := Map(b) in, ok := m["A"].(map[string]interface{}) if !ok { t.Error("Map nested structs is not available in the map") } // should not happen if _, ok := in["Time"].(map[string]interface{}); ok { t.Error("Map nested struct should omit recursiving parsing of Time") } if _, ok := in["Time"].(time.Time); !ok { t.Error("Map nested struct should stop parsing of Time at is current value") } } func TestMap_Nested(t *testing.T) { type A struct { Name string } a := &A{Name: "example"} type B struct { A *A } b := &B{A: a} m := Map(b) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["A"].(map[string]interface{}) if !ok { t.Error("Map nested structs is not available in the map") } if name := in["Name"].(string); name != "example" { t.Errorf("Map nested struct's name field should give example, got: %s", name) } } func TestMap_NestedMapWithStructValues(t *testing.T) { type A struct { Name string } type B struct { A map[string]*A } a := &A{Name: "example"} b := &B{ A: map[string]*A{ "example_key": a, }, } m := Map(b) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["A"].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["A"]) } example := in["example_key"].(map[string]interface{}) if name := example["Name"].(string); name != "example" { t.Errorf("Map nested struct's name field should give example, got: %s", name) } } func TestMap_NestedMapWithStringValues(t *testing.T) { type B struct { Foo map[string]string } type A struct { B *B } b := &B{ Foo: map[string]string{ "example_key": "example", }, } a := &A{B: b} m := Map(a) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["B"].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"]) } foo := in["Foo"].(map[string]string) if name := foo["example_key"]; name != "example" { t.Errorf("Map nested struct's name field should give example, got: %s", name) } } func TestMap_NestedMapWithInterfaceValues(t *testing.T) { type B struct { Foo map[string]interface{} } type A struct { B *B } b := &B{ Foo: map[string]interface{}{ "example_key": "example", }, } a := &A{B: b} m := Map(a) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["B"].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"]) } foo := in["Foo"].(map[string]interface{}) if name := foo["example_key"]; name != "example" { t.Errorf("Map nested struct's name field should give example, got: %s", name) } } func TestMap_NestedMapWithSliceIntValues(t *testing.T) { type B struct { Foo map[string][]int } type A struct { B *B } b := &B{ Foo: map[string][]int{ "example_key": {80}, }, } a := &A{B: b} m := Map(a) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["B"].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"]) } foo := in["Foo"].(map[string][]int) if name := foo["example_key"]; name[0] != 80 { t.Errorf("Map nested struct's name field should give example, got: %v", name) } } func TestMap_NestedMapWithSliceStructValues(t *testing.T) { type address struct { Country string `structs:"country"` } type B struct { Foo map[string][]address } type A struct { B *B } b := &B{ Foo: map[string][]address{ "example_key": { {Country: "Turkey"}, }, }, } a := &A{B: b} m := Map(a) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["B"].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"]) } foo := in["Foo"].(map[string]interface{}) addresses := foo["example_key"].([]interface{}) addr, ok := addresses[0].(map[string]interface{}) if !ok { t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"]) } if _, exists := addr["country"]; !exists { t.Errorf("Expecting country, but found Country") } } func TestMap_NestedSliceWithStructValues(t *testing.T) { type address struct { Country string `structs:"customCountryName"` } type person struct { Name string `structs:"name"` Addresses []address `structs:"addresses"` } p := person{ Name: "test", Addresses: []address{ {Country: "England"}, {Country: "Italy"}, }, } mp := Map(p) mpAddresses := mp["addresses"].([]interface{}) if _, exists := mpAddresses[0].(map[string]interface{})["Country"]; exists { t.Errorf("Expecting customCountryName, but found Country") } if _, exists := mpAddresses[0].(map[string]interface{})["customCountryName"]; !exists { t.Errorf("customCountryName key not found") } } func TestMap_NestedSliceWithPointerOfStructValues(t *testing.T) { type address struct { Country string `structs:"customCountryName"` } type person struct { Name string `structs:"name"` Addresses []*address `structs:"addresses"` } p := person{ Name: "test", Addresses: []*address{ {Country: "England"}, {Country: "Italy"}, }, } mp := Map(p) mpAddresses := mp["addresses"].([]interface{}) if _, exists := mpAddresses[0].(map[string]interface{})["Country"]; exists { t.Errorf("Expecting customCountryName, but found Country") } if _, exists := mpAddresses[0].(map[string]interface{})["customCountryName"]; !exists { t.Errorf("customCountryName key not found") } } func TestMap_NestedSliceWithIntValues(t *testing.T) { type person struct { Name string `structs:"name"` Ports []int `structs:"ports"` } p := person{ Name: "test", Ports: []int{80}, } m := Map(p) ports, ok := m["ports"].([]int) if !ok { t.Errorf("Nested type of map should be of type []int, have %T", m["ports"]) } if ports[0] != 80 { t.Errorf("Map nested struct's ports field should give 80, got: %v", ports) } } func TestMap_Anonymous(t *testing.T) { type A struct { Name string } a := &A{Name: "example"} type B struct { *A } b := &B{} b.A = a m := Map(b) if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { t.Errorf("Map should return a map type, got: %v", typ) } in, ok := m["A"].(map[string]interface{}) if !ok { t.Error("Embedded structs is not available in the map") } if name := in["Name"].(string); name != "example" { t.Errorf("Embedded A struct's Name field should give example, got: %s", name) } } func TestMap_Flatnested(t *testing.T) { type A struct { Name string } a := A{Name: "example"} type B struct { A `structs:",flatten"` C int } b := &B{C: 123} b.A = a m := Map(b) _, ok := m["A"].(map[string]interface{}) if ok { t.Error("Embedded A struct with tag flatten has to be flat in the map") } expectedMap := map[string]interface{}{"Name": "example", "C": 123} if !reflect.DeepEqual(m, expectedMap) { t.Errorf("The exprected map %+v does't correspond to %+v", expectedMap, m) } } func TestMap_FlatnestedOverwrite(t *testing.T) { type A struct { Name string } a := A{Name: "example"} type B struct { A `structs:",flatten"` Name string C int } b := &B{C: 123, Name: "bName"} b.A = a m := Map(b) _, ok := m["A"].(map[string]interface{}) if ok { t.Error("Embedded A struct with tag flatten has to be flat in the map") } expectedMap := map[string]interface{}{"Name": "bName", "C": 123} if !reflect.DeepEqual(m, expectedMap) { t.Errorf("The exprected map %+v does't correspond to %+v", expectedMap, m) } } func TestMap_TimeField(t *testing.T) { type A struct { CreatedAt time.Time } a := &A{CreatedAt: time.Now().UTC()} m := Map(a) _, ok := m["CreatedAt"].(time.Time) if !ok { t.Error("Time field must be final") } } func TestFillMap(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } a := make(map[string]interface{}, 0) FillMap(T, a) // we have three fields if len(a) != 3 { t.Errorf("FillMap should fill a map of len 3, got: %d", len(a)) } inMap := func(val interface{}) bool { for _, v := range a { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{"a-value", 2, true} { if !inMap(val) { t.Errorf("FillMap should have the value %v", val) } } } func TestFillMap_Nil(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } defer func() { err := recover() if err != nil { t.Error("FillMap should not panic if a nil map is passed") } }() // nil should no FillMap(T, nil) } func TestStruct(t *testing.T) { var T = struct{}{} if !IsStruct(T) { t.Errorf("T should be a struct, got: %T", T) } if !IsStruct(&T) { t.Errorf("T should be a struct, got: %T", T) } } func TestValues(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } s := Values(T) if typ := reflect.TypeOf(s).Kind(); typ != reflect.Slice { t.Errorf("Values should return a slice type, got: %v", typ) } inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{"a-value", 2, true} { if !inSlice(val) { t.Errorf("Values should have the value %v", val) } } } func TestValues_OmitEmpty(t *testing.T) { type A struct { Name string Value int `structs:",omitempty"` } a := A{Name: "example"} s := Values(a) if len(s) != 1 { t.Errorf("Values of omitted empty fields should be not counted") } if s[0].(string) != "example" { t.Errorf("Values of omitted empty fields should left the value example") } } func TestValues_OmitNested(t *testing.T) { type A struct { Name string Value int } a := A{ Name: "example", Value: 123, } type B struct { A A `structs:",omitnested"` C int } b := &B{A: a, C: 123} s := Values(b) if len(s) != 2 { t.Errorf("Values of omitted nested struct should be not counted") } inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{123, a} { if !inSlice(val) { t.Errorf("Values should have the value %v", val) } } } func TestValues_Nested(t *testing.T) { type A struct { Name string } a := A{Name: "example"} type B struct { A A C int } b := &B{A: a, C: 123} s := Values(b) inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{"example", 123} { if !inSlice(val) { t.Errorf("Values should have the value %v", val) } } } func TestValues_Anonymous(t *testing.T) { type A struct { Name string } a := A{Name: "example"} type B struct { A C int } b := &B{C: 123} b.A = a s := Values(b) inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []interface{}{"example", 123} { if !inSlice(val) { t.Errorf("Values should have the value %v", val) } } } func TestNames(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } s := Names(T) if len(s) != 3 { t.Errorf("Names should return a slice of len 3, got: %d", len(s)) } inSlice := func(val string) bool { for _, v := range s { if reflect.DeepEqual(v, val) { return true } } return false } for _, val := range []string{"A", "B", "C"} { if !inSlice(val) { t.Errorf("Names should have the value %v", val) } } } func TestFields(t *testing.T) { var T = struct { A string B int C bool }{ A: "a-value", B: 2, C: true, } s := Fields(T) if len(s) != 3 { t.Errorf("Fields should return a slice of len 3, got: %d", len(s)) } inSlice := func(val string) bool { for _, v := range s { if reflect.DeepEqual(v.Name(), val) { return true } } return false } for _, val := range []string{"A", "B", "C"} { if !inSlice(val) { t.Errorf("Fields should have the value %v", val) } } } func TestFields_OmitNested(t *testing.T) { type A struct { Name string Enabled bool } a := A{Name: "example"} type B struct { A A C int Value string `structs:"-"` Number int } b := &B{A: a, C: 123} s := Fields(b) if len(s) != 3 { t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s)) } inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v.Name(), val) { return true } } return false } for _, val := range []interface{}{"A", "C"} { if !inSlice(val) { t.Errorf("Fields should have the value %v", val) } } } func TestFields_Anonymous(t *testing.T) { type A struct { Name string } a := A{Name: "example"} type B struct { A C int } b := &B{C: 123} b.A = a s := Fields(b) inSlice := func(val interface{}) bool { for _, v := range s { if reflect.DeepEqual(v.Name(), val) { return true } } return false } for _, val := range []interface{}{"A", "C"} { if !inSlice(val) { t.Errorf("Fields should have the value %v", val) } } } func TestIsZero(t *testing.T) { var T = struct { A string B int C bool `structs:"-"` D []string }{} ok := IsZero(T) if !ok { t.Error("IsZero should return true because none of the fields are initialized.") } var X = struct { A string F *bool }{ A: "a-value", } ok = IsZero(X) if ok { t.Error("IsZero should return false because A is initialized") } var Y = struct { A string B int }{ A: "a-value", B: 123, } ok = IsZero(Y) if ok { t.Error("IsZero should return false because A and B is initialized") } } func TestIsZero_OmitNested(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A A `structs:",omitnested"` C int } b := &B{A: a, C: 123} ok := IsZero(b) if ok { t.Error("IsZero should return false because A, B and C are initialized") } aZero := A{} bZero := &B{A: aZero} ok = IsZero(bZero) if !ok { t.Error("IsZero should return true because neither A nor B is initialized") } } func TestIsZero_Nested(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A A C int } b := &B{A: a, C: 123} ok := IsZero(b) if ok { t.Error("IsZero should return false because A, B and C are initialized") } aZero := A{} bZero := &B{A: aZero} ok = IsZero(bZero) if !ok { t.Error("IsZero should return true because neither A nor B is initialized") } } func TestIsZero_Anonymous(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A C int } b := &B{C: 123} b.A = a ok := IsZero(b) if ok { t.Error("IsZero should return false because A, B and C are initialized") } aZero := A{} bZero := &B{} bZero.A = aZero ok = IsZero(bZero) if !ok { t.Error("IsZero should return true because neither A nor B is initialized") } } func TestHasZero(t *testing.T) { var T = struct { A string B int C bool `structs:"-"` D []string }{ A: "a-value", B: 2, } ok := HasZero(T) if !ok { t.Error("HasZero should return true because A and B are initialized.") } var X = struct { A string F *bool }{ A: "a-value", } ok = HasZero(X) if !ok { t.Error("HasZero should return true because A is initialized") } var Y = struct { A string B int }{ A: "a-value", B: 123, } ok = HasZero(Y) if ok { t.Error("HasZero should return false because A and B is initialized") } } func TestHasZero_OmitNested(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A A `structs:",omitnested"` C int } b := &B{A: a, C: 123} // Because the Field A inside B is omitted HasZero should return false // because it will stop iterating deeper andnot going to lookup for D ok := HasZero(b) if ok { t.Error("HasZero should return false because A and C are initialized") } } func TestHasZero_Nested(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A A C int } b := &B{A: a, C: 123} ok := HasZero(b) if !ok { t.Error("HasZero should return true because D is not initialized") } } func TestHasZero_Anonymous(t *testing.T) { type A struct { Name string D string } a := A{Name: "example"} type B struct { A C int } b := &B{C: 123} b.A = a ok := HasZero(b) if !ok { t.Error("HasZero should return false because D is not initialized") } } func TestName(t *testing.T) { type Foo struct { A string B bool } f := &Foo{} n := Name(f) if n != "Foo" { t.Errorf("Name should return Foo, got: %s", n) } unnamed := struct{ Name string }{Name: "Cihangir"} m := Name(unnamed) if m != "" { t.Errorf("Name should return empty string for unnamed struct, got: %s", n) } defer func() { err := recover() if err == nil { t.Error("Name should panic if a non struct is passed") } }() Name([]string{}) } func TestNestedNilPointer(t *testing.T) { type Collar struct { Engraving string } type Dog struct { Name string Collar *Collar } type Person struct { Name string Dog *Dog } person := &Person{ Name: "John", } personWithDog := &Person{ Name: "Ron", Dog: &Dog{ Name: "Rover", }, } personWithDogWithCollar := &Person{ Name: "Kon", Dog: &Dog{ Name: "Ruffles", Collar: &Collar{ Engraving: "If lost, call Kon", }, }, } defer func() { err := recover() if err != nil { fmt.Printf("err %+v\n", err) t.Error("Internal nil pointer should not panic") } }() _ = Map(person) // Panics _ = Map(personWithDog) // Panics _ = Map(personWithDogWithCollar) // Doesn't panic } func TestSetValueOnNestedField(t *testing.T) { type Base struct { ID int } type User struct { Base Name string } u := User{} s := New(&u) f := s.Field("Base").Field("ID") err := f.Set(10) if err != nil { t.Errorf("Error %v", err) } if f.Value().(int) != 10 { t.Errorf("Value should be equal to 10, got %v", f.Value()) } } type Person struct { Name string Age int } func (p *Person) String() string { return fmt.Sprintf("%s(%d)", p.Name, p.Age) } func TestTagWithStringOption(t *testing.T) { type Address struct { Country string `json:"country"` Person *Person `json:"person,string"` } person := &Person{ Name: "John", Age: 23, } address := &Address{ Country: "EU", Person: person, } defer func() { err := recover() if err != nil { fmt.Printf("err %+v\n", err) t.Error("Internal nil pointer should not panic") } }() s := New(address) s.TagName = "json" m := s.Map() if m["person"] != person.String() { t.Errorf("Value for field person should be %s, got: %s", person.String(), m["person"]) } vs := s.Values() if vs[1] != person.String() { t.Errorf("Value for 2nd field (person) should be %T, got: %T", person.String(), vs[1]) } } type Animal struct { Name string Age int } type Dog struct { Animal *Animal `json:"animal,string"` } func TestNonStringerTagWithStringOption(t *testing.T) { a := &Animal{ Name: "Fluff", Age: 4, } d := &Dog{ Animal: a, } defer func() { err := recover() if err != nil { fmt.Printf("err %+v\n", err) t.Error("Internal nil pointer should not panic") } }() s := New(d) s.TagName = "json" m := s.Map() if _, exists := m["animal"]; exists { t.Errorf("Value for field Animal should not exist") } } func TestMap_InterfaceValue(t *testing.T) { type TestStruct struct { A interface{} } expected := []byte("test value") a := TestStruct{A: expected} s := Map(a) if !reflect.DeepEqual(s["A"], expected) { t.Errorf("Value does not match expected: %q != %q", s["A"], expected) } } func TestPointer2Pointer(t *testing.T) { defer func() { err := recover() if err != nil { fmt.Printf("err %+v\n", err) t.Error("Internal nil pointer should not panic") } }() a := &Animal{ Name: "Fluff", Age: 4, } _ = Map(&a) b := &a _ = Map(&b) c := &b _ = Map(&c) } func TestMap_InterfaceTypeWithMapValue(t *testing.T) { type A struct { Name string `structs:"name"` IP string `structs:"ip"` Query string `structs:"query"` Payload interface{} `structs:"payload"` } a := A{ Name: "test", IP: "127.0.0.1", Query: "", Payload: map[string]string{"test_param": "test_param"}, } defer func() { err := recover() if err != nil { t.Error("Converting Map with an interface{} type with map value should not panic") } }() _ = Map(a) }