2019-07-03 17:39:07 +00:00
package jsonrpc
2019-06-28 13:49:34 +00:00
import (
2019-06-28 14:53:01 +00:00
"context"
2019-06-28 13:49:34 +00:00
"errors"
2019-07-22 23:58:43 +00:00
"fmt"
2019-06-28 13:49:34 +00:00
"net/http/httptest"
"strconv"
2019-07-18 11:09:13 +00:00
"strings"
2019-07-02 19:08:30 +00:00
"sync"
2019-06-28 13:49:34 +00:00
"testing"
2019-06-28 14:53:01 +00:00
"time"
2019-07-22 20:28:15 +00:00
2019-07-22 22:47:02 +00:00
"github.com/stretchr/testify/require"
2019-06-28 13:49:34 +00:00
)
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 ( )
2019-06-29 09:19:06 +00:00
rpcServer . Register ( "SimpleServerHandler" , serverHandler )
2019-06-28 13:49:34 +00:00
// 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 )
2019-07-03 12:30:21 +00:00
defer closer ( )
2019-06-28 13:49:34 +00:00
// Add(int) error
2019-07-22 22:47:02 +00:00
require . NoError ( t , client . Add ( 2 ) )
require . Equal ( t , 2 , serverHandler . n )
2019-06-28 13:49:34 +00:00
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" )
2019-06-28 13:49:34 +00:00
// 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 )
2019-06-28 13:49:34 +00:00
// 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 )
2019-06-28 13:49:34 +00:00
_ , err = client . StringMatch ( TestType { S : "5" } , 5 )
2019-07-22 22:47:02 +00:00
require . EqualError ( t , err , ":(" )
2019-06-28 13:49:34 +00:00
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 )
2019-06-28 13:49:34 +00:00
// 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 )
2019-06-28 13:49:34 +00:00
// this one should actually work
noret . Add ( 4 )
2019-07-22 22:47:02 +00:00
require . Equal ( t , 9 , serverHandler . n )
2019-07-03 12:30:21 +00:00
closer ( )
2019-06-28 13:49:34 +00:00
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 )
2019-06-28 13:49:34 +00:00
// shouldn't panic
noparam . Add ( )
2019-07-03 12:30:21 +00:00
closer ( )
2019-06-28 13:49:34 +00:00
2019-06-28 14:05:10 +00:00
var erronly struct {
2019-06-28 14:53:01 +00:00
AddGet func ( ) ( int , error )
2019-06-28 14:05:10 +00:00
}
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 )
2019-06-28 14:05:10 +00:00
2019-06-28 14:53:01 +00:00
_ , 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 )
2019-06-28 14:05:10 +00:00
}
2019-07-03 12:30:21 +00:00
closer ( )
2019-06-28 14:05:10 +00:00
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 )
2019-06-28 14:05:10 +00:00
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 )
}
2019-07-03 12:30:21 +00:00
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 )
2019-06-28 14:05:10 +00:00
}
2019-07-03 12:30:21 +00:00
closer ( )
2019-06-28 13:49:34 +00:00
}
2019-06-28 14:53:01 +00:00
type CtxHandler struct {
2019-07-02 19:08:30 +00:00
lk sync . Mutex
2019-06-28 14:53:01 +00:00
cancelled bool
2019-07-02 19:08:30 +00:00
i int
2019-06-28 14:53:01 +00:00
}
func ( h * CtxHandler ) Test ( ctx context . Context ) {
2019-07-02 19:08:30 +00:00
h . lk . Lock ( )
defer h . lk . Unlock ( )
2019-06-28 14:53:01 +00:00
timeout := time . After ( 300 * time . Millisecond )
2019-07-02 19:08:30 +00:00
h . i ++
2019-06-28 14:53:01 +00:00
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 )
2019-06-28 14:53:01 +00:00
// 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-06-28 14:53:01 +00:00
2019-07-02 13:20:33 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 100 * time . Millisecond )
2019-06-28 14:53:01 +00:00
defer cancel ( )
client . Test ( ctx )
2019-07-02 19:08:30 +00:00
serverHandler . lk . Lock ( )
2019-06-28 14:53:01 +00:00
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 ( )
2019-07-03 12:30:21 +00:00
closer ( )
2019-07-02 19:08:30 +00:00
2019-06-28 14:53:01 +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 )
}
2019-06-28 14:53:01 +00:00
noCtxClient . Test ( )
2019-07-02 19:08:30 +00:00
serverHandler . lk . Lock ( )
if serverHandler . cancelled || serverHandler . i != 2 {
2019-06-28 14:53:01 +00:00
t . Error ( "wrong serverHandler state" )
}
2019-07-02 19:08:30 +00:00
serverHandler . lk . Unlock ( )
2019-07-03 12:30:21 +00:00
closer ( )
2019-07-01 20:00:22 +00:00
}
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 )
2020-01-21 13:48:17 +00:00
}
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
2020-01-21 13:48:17 +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 { } { }
}
ctx , _ := context . WithCancel ( context . Background ( ) )
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
}