lotus/lib/jsonrpc/rpc_test.go

483 lines
9.4 KiB
Go
Raw Normal View History

2019-07-03 17:39:07 +00:00
package jsonrpc
import (
"context"
"errors"
2019-07-22 23:58:43 +00:00
"fmt"
"net"
"net/http/httptest"
"strconv"
2019-07-18 11:09:13 +00:00
"strings"
2019-07-02 19:08:30 +00:00
"sync"
"testing"
"time"
2019-07-22 20:28:15 +00:00
2019-07-22 22:47:02 +00:00
"github.com/stretchr/testify/require"
)
type SimpleServerHandler struct {
n int
}
type TestType struct {
S string
I int
}
type TestOut struct {
TestType
Ok bool
}
func (h *SimpleServerHandler) Add(in int) error {
if in == -3546 {
return errors.New("test")
}
h.n += in
return nil
}
func (h *SimpleServerHandler) AddGet(in int) int {
h.n += in
return h.n
}
func (h *SimpleServerHandler) StringMatch(t TestType, i2 int64) (out TestOut, err error) {
if strconv.FormatInt(i2, 10) == t.S {
out.Ok = true
}
if i2 != int64(t.I) {
return TestOut{}, errors.New(":(")
}
out.I = t.I
out.S = t.S
return
}
func TestRPC(t *testing.T) {
// setup server
serverHandler := &SimpleServerHandler{}
2019-07-08 12:46:30 +00:00
rpcServer := NewServer()
rpcServer.Register("SimpleServerHandler", serverHandler)
// httptest stuff
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
// setup client
var client struct {
Add func(int) error
AddGet func(int) int
StringMatch func(t TestType, i2 int64) (out TestOut, err error)
}
2019-07-23 18:49:09 +00:00
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &client, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
defer closer()
// Add(int) error
2019-07-22 22:47:02 +00:00
require.NoError(t, client.Add(2))
require.Equal(t, 2, serverHandler.n)
2019-07-12 17:12:51 +00:00
err = client.Add(-3546)
2019-07-22 22:47:02 +00:00
require.EqualError(t, err, "test")
// AddGet(int) int
n := client.AddGet(3)
2019-07-22 22:47:02 +00:00
require.Equal(t, 5, n)
require.Equal(t, 5, serverHandler.n)
// StringMatch
o, err := client.StringMatch(TestType{S: "0"}, 0)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
require.Equal(t, "0", o.S)
require.Equal(t, 0, o.I)
_, err = client.StringMatch(TestType{S: "5"}, 5)
2019-07-22 22:47:02 +00:00
require.EqualError(t, err, ":(")
o, err = client.StringMatch(TestType{S: "8", I: 8}, 8)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
require.Equal(t, "8", o.S)
require.Equal(t, 8, o.I)
// Invalid client handlers
var noret struct {
Add func(int)
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noret, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
// this one should actually work
noret.Add(4)
2019-07-22 22:47:02 +00:00
require.Equal(t, 9, serverHandler.n)
closer()
var noparam struct {
Add func()
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noparam, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
// shouldn't panic
noparam.Add()
closer()
var erronly struct {
AddGet func() (int, error)
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &erronly, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
_, err = erronly.AddGet()
2019-07-02 13:49:10 +00:00
if err == nil || err.Error() != "RPC error (-32602): wrong param count" {
t.Error("wrong error:", err)
}
closer()
var wrongtype struct {
Add func(string) error
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &wrongtype, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
err = wrongtype.Add("not an int")
2019-07-18 11:09:13 +00:00
if err == nil || !strings.Contains(err.Error(), "RPC error (-32700):") || !strings.Contains(err.Error(), "json: cannot unmarshal string into Go value of type int") {
2019-07-02 13:49:10 +00:00
t.Error("wrong error:", err)
}
closer()
2019-07-02 13:49:10 +00:00
var notfound struct {
NotThere func(string) error
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &notfound, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
2019-07-02 13:49:10 +00:00
err = notfound.NotThere("hello?")
if err == nil || err.Error() != "RPC error (-32601): method 'SimpleServerHandler.NotThere' not found" {
t.Error("wrong error:", err)
}
closer()
}
type CtxHandler struct {
2019-07-02 19:08:30 +00:00
lk sync.Mutex
cancelled bool
2019-07-02 19:08:30 +00:00
i int
}
func (h *CtxHandler) Test(ctx context.Context) {
2019-07-02 19:08:30 +00:00
h.lk.Lock()
defer h.lk.Unlock()
timeout := time.After(300 * time.Millisecond)
2019-07-02 19:08:30 +00:00
h.i++
select {
case <-timeout:
case <-ctx.Done():
h.cancelled = true
}
}
func TestCtx(t *testing.T) {
// setup server
serverHandler := &CtxHandler{}
2019-07-08 12:46:30 +00:00
rpcServer := NewServer()
2019-07-02 13:20:33 +00:00
rpcServer.Register("CtxHandler", serverHandler)
// httptest stuff
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
// setup client
var client struct {
Test func(ctx context.Context)
}
2019-07-23 18:49:09 +00:00
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &client, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
2019-07-02 13:20:33 +00:00
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
client.Test(ctx)
2019-07-02 19:08:30 +00:00
serverHandler.lk.Lock()
if !serverHandler.cancelled {
t.Error("expected cancellation on the server side")
}
serverHandler.cancelled = false
2019-07-02 19:08:30 +00:00
serverHandler.lk.Unlock()
closer()
2019-07-02 19:08:30 +00:00
var noCtxClient struct {
Test func()
}
2019-07-23 18:49:09 +00:00
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &noCtxClient, nil)
2019-07-12 17:12:51 +00:00
if err != nil {
t.Fatal(err)
}
noCtxClient.Test()
2019-07-02 19:08:30 +00:00
serverHandler.lk.Lock()
if serverHandler.cancelled || serverHandler.i != 2 {
t.Error("wrong serverHandler state")
}
2019-07-02 19:08:30 +00:00
serverHandler.lk.Unlock()
closer()
}
2019-07-22 22:47:02 +00:00
2019-07-22 22:57:08 +00:00
type UnUnmarshalable int
2019-07-22 23:58:43 +00:00
2019-07-22 22:57:08 +00:00
func (*UnUnmarshalable) UnmarshalJSON([]byte) error {
return errors.New("nope")
}
2019-07-22 23:58:43 +00:00
type UnUnmarshalableHandler struct{}
2019-07-22 22:57:08 +00:00
func (*UnUnmarshalableHandler) GetUnUnmarshalableStuff() (UnUnmarshalable, error) {
return UnUnmarshalable(5), nil
}
func TestUnmarshalableResult(t *testing.T) {
var client struct {
GetUnUnmarshalableStuff func() (UnUnmarshalable, error)
}
rpcServer := NewServer()
rpcServer.Register("Handler", &UnUnmarshalableHandler{})
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
2019-07-23 22:39:48 +00:00
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "Handler", &client, nil)
2019-07-22 22:57:08 +00:00
require.NoError(t, err)
defer closer()
_, err = client.GetUnUnmarshalableStuff()
require.EqualError(t, err, "RPC client error: unmarshaling result: nope")
}
2019-07-22 22:47:02 +00:00
type ChanHandler struct {
wait chan struct{}
}
2019-07-22 23:58:43 +00:00
func (h *ChanHandler) Sub(ctx context.Context, i int, eq int) (<-chan int, error) {
2019-07-22 22:47:02 +00:00
out := make(chan int)
go func() {
defer close(out)
var n int
for {
2019-07-22 23:58:43 +00:00
select {
case <-ctx.Done():
fmt.Println("ctxdone1")
return
case <-h.wait:
}
2019-07-22 22:47:02 +00:00
n += i
2019-07-22 23:58:43 +00:00
if n == eq {
fmt.Println("eq")
return
}
2019-07-22 22:47:02 +00:00
select {
case <-ctx.Done():
2019-07-22 23:58:43 +00:00
fmt.Println("ctxdone2")
2019-07-22 22:47:02 +00:00
return
case out <- n:
}
}
}()
return out, nil
}
func TestChan(t *testing.T) {
var client struct {
2019-07-22 23:58:43 +00:00
Sub func(context.Context, int, int) (<-chan int, error)
2019-07-22 22:47:02 +00:00
}
serverHandler := &ChanHandler{
wait: make(chan struct{}, 5),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
2019-07-23 22:39:48 +00:00
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
defer closer()
serverHandler.wait <- struct{}{}
ctx, cancel := context.WithCancel(context.Background())
2019-07-22 23:58:43 +00:00
// sub
2019-07-22 22:47:02 +00:00
2019-07-22 23:58:43 +00:00
sub, err := client.Sub(ctx, 2, -1)
2019-07-22 22:47:02 +00:00
require.NoError(t, err)
2019-07-22 23:58:43 +00:00
// recv one
2019-07-22 22:47:02 +00:00
require.Equal(t, 2, <-sub)
2019-07-22 23:58:43 +00:00
// recv many (order)
serverHandler.wait <- struct{}{}
serverHandler.wait <- struct{}{}
serverHandler.wait <- struct{}{}
require.Equal(t, 4, <-sub)
require.Equal(t, 6, <-sub)
require.Equal(t, 8, <-sub)
// close (through ctx)
cancel()
_, ok := <-sub
require.Equal(t, false, ok)
// sub (again)
serverHandler.wait <- struct{}{}
ctx, cancel = context.WithCancel(context.Background())
defer cancel()
sub, err = client.Sub(ctx, 3, 6)
require.NoError(t, err)
require.Equal(t, 3, <-sub)
// close (remote)
serverHandler.wait <- struct{}{}
_, ok = <-sub
require.Equal(t, false, ok)
}
func TestChanServerClose(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
serverHandler := &ChanHandler{
wait: make(chan struct{}, 5),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
tctx, tcancel := context.WithCancel(context.Background())
testServ := httptest.NewServer(rpcServer)
testServ.Config.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
return tctx
}
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
serverHandler.wait <- struct{}{}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// sub
sub, err := client.Sub(ctx, 2, -1)
require.NoError(t, err)
// recv one
require.Equal(t, 2, <-sub)
// make sure we're blocked
select {
case <-time.After(200 * time.Millisecond):
case <-sub:
t.Fatal("didn't expect to get anything from sub")
}
// close server
tcancel()
testServ.Close()
_, ok := <-sub
require.Equal(t, false, ok)
}
func TestControlChanDeadlock(t *testing.T) {
for r := 0; r < 20; r++ {
testControlChanDeadlock(t)
}
}
func testControlChanDeadlock(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
n := 5000
2019-07-22 23:58:43 +00:00
serverHandler := &ChanHandler{
wait: make(chan struct{}, n),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
for i := 0; i < n; i++ {
serverHandler.wait <- struct{}{}
}
2020-03-06 06:46:07 +00:00
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sub, err := client.Sub(ctx, 1, -1)
require.NoError(t, err)
go func() {
for i := 0; i < n; i++ {
require.Equal(t, i+1, <-sub)
}
}()
_, err = client.Sub(ctx, 2, -1)
require.NoError(t, err)
2019-07-22 22:47:02 +00:00
}