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