- package rpcserver
-
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "strconv"
- "testing"
-
- "github.com/stretchr/testify/assert"
- amino "github.com/tendermint/go-amino"
-
- "github.com/tendermint/tendermint/libs/bytes"
- types "github.com/tendermint/tendermint/rpc/lib/types"
- )
-
- func TestParseJSONMap(t *testing.T) {
- input := []byte(`{"value":"1234","height":22}`)
-
- // naive is float,string
- var p1 map[string]interface{}
- err := json.Unmarshal(input, &p1)
- if assert.Nil(t, err) {
- h, ok := p1["height"].(float64)
- if assert.True(t, ok, "%#v", p1["height"]) {
- assert.EqualValues(t, 22, h)
- }
- v, ok := p1["value"].(string)
- if assert.True(t, ok, "%#v", p1["value"]) {
- assert.EqualValues(t, "1234", v)
- }
- }
-
- // preloading map with values doesn't help
- tmp := 0
- p2 := map[string]interface{}{
- "value": &bytes.HexBytes{},
- "height": &tmp,
- }
- err = json.Unmarshal(input, &p2)
- if assert.Nil(t, err) {
- h, ok := p2["height"].(float64)
- if assert.True(t, ok, "%#v", p2["height"]) {
- assert.EqualValues(t, 22, h)
- }
- v, ok := p2["value"].(string)
- if assert.True(t, ok, "%#v", p2["value"]) {
- assert.EqualValues(t, "1234", v)
- }
- }
-
- // preload here with *pointers* to the desired types
- // struct has unknown types, but hard-coded keys
- tmp = 0
- p3 := struct {
- Value interface{} `json:"value"`
- Height interface{} `json:"height"`
- }{
- Height: &tmp,
- Value: &bytes.HexBytes{},
- }
- err = json.Unmarshal(input, &p3)
- if assert.Nil(t, err) {
- h, ok := p3.Height.(*int)
- if assert.True(t, ok, "%#v", p3.Height) {
- assert.Equal(t, 22, *h)
- }
- v, ok := p3.Value.(*bytes.HexBytes)
- if assert.True(t, ok, "%#v", p3.Value) {
- assert.EqualValues(t, []byte{0x12, 0x34}, *v)
- }
- }
-
- // simplest solution, but hard-coded
- p4 := struct {
- Value bytes.HexBytes `json:"value"`
- Height int `json:"height"`
- }{}
- err = json.Unmarshal(input, &p4)
- if assert.Nil(t, err) {
- assert.EqualValues(t, 22, p4.Height)
- assert.EqualValues(t, []byte{0x12, 0x34}, p4.Value)
- }
-
- // so, let's use this trick...
- // dynamic keys on map, and we can deserialize to the desired types
- var p5 map[string]*json.RawMessage
- err = json.Unmarshal(input, &p5)
- if assert.Nil(t, err) {
- var h int
- err = json.Unmarshal(*p5["height"], &h)
- if assert.Nil(t, err) {
- assert.Equal(t, 22, h)
- }
-
- var v bytes.HexBytes
- err = json.Unmarshal(*p5["value"], &v)
- if assert.Nil(t, err) {
- assert.Equal(t, bytes.HexBytes{0x12, 0x34}, v)
- }
- }
- }
-
- func TestParseJSONArray(t *testing.T) {
- input := []byte(`["1234",22]`)
-
- // naive is float,string
- var p1 []interface{}
- err := json.Unmarshal(input, &p1)
- if assert.Nil(t, err) {
- v, ok := p1[0].(string)
- if assert.True(t, ok, "%#v", p1[0]) {
- assert.EqualValues(t, "1234", v)
- }
- h, ok := p1[1].(float64)
- if assert.True(t, ok, "%#v", p1[1]) {
- assert.EqualValues(t, 22, h)
- }
- }
-
- // preloading map with values helps here (unlike map - p2 above)
- tmp := 0
- p2 := []interface{}{&bytes.HexBytes{}, &tmp}
- err = json.Unmarshal(input, &p2)
- if assert.Nil(t, err) {
- v, ok := p2[0].(*bytes.HexBytes)
- if assert.True(t, ok, "%#v", p2[0]) {
- assert.EqualValues(t, []byte{0x12, 0x34}, *v)
- }
- h, ok := p2[1].(*int)
- if assert.True(t, ok, "%#v", p2[1]) {
- assert.EqualValues(t, 22, *h)
- }
- }
- }
-
- func TestParseJSONRPC(t *testing.T) {
- demo := func(ctx *types.Context, height int, name string) {}
- call := NewRPCFunc(demo, "height,name")
- cdc := amino.NewCodec()
-
- cases := []struct {
- raw string
- height int64
- name string
- fail bool
- }{
- // should parse
- {`["7", "flew"]`, 7, "flew", false},
- {`{"name": "john", "height": "22"}`, 22, "john", false},
- // defaults
- {`{"name": "solo", "unused": "stuff"}`, 0, "solo", false},
- // should fail - wrong types/length
- {`["flew", 7]`, 0, "", true},
- {`[7,"flew",100]`, 0, "", true},
- {`{"name": -12, "height": "fred"}`, 0, "", true},
- }
- for idx, tc := range cases {
- i := strconv.Itoa(idx)
- data := []byte(tc.raw)
- vals, err := jsonParamsToArgs(call, cdc, data)
- if tc.fail {
- assert.NotNil(t, err, i)
- } else {
- assert.Nil(t, err, "%s: %+v", i, err)
- if assert.Equal(t, 2, len(vals), i) {
- assert.Equal(t, tc.height, vals[0].Int(), i)
- assert.Equal(t, tc.name, vals[1].String(), i)
- }
- }
-
- }
- }
-
- func TestParseURI(t *testing.T) {
- demo := func(ctx *types.Context, height int, name string) {}
- call := NewRPCFunc(demo, "height,name")
- cdc := amino.NewCodec()
-
- cases := []struct {
- raw []string
- height int64
- name string
- fail bool
- }{
- // can parse numbers unquoted and strings quoted
- {[]string{"7", `"flew"`}, 7, "flew", false},
- {[]string{"22", `"john"`}, 22, "john", false},
- {[]string{"-10", `"bob"`}, -10, "bob", false},
- // can parse numbers quoted, too
- {[]string{`"7"`, `"flew"`}, 7, "flew", false},
- {[]string{`"-10"`, `"bob"`}, -10, "bob", false},
- // cant parse strings uquoted
- {[]string{`"-10"`, `bob`}, -10, "bob", true},
- }
- for idx, tc := range cases {
- i := strconv.Itoa(idx)
- // data := []byte(tc.raw)
- url := fmt.Sprintf(
- "test.com/method?height=%v&name=%v",
- tc.raw[0], tc.raw[1])
- req, err := http.NewRequest("GET", url, nil)
- assert.NoError(t, err)
- vals, err := httpParamsToArgs(call, cdc, req)
- if tc.fail {
- assert.NotNil(t, err, i)
- } else {
- assert.Nil(t, err, "%s: %+v", i, err)
- if assert.Equal(t, 2, len(vals), i) {
- assert.Equal(t, tc.height, vals[0].Int(), i)
- assert.Equal(t, tc.name, vals[1].String(), i)
- }
- }
-
- }
- }
|