github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/jsonrpc/server/parse_test.go (about) 1 package server 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 amino "github.com/tendermint/go-amino" 12 13 "github.com/okex/exchain/libs/tendermint/libs/bytes" 14 types "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types" 15 ) 16 17 func TestParseJSONMap(t *testing.T) { 18 input := []byte(`{"value":"1234","height":22}`) 19 20 // naive is float,string 21 var p1 map[string]interface{} 22 err := json.Unmarshal(input, &p1) 23 if assert.Nil(t, err) { 24 h, ok := p1["height"].(float64) 25 if assert.True(t, ok, "%#v", p1["height"]) { 26 assert.EqualValues(t, 22, h) 27 } 28 v, ok := p1["value"].(string) 29 if assert.True(t, ok, "%#v", p1["value"]) { 30 assert.EqualValues(t, "1234", v) 31 } 32 } 33 34 // preloading map with values doesn't help 35 tmp := 0 36 p2 := map[string]interface{}{ 37 "value": &bytes.HexBytes{}, 38 "height": &tmp, 39 } 40 err = json.Unmarshal(input, &p2) 41 if assert.Nil(t, err) { 42 h, ok := p2["height"].(float64) 43 if assert.True(t, ok, "%#v", p2["height"]) { 44 assert.EqualValues(t, 22, h) 45 } 46 v, ok := p2["value"].(string) 47 if assert.True(t, ok, "%#v", p2["value"]) { 48 assert.EqualValues(t, "1234", v) 49 } 50 } 51 52 // preload here with *pointers* to the desired types 53 // struct has unknown types, but hard-coded keys 54 tmp = 0 55 p3 := struct { 56 Value interface{} `json:"value"` 57 Height interface{} `json:"height"` 58 }{ 59 Height: &tmp, 60 Value: &bytes.HexBytes{}, 61 } 62 err = json.Unmarshal(input, &p3) 63 if assert.Nil(t, err) { 64 h, ok := p3.Height.(*int) 65 if assert.True(t, ok, "%#v", p3.Height) { 66 assert.Equal(t, 22, *h) 67 } 68 v, ok := p3.Value.(*bytes.HexBytes) 69 if assert.True(t, ok, "%#v", p3.Value) { 70 assert.EqualValues(t, []byte{0x12, 0x34}, *v) 71 } 72 } 73 74 // simplest solution, but hard-coded 75 p4 := struct { 76 Value bytes.HexBytes `json:"value"` 77 Height int `json:"height"` 78 }{} 79 err = json.Unmarshal(input, &p4) 80 if assert.Nil(t, err) { 81 assert.EqualValues(t, 22, p4.Height) 82 assert.EqualValues(t, []byte{0x12, 0x34}, p4.Value) 83 } 84 85 // so, let's use this trick... 86 // dynamic keys on map, and we can deserialize to the desired types 87 var p5 map[string]*json.RawMessage 88 err = json.Unmarshal(input, &p5) 89 if assert.Nil(t, err) { 90 var h int 91 err = json.Unmarshal(*p5["height"], &h) 92 if assert.Nil(t, err) { 93 assert.Equal(t, 22, h) 94 } 95 96 var v bytes.HexBytes 97 err = json.Unmarshal(*p5["value"], &v) 98 if assert.Nil(t, err) { 99 assert.Equal(t, bytes.HexBytes{0x12, 0x34}, v) 100 } 101 } 102 } 103 104 func TestParseJSONArray(t *testing.T) { 105 input := []byte(`["1234",22]`) 106 107 // naive is float,string 108 var p1 []interface{} 109 err := json.Unmarshal(input, &p1) 110 if assert.Nil(t, err) { 111 v, ok := p1[0].(string) 112 if assert.True(t, ok, "%#v", p1[0]) { 113 assert.EqualValues(t, "1234", v) 114 } 115 h, ok := p1[1].(float64) 116 if assert.True(t, ok, "%#v", p1[1]) { 117 assert.EqualValues(t, 22, h) 118 } 119 } 120 121 // preloading map with values helps here (unlike map - p2 above) 122 tmp := 0 123 p2 := []interface{}{&bytes.HexBytes{}, &tmp} 124 err = json.Unmarshal(input, &p2) 125 if assert.Nil(t, err) { 126 v, ok := p2[0].(*bytes.HexBytes) 127 if assert.True(t, ok, "%#v", p2[0]) { 128 assert.EqualValues(t, []byte{0x12, 0x34}, *v) 129 } 130 h, ok := p2[1].(*int) 131 if assert.True(t, ok, "%#v", p2[1]) { 132 assert.EqualValues(t, 22, *h) 133 } 134 } 135 } 136 137 func TestParseJSONRPC(t *testing.T) { 138 demo := func(ctx *types.Context, height int, name string) {} 139 call := NewRPCFunc(demo, "height,name") 140 cdc := amino.NewCodec() 141 142 cases := []struct { 143 raw string 144 height int64 145 name string 146 fail bool 147 }{ 148 // should parse 149 {`["7", "flew"]`, 7, "flew", false}, 150 {`{"name": "john", "height": "22"}`, 22, "john", false}, 151 // defaults 152 {`{"name": "solo", "unused": "stuff"}`, 0, "solo", false}, 153 // should fail - wrong types/length 154 {`["flew", 7]`, 0, "", true}, 155 {`[7,"flew",100]`, 0, "", true}, 156 {`{"name": -12, "height": "fred"}`, 0, "", true}, 157 } 158 for idx, tc := range cases { 159 i := strconv.Itoa(idx) 160 data := []byte(tc.raw) 161 vals, err := jsonParamsToArgs(call, cdc, data) 162 if tc.fail { 163 assert.NotNil(t, err, i) 164 } else { 165 assert.Nil(t, err, "%s: %+v", i, err) 166 if assert.Equal(t, 2, len(vals), i) { 167 assert.Equal(t, tc.height, vals[0].Int(), i) 168 assert.Equal(t, tc.name, vals[1].String(), i) 169 } 170 } 171 172 } 173 } 174 175 func TestParseURI(t *testing.T) { 176 demo := func(ctx *types.Context, height int, name string) {} 177 call := NewRPCFunc(demo, "height,name") 178 cdc := amino.NewCodec() 179 180 cases := []struct { 181 raw []string 182 height int64 183 name string 184 fail bool 185 }{ 186 // can parse numbers unquoted and strings quoted 187 {[]string{"7", `"flew"`}, 7, "flew", false}, 188 {[]string{"22", `"john"`}, 22, "john", false}, 189 {[]string{"-10", `"bob"`}, -10, "bob", false}, 190 // can parse numbers quoted, too 191 {[]string{`"7"`, `"flew"`}, 7, "flew", false}, 192 {[]string{`"-10"`, `"bob"`}, -10, "bob", false}, 193 // cant parse strings uquoted 194 {[]string{`"-10"`, `bob`}, -10, "bob", true}, 195 } 196 for idx, tc := range cases { 197 i := strconv.Itoa(idx) 198 // data := []byte(tc.raw) 199 url := fmt.Sprintf( 200 "test.com/method?height=%v&name=%v", 201 tc.raw[0], tc.raw[1]) 202 req, err := http.NewRequest("GET", url, nil) 203 assert.NoError(t, err) 204 vals, err := httpParamsToArgs(call, cdc, req) 205 if tc.fail { 206 assert.NotNil(t, err, i) 207 } else { 208 assert.Nil(t, err, "%s: %+v", i, err) 209 if assert.Equal(t, 2, len(vals), i) { 210 assert.Equal(t, tc.height, vals[0].Int(), i) 211 assert.Equal(t, tc.name, vals[1].String(), i) 212 } 213 } 214 215 } 216 }