github.com/noirx94/tendermintmp@v0.0.1/rpc/jsonrpc/jsonrpc_test.go (about) 1 package jsonrpc 2 3 import ( 4 "bytes" 5 "context" 6 crand "crypto/rand" 7 "encoding/json" 8 "fmt" 9 "net/http" 10 "os" 11 "os/exec" 12 "testing" 13 "time" 14 15 "github.com/go-kit/kit/log/term" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 tmbytes "github.com/tendermint/tendermint/libs/bytes" 20 "github.com/tendermint/tendermint/libs/log" 21 tmrand "github.com/tendermint/tendermint/libs/rand" 22 23 client "github.com/tendermint/tendermint/rpc/jsonrpc/client" 24 server "github.com/tendermint/tendermint/rpc/jsonrpc/server" 25 types "github.com/tendermint/tendermint/rpc/jsonrpc/types" 26 ) 27 28 // Client and Server should work over tcp or unix sockets 29 const ( 30 tcpAddr = "tcp://127.0.0.1:47768" 31 32 unixSocket = "/tmp/rpc_test.sock" 33 unixAddr = "unix://" + unixSocket 34 35 websocketEndpoint = "/websocket/endpoint" 36 37 testVal = "acbd" 38 ) 39 40 var ( 41 ctx = context.Background() 42 ) 43 44 type ResultEcho struct { 45 Value string `json:"value"` 46 } 47 48 type ResultEchoInt struct { 49 Value int `json:"value"` 50 } 51 52 type ResultEchoBytes struct { 53 Value []byte `json:"value"` 54 } 55 56 type ResultEchoDataBytes struct { 57 Value tmbytes.HexBytes `json:"value"` 58 } 59 60 // Define some routes 61 var Routes = map[string]*server.RPCFunc{ 62 "echo": server.NewRPCFunc(EchoResult, "arg"), 63 "echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"), 64 "echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"), 65 "echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"), 66 "echo_int": server.NewRPCFunc(EchoIntResult, "arg"), 67 } 68 69 func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) { 70 return &ResultEcho{v}, nil 71 } 72 73 func EchoWSResult(ctx *types.Context, v string) (*ResultEcho, error) { 74 return &ResultEcho{v}, nil 75 } 76 77 func EchoIntResult(ctx *types.Context, v int) (*ResultEchoInt, error) { 78 return &ResultEchoInt{v}, nil 79 } 80 81 func EchoBytesResult(ctx *types.Context, v []byte) (*ResultEchoBytes, error) { 82 return &ResultEchoBytes{v}, nil 83 } 84 85 func EchoDataBytesResult(ctx *types.Context, v tmbytes.HexBytes) (*ResultEchoDataBytes, error) { 86 return &ResultEchoDataBytes{v}, nil 87 } 88 89 func TestMain(m *testing.M) { 90 setup() 91 code := m.Run() 92 os.Exit(code) 93 } 94 95 var colorFn = func(keyvals ...interface{}) term.FgBgColor { 96 for i := 0; i < len(keyvals)-1; i += 2 { 97 if keyvals[i] == "socket" { 98 if keyvals[i+1] == "tcp" { 99 return term.FgBgColor{Fg: term.DarkBlue} 100 } else if keyvals[i+1] == "unix" { 101 return term.FgBgColor{Fg: term.DarkCyan} 102 } 103 } 104 } 105 return term.FgBgColor{} 106 } 107 108 // launch unix and tcp servers 109 func setup() { 110 logger := log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn) 111 112 cmd := exec.Command("rm", "-f", unixSocket) 113 err := cmd.Start() 114 if err != nil { 115 panic(err) 116 } 117 if err = cmd.Wait(); err != nil { 118 panic(err) 119 } 120 121 tcpLogger := logger.With("socket", "tcp") 122 mux := http.NewServeMux() 123 server.RegisterRPCFuncs(mux, Routes, tcpLogger) 124 wm := server.NewWebsocketManager(Routes, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second)) 125 wm.SetLogger(tcpLogger) 126 mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler) 127 config := server.DefaultConfig() 128 listener1, err := server.Listen(tcpAddr, config) 129 if err != nil { 130 panic(err) 131 } 132 go func() { 133 if err := server.Serve(listener1, mux, tcpLogger, config); err != nil { 134 panic(err) 135 } 136 }() 137 138 unixLogger := logger.With("socket", "unix") 139 mux2 := http.NewServeMux() 140 server.RegisterRPCFuncs(mux2, Routes, unixLogger) 141 wm = server.NewWebsocketManager(Routes) 142 wm.SetLogger(unixLogger) 143 mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler) 144 listener2, err := server.Listen(unixAddr, config) 145 if err != nil { 146 panic(err) 147 } 148 go func() { 149 if err := server.Serve(listener2, mux2, unixLogger, config); err != nil { 150 panic(err) 151 } 152 }() 153 154 // wait for servers to start 155 time.Sleep(time.Second * 2) 156 } 157 158 func echoViaHTTP(cl client.Caller, val string) (string, error) { 159 params := map[string]interface{}{ 160 "arg": val, 161 } 162 result := new(ResultEcho) 163 if _, err := cl.Call(ctx, "echo", params, result); err != nil { 164 return "", err 165 } 166 return result.Value, nil 167 } 168 169 func echoIntViaHTTP(cl client.Caller, val int) (int, error) { 170 params := map[string]interface{}{ 171 "arg": val, 172 } 173 result := new(ResultEchoInt) 174 if _, err := cl.Call(ctx, "echo_int", params, result); err != nil { 175 return 0, err 176 } 177 return result.Value, nil 178 } 179 180 func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) { 181 params := map[string]interface{}{ 182 "arg": bytes, 183 } 184 result := new(ResultEchoBytes) 185 if _, err := cl.Call(ctx, "echo_bytes", params, result); err != nil { 186 return []byte{}, err 187 } 188 return result.Value, nil 189 } 190 191 func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.HexBytes, error) { 192 params := map[string]interface{}{ 193 "arg": bytes, 194 } 195 result := new(ResultEchoDataBytes) 196 if _, err := cl.Call(ctx, "echo_data_bytes", params, result); err != nil { 197 return []byte{}, err 198 } 199 return result.Value, nil 200 } 201 202 func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { 203 val := testVal 204 got, err := echoViaHTTP(cl, val) 205 require.Nil(t, err) 206 assert.Equal(t, got, val) 207 208 val2 := randBytes(t) 209 got2, err := echoBytesViaHTTP(cl, val2) 210 require.Nil(t, err) 211 assert.Equal(t, got2, val2) 212 213 val3 := tmbytes.HexBytes(randBytes(t)) 214 got3, err := echoDataBytesViaHTTP(cl, val3) 215 require.Nil(t, err) 216 assert.Equal(t, got3, val3) 217 218 val4 := tmrand.Intn(10000) 219 got4, err := echoIntViaHTTP(cl, val4) 220 require.Nil(t, err) 221 assert.Equal(t, got4, val4) 222 } 223 224 func echoViaWS(cl *client.WSClient, val string) (string, error) { 225 params := map[string]interface{}{ 226 "arg": val, 227 } 228 err := cl.Call(context.Background(), "echo", params) 229 if err != nil { 230 return "", err 231 } 232 233 msg := <-cl.ResponsesCh 234 if msg.Error != nil { 235 return "", err 236 237 } 238 result := new(ResultEcho) 239 err = json.Unmarshal(msg.Result, result) 240 if err != nil { 241 return "", nil 242 } 243 return result.Value, nil 244 } 245 246 func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) { 247 params := map[string]interface{}{ 248 "arg": bytes, 249 } 250 err := cl.Call(context.Background(), "echo_bytes", params) 251 if err != nil { 252 return []byte{}, err 253 } 254 255 msg := <-cl.ResponsesCh 256 if msg.Error != nil { 257 return []byte{}, msg.Error 258 259 } 260 result := new(ResultEchoBytes) 261 err = json.Unmarshal(msg.Result, result) 262 if err != nil { 263 return []byte{}, nil 264 } 265 return result.Value, nil 266 } 267 268 func testWithWSClient(t *testing.T, cl *client.WSClient) { 269 val := testVal 270 got, err := echoViaWS(cl, val) 271 require.Nil(t, err) 272 assert.Equal(t, got, val) 273 274 val2 := randBytes(t) 275 got2, err := echoBytesViaWS(cl, val2) 276 require.Nil(t, err) 277 assert.Equal(t, got2, val2) 278 } 279 280 //------------- 281 282 func TestServersAndClientsBasic(t *testing.T) { 283 serverAddrs := [...]string{tcpAddr, unixAddr} 284 for _, addr := range serverAddrs { 285 cl1, err := client.NewURI(addr) 286 require.Nil(t, err) 287 fmt.Printf("=== testing server on %s using URI client", addr) 288 testWithHTTPClient(t, cl1) 289 290 cl2, err := client.New(addr) 291 require.Nil(t, err) 292 fmt.Printf("=== testing server on %s using JSONRPC client", addr) 293 testWithHTTPClient(t, cl2) 294 295 cl3, err := client.NewWS(addr, websocketEndpoint) 296 require.Nil(t, err) 297 cl3.SetLogger(log.TestingLogger()) 298 err = cl3.Start() 299 require.Nil(t, err) 300 fmt.Printf("=== testing server on %s using WS client", addr) 301 testWithWSClient(t, cl3) 302 err = cl3.Stop() 303 require.NoError(t, err) 304 } 305 } 306 307 func TestHexStringArg(t *testing.T) { 308 cl, err := client.NewURI(tcpAddr) 309 require.Nil(t, err) 310 // should NOT be handled as hex 311 val := "0xabc" 312 got, err := echoViaHTTP(cl, val) 313 require.Nil(t, err) 314 assert.Equal(t, got, val) 315 } 316 317 func TestQuotedStringArg(t *testing.T) { 318 cl, err := client.NewURI(tcpAddr) 319 require.Nil(t, err) 320 // should NOT be unquoted 321 val := "\"abc\"" 322 got, err := echoViaHTTP(cl, val) 323 require.Nil(t, err) 324 assert.Equal(t, got, val) 325 } 326 327 func TestWSNewWSRPCFunc(t *testing.T) { 328 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 329 require.Nil(t, err) 330 cl.SetLogger(log.TestingLogger()) 331 err = cl.Start() 332 require.Nil(t, err) 333 t.Cleanup(func() { 334 if err := cl.Stop(); err != nil { 335 t.Error(err) 336 } 337 }) 338 339 val := testVal 340 params := map[string]interface{}{ 341 "arg": val, 342 } 343 err = cl.Call(context.Background(), "echo_ws", params) 344 require.Nil(t, err) 345 346 msg := <-cl.ResponsesCh 347 if msg.Error != nil { 348 t.Fatal(err) 349 } 350 result := new(ResultEcho) 351 err = json.Unmarshal(msg.Result, result) 352 require.Nil(t, err) 353 got := result.Value 354 assert.Equal(t, got, val) 355 } 356 357 func TestWSHandlesArrayParams(t *testing.T) { 358 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 359 require.Nil(t, err) 360 cl.SetLogger(log.TestingLogger()) 361 err = cl.Start() 362 require.Nil(t, err) 363 t.Cleanup(func() { 364 if err := cl.Stop(); err != nil { 365 t.Error(err) 366 } 367 }) 368 369 val := testVal 370 params := []interface{}{val} 371 err = cl.CallWithArrayParams(context.Background(), "echo_ws", params) 372 require.Nil(t, err) 373 374 msg := <-cl.ResponsesCh 375 if msg.Error != nil { 376 t.Fatalf("%+v", err) 377 } 378 result := new(ResultEcho) 379 err = json.Unmarshal(msg.Result, result) 380 require.Nil(t, err) 381 got := result.Value 382 assert.Equal(t, got, val) 383 } 384 385 // TestWSClientPingPong checks that a client & server exchange pings 386 // & pongs so connection stays alive. 387 func TestWSClientPingPong(t *testing.T) { 388 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 389 require.Nil(t, err) 390 cl.SetLogger(log.TestingLogger()) 391 err = cl.Start() 392 require.Nil(t, err) 393 t.Cleanup(func() { 394 if err := cl.Stop(); err != nil { 395 t.Error(err) 396 } 397 }) 398 399 time.Sleep(6 * time.Second) 400 } 401 402 func randBytes(t *testing.T) []byte { 403 n := tmrand.Intn(10) + 2 404 buf := make([]byte, n) 405 _, err := crand.Read(buf) 406 require.Nil(t, err) 407 return bytes.ReplaceAll(buf, []byte("="), []byte{100}) 408 }