Newer
Older
type ClientMaker func() codec.Conn
type ServerMaker func() (*server.Server, ClientMaker, func())
type BasicTestSuiteArgs struct {
ServerMaker ServerMaker
}
type TestContext func(t *testing.T, server *server.Server, client codec.Conn)
func TestExecutor(sm ServerMaker) func(t *testing.T, c TestContext) {
return func(t *testing.T, c TestContext) {
server, dialer, cn := sm()
defer cn()
defer server.Stop()
client := dialer()
defer client.Close()
c(t, server, client)
}
}
func BenchExecutor(sm ServerMaker) func(t *testing.B, c BenchContext) {
return func(t *testing.B, c BenchContext) {
server, dialer, cn := sm()
defer cn()
defer server.Stop()
client := dialer()
defer client.Close()
c(t, server, client)
}
}
// go:embed testdata/
var testData embed.FS
func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
var executeTest = TestExecutor(args.ServerMaker)
makeTest("Request", func(t *testing.T, server *server.Server, client codec.Conn) {
var resp EchoResult
err := client.Do(nil, &resp, "test_echo", []any{"hello", 10, &EchoArgs{"world"}})
require.NoError(t, err)
if !reflect.DeepEqual(resp, EchoResult{"hello", 10, &EchoArgs{"world"}}) {
t.Errorf("incorrect result %#v", resp)
}
})
makeTest("ResponseType", func(t *testing.T, server *server.Server, client codec.Conn) {
err := codec.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"})
assert.NoErrorf(t, err, "passing nil as result should be ok")
err = codec.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
assert.Error(t, err, "passing var as nil gives error")
makeTest("BatchRequest", func(t *testing.T, server *server.Server, client codec.Conn) {
batch := []*codec.BatchElem{
{
Method: "test_echo",
Params: []any{"hello", 10, &EchoArgs{"world"}},
Result: new(EchoResult),
},
{
Method: "test_echo",
Params: []any{"hello2", 11, &EchoArgs{"world"}},
Result: new(EchoResult),
},
{
Method: "test_echo",
Params: []any{"hello3", 12, &EchoArgs{"world"}},
IsNotification: true,
},
Params: []any{1, 2, 3},
Result: new(int),
},
}
if err := client.BatchCall(nil, batch...); err != nil {
t.Fatal(err)
}
{
Method: "test_echo",
Params: []any{"hello", 10, &EchoArgs{"world"}},
Result: &EchoResult{"hello", 10, &EchoArgs{"world"}},
},
{
Method: "test_echo",
Params: []any{"hello2", 11, &EchoArgs{"world"}},
Result: &EchoResult{"hello2", 11, &EchoArgs{"world"}},
},
{
Method: "test_echo",
Params: []any{"hello3", 12, &EchoArgs{"world"}},
},
Error: &codec.JsonError{Code: -32601, Message: "the method no/such/method does not exist/is not available"},
},
}
require.EqualValues(t, len(batch), len(wantResult))
for i := range batch {
a := batch[i]
assert.EqualValuesf(t, a.Method, b.Method, "item %d", i)
assert.EqualValuesf(t, a.Result, b.Result, "item %d", i)
assert.EqualValuesf(t, a.Params, b.Params, "item %d", i)
assert.EqualValuesf(t, a.Error, b.Error, "item %d", i)
makeTest("ResposeType2", func(t *testing.T, server *server.Server, client codec.Conn) {
if err := codec.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
}
var resultVar EchoResult
// Note: passing the var, not a ref
err := codec.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
if err == nil {
t.Error("Passing a var as result should be an error")
}
})
makeTest("ErrorReturnType", func(t *testing.T, server *server.Server, client codec.Conn) {
require.Error(t, err)
// Check code.
if e, ok := err.(codec.Error); !ok {
t.Fatalf("client did not return rpc.Error, got %#v", e)
} else if e.ErrorCode() != (testError{}.ErrorCode()) {
t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode())
}
// Check data.
if e, ok := err.(codec.DataError); !ok {
t.Fatalf("client did not return rpc.DataError, got %#v", e)
} else if e.ErrorData() != (testError{}.ErrorData()) {
t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData())
}
})
makeTest("Notify", func(t *testing.T, server *server.Server, client codec.Conn) {
if c, ok := client.(codec.StreamingConn); ok {
if err := c.Notify(context.Background(), "test_echo", []any{"hello", 10, &EchoArgs{"world"}}); err != nil {
t.Fatal(err)
}
makeTest("context cancel", func(t *testing.T, server *server.Server, client codec.Conn) {
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
maxContextCancelTimeout := 300 * time.Millisecond
// The actual test starts here.
var (
wg sync.WaitGroup
nreqs = 10
ncallers = 10
)
caller := func(index int) {
defer wg.Done()
for i := 0; i < nreqs; i++ {
var (
ctx context.Context
cancel func()
timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout)))
)
if index < ncallers/2 {
// For half of the callers, create a context without deadline
// and cancel it later.
ctx, cancel = context.WithCancel(context.Background())
time.AfterFunc(timeout, cancel)
} else {
// For the other half, create a context with a deadline instead. This is
// different because the context deadline is used to set the socket write
// deadline.
ctx, cancel = context.WithTimeout(context.Background(), timeout)
}
// Now perform a call with the context.
// The key thing here is that no call will ever complete successfully.
switch {
case err == nil:
_, hasDeadline := ctx.Deadline()
t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline)
// default:
// t.Logf("got expected error with %v wait time: %v", timeout, err)
}
cancel()
}
}
wg.Add(ncallers)
for i := 0; i < ncallers; i++ {
go caller(i)
}
wg.Wait()
})
makeTest("", func(t *testing.T, server *server.Server, client codec.Conn) {
})