| 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) |
| } |