You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
6.7 KiB

  1. package json_test
  2. import (
  3. "reflect"
  4. "testing"
  5. "time"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. "github.com/tendermint/tendermint/libs/json"
  9. )
  10. func TestUnmarshal(t *testing.T) {
  11. i64Nil := (*int64)(nil)
  12. str := "string"
  13. strPtr := &str
  14. structNil := (*Struct)(nil)
  15. i32 := int32(32)
  16. i64 := int64(64)
  17. testcases := map[string]struct {
  18. json string
  19. value interface{}
  20. err bool
  21. }{
  22. "bool true": {"true", true, false},
  23. "bool false": {"false", false, false},
  24. "float32": {"3.14", float32(3.14), false},
  25. "float64": {"3.14", float64(3.14), false},
  26. "int32": {`32`, int32(32), false},
  27. "int32 string": {`"32"`, int32(32), true},
  28. "int32 ptr": {`32`, &i32, false},
  29. "int64": {`"64"`, int64(64), false},
  30. "int64 noend": {`"64`, int64(64), true},
  31. "int64 number": {`64`, int64(64), true},
  32. "int64 ptr": {`"64"`, &i64, false},
  33. "int64 ptr nil": {`null`, i64Nil, false},
  34. "string": {`"foo"`, "foo", false},
  35. "string noend": {`"foo`, "foo", true},
  36. "string ptr": {`"string"`, &str, false},
  37. "slice byte": {`"AQID"`, []byte{1, 2, 3}, false},
  38. "slice bytes": {`["AQID"]`, [][]byte{{1, 2, 3}}, false},
  39. "slice int32": {`[1,2,3]`, []int32{1, 2, 3}, false},
  40. "slice int64": {`["1","2","3"]`, []int64{1, 2, 3}, false},
  41. "slice int64 number": {`[1,2,3]`, []int64{1, 2, 3}, true},
  42. "slice int64 ptr": {`["64"]`, []*int64{&i64}, false},
  43. "slice int64 empty": {`[]`, []int64(nil), false},
  44. "slice int64 null": {`null`, []int64(nil), false},
  45. "array byte": {`"AQID"`, [3]byte{1, 2, 3}, false},
  46. "array byte large": {`"AQID"`, [4]byte{1, 2, 3, 4}, true},
  47. "array byte small": {`"AQID"`, [2]byte{1, 2}, true},
  48. "array int32": {`[1,2,3]`, [3]int32{1, 2, 3}, false},
  49. "array int64": {`["1","2","3"]`, [3]int64{1, 2, 3}, false},
  50. "array int64 number": {`[1,2,3]`, [3]int64{1, 2, 3}, true},
  51. "array int64 large": {`["1","2","3"]`, [4]int64{1, 2, 3, 4}, true},
  52. "array int64 small": {`["1","2","3"]`, [2]int64{1, 2}, true},
  53. "map bytes": {`{"b":"AQID"}`, map[string][]byte{"b": {1, 2, 3}}, false},
  54. "map int32": {`{"a":1,"b":2}`, map[string]int32{"a": 1, "b": 2}, false},
  55. "map int64": {`{"a":"1","b":"2"}`, map[string]int64{"a": 1, "b": 2}, false},
  56. "map int64 empty": {`{}`, map[string]int64{}, false},
  57. "map int64 null": {`null`, map[string]int64(nil), false},
  58. "map int key": {`{}`, map[int]int{}, true},
  59. "time": {`"2020-06-03T17:35:30Z"`, time.Date(2020, 6, 3, 17, 35, 30, 0, time.UTC), false},
  60. "time non-utc": {`"2020-06-03T17:35:30+02:00"`, time.Time{}, true},
  61. "time nozone": {`"2020-06-03T17:35:30"`, time.Time{}, true},
  62. "car": {`{"type":"vehicle/car","value":{"Wheels":4}}`, Car{Wheels: 4}, false},
  63. "car ptr": {`{"type":"vehicle/car","value":{"Wheels":4}}`, &Car{Wheels: 4}, false},
  64. "car iface": {`{"type":"vehicle/car","value":{"Wheels":4}}`, Vehicle(&Car{Wheels: 4}), false},
  65. "boat": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Boat{Sail: true}, false},
  66. "boat ptr": {`{"type":"vehicle/boat","value":{"Sail":true}}`, &Boat{Sail: true}, false},
  67. "boat iface": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(Boat{Sail: true}), false},
  68. "boat into car": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Car{}, true},
  69. "boat into car iface": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(&Car{}), true},
  70. "shoes": {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, Car{}, true},
  71. "shoes ptr": {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, &Car{}, true},
  72. "shoes iface": {`{"type":"vehicle/shoes","value":{"Soles":"rubbes"}}`, Vehicle(&Car{}), true},
  73. "key public": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PublicKey{1, 2, 3, 4, 5, 6, 7, 8}, false},
  74. "key wrong": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PrivateKey{1, 2, 3, 4, 5, 6, 7, 8}, true},
  75. "key into car": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, Vehicle(&Car{}), true},
  76. "tags": {
  77. `{"name":"name","OmitEmpty":"foo","Hidden":"bar","tags":{"name":"child"}}`,
  78. Tags{JSONName: "name", OmitEmpty: "foo", Tags: &Tags{JSONName: "child"}},
  79. false,
  80. },
  81. "tags ptr": {
  82. `{"name":"name","OmitEmpty":"foo","tags":null}`,
  83. &Tags{JSONName: "name", OmitEmpty: "foo"},
  84. false,
  85. },
  86. "tags real name": {`{"JSONName":"name"}`, Tags{}, false},
  87. "struct": {
  88. `{
  89. "Bool":true, "Float64":3.14, "Int32":32, "Int64":"64", "Int64Ptr":"64",
  90. "String":"foo", "StringPtrPtr": "string", "Bytes":"AQID",
  91. "Time":"2020-06-02T16:05:13.004346374Z",
  92. "Car":{"Wheels":4},
  93. "Boat":{"Sail":true},
  94. "Vehicles":[
  95. {"type":"vehicle/car","value":{"Wheels":4}},
  96. {"type":"vehicle/boat","value":{"Sail":true}}
  97. ],
  98. "Child":{
  99. "Bool":false, "Float64":0, "Int32":0, "Int64":"0", "Int64Ptr":null,
  100. "String":"child", "StringPtrPtr":null, "Bytes":null,
  101. "Time":"0001-01-01T00:00:00Z",
  102. "Car":null, "Boat":{"Sail":false}, "Vehicles":null, "Child":null
  103. },
  104. "private": "foo", "unknown": "bar"
  105. }`,
  106. Struct{
  107. Bool: true, Float64: 3.14, Int32: 32, Int64: 64, Int64Ptr: &i64,
  108. String: "foo", StringPtrPtr: &strPtr, Bytes: []byte{1, 2, 3},
  109. Time: time.Date(2020, 6, 2, 16, 5, 13, 4346374, time.UTC),
  110. Car: &Car{Wheels: 4}, Boat: Boat{Sail: true}, Vehicles: []Vehicle{
  111. Vehicle(&Car{Wheels: 4}),
  112. Vehicle(Boat{Sail: true}),
  113. },
  114. Child: &Struct{Bool: false, String: "child"},
  115. },
  116. false,
  117. },
  118. "struct key into vehicle": {`{"Vehicles":[
  119. {"type":"vehicle/car","value":{"Wheels":4}},
  120. {"type":"key/public","value":"MTIzNDU2Nzg="}
  121. ]}`, Struct{}, true},
  122. "struct ptr null": {`null`, structNil, false},
  123. "custom value": {`{"Value":"foo"}`, CustomValue{}, false},
  124. "custom ptr": {`"foo"`, &CustomPtr{Value: "custom"}, false},
  125. "custom ptr value": {`"foo"`, CustomPtr{Value: "custom"}, false},
  126. "invalid type": {`"foo"`, Struct{}, true},
  127. }
  128. for name, tc := range testcases {
  129. tc := tc
  130. t.Run(name, func(t *testing.T) {
  131. // Create a target variable as a pointer to the zero value of the tc.value type,
  132. // and wrap it in an empty interface. Decode into that interface.
  133. target := reflect.New(reflect.TypeOf(tc.value)).Interface()
  134. err := json.Unmarshal([]byte(tc.json), target)
  135. if tc.err {
  136. require.Error(t, err)
  137. return
  138. }
  139. require.NoError(t, err)
  140. // Unwrap the target pointer and get the value behind the interface.
  141. actual := reflect.ValueOf(target).Elem().Interface()
  142. assert.Equal(t, tc.value, actual)
  143. })
  144. }
  145. }