mirror of
https://github.com/sourcegraph/jsonrpc2.git
synced 2026-06-18 05:00:03 +02:00
The JSON-RPC 2.0 specification allows the params member of a request to be omitted [1]. Before this commit, this library did not allow the params member to be omitted. When the params argument of the Conn.Call or Conn.Notify method was set to nil, then Request.Params was set to the JSON encoding of nil which is null. This commit adds a ConnOption named OmitNilParams. If OmitNilParams is applied on Conn and Conn.Call or Conn.Notify are invoked with their params argument set to nil, then the params member in the JSON encoding of Request is omitted. If the OmitNilParams option is not applied on Conn then the previous behavior is maintained. In other words, the changes in this commit are backwards compatible. References [1]: https://www.jsonrpc.org/specification#request_object
166 lines
3.6 KiB
Go
166 lines
3.6 KiB
Go
package jsonrpc2_test
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/sourcegraph/jsonrpc2"
|
|
)
|
|
|
|
func TestSetLogger(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
rd, wr := io.Pipe()
|
|
defer rd.Close()
|
|
defer wr.Close()
|
|
|
|
buf := bufio.NewReader(rd)
|
|
logger := log.New(wr, "", log.Lmsgprefix)
|
|
|
|
a, b := net.Pipe()
|
|
connA := jsonrpc2.NewConn(
|
|
ctx,
|
|
jsonrpc2.NewBufferedStream(a, jsonrpc2.VSCodeObjectCodec{}),
|
|
noopHandler{},
|
|
jsonrpc2.SetLogger(logger),
|
|
)
|
|
connB := jsonrpc2.NewConn(
|
|
ctx,
|
|
jsonrpc2.NewBufferedStream(b, jsonrpc2.VSCodeObjectCodec{}),
|
|
noopHandler{},
|
|
)
|
|
defer connA.Close()
|
|
defer connB.Close()
|
|
|
|
// Write a response with no corresponding request.
|
|
if err := connB.Reply(ctx, jsonrpc2.ID{Num: 0}, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := "jsonrpc2: ignoring response #0 with no corresponding request\n"
|
|
got, err := buf.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != want {
|
|
t.Fatalf("got %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestOmitNilParams(t *testing.T) {
|
|
rawJSONMessage := func(v string) *json.RawMessage {
|
|
b := []byte(v)
|
|
return (*json.RawMessage)(&b)
|
|
}
|
|
|
|
type testCase struct {
|
|
connOpt jsonrpc2.ConnOpt
|
|
sendParams interface{}
|
|
wantParams *json.RawMessage
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
sendParams: nil,
|
|
wantParams: rawJSONMessage("null"),
|
|
},
|
|
{
|
|
sendParams: rawJSONMessage("null"),
|
|
wantParams: rawJSONMessage("null"),
|
|
},
|
|
{
|
|
connOpt: jsonrpc2.OmitNilParams(),
|
|
sendParams: nil,
|
|
wantParams: nil,
|
|
},
|
|
{
|
|
connOpt: jsonrpc2.OmitNilParams(),
|
|
sendParams: rawJSONMessage("null"),
|
|
wantParams: rawJSONMessage("null"),
|
|
},
|
|
}
|
|
|
|
assert := func(got *json.RawMessage, want *json.RawMessage) error {
|
|
// Assert pointers.
|
|
if got == nil || want == nil {
|
|
if got != want {
|
|
return fmt.Errorf("got %v, want %v", got, want)
|
|
}
|
|
return nil
|
|
}
|
|
{
|
|
// If pointers are not nil, then assert values.
|
|
got := string(*got)
|
|
want := string(*want)
|
|
if got != want {
|
|
return fmt.Errorf("got %q, want %q", got, want)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
newClientServer := func(handler jsonrpc2.Handler, connOpt jsonrpc2.ConnOpt) (client *jsonrpc2.Conn, server *jsonrpc2.Conn) {
|
|
ctx := context.Background()
|
|
connA, connB := net.Pipe()
|
|
client = jsonrpc2.NewConn(
|
|
ctx,
|
|
jsonrpc2.NewPlainObjectStream(connA),
|
|
noopHandler{},
|
|
connOpt,
|
|
)
|
|
server = jsonrpc2.NewConn(
|
|
ctx,
|
|
jsonrpc2.NewPlainObjectStream(connB),
|
|
handler,
|
|
connOpt,
|
|
)
|
|
return client, server
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
|
|
t.Run(fmt.Sprintf("test case %v", i), func(t *testing.T) {
|
|
t.Run("call", func(t *testing.T) {
|
|
handler := jsonrpc2.HandlerWithError(func(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) (result interface{}, err error) {
|
|
return nil, assert(r.Params, tc.wantParams)
|
|
})
|
|
|
|
client, server := newClientServer(handler, tc.connOpt)
|
|
defer client.Close()
|
|
defer server.Close()
|
|
|
|
if err := client.Call(context.Background(), "f", tc.sendParams, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
t.Run("notify", func(t *testing.T) {
|
|
wg := &sync.WaitGroup{}
|
|
handler := handlerFunc(func(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) {
|
|
err := assert(req.Params, tc.wantParams)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
wg.Done()
|
|
})
|
|
|
|
client, server := newClientServer(handler, tc.connOpt)
|
|
defer client.Close()
|
|
defer server.Close()
|
|
|
|
wg.Add(1)
|
|
if err := client.Notify(context.Background(), "f", tc.sendParams); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wg.Wait()
|
|
})
|
|
})
|
|
}
|
|
}
|