github.com/sourcegraph/jsonrpc2@v0.2.0/conn_test.go (about)

     1  package jsonrpc2_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"net"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/sourcegraph/jsonrpc2"
    15  )
    16  
    17  var paramsTests = []struct {
    18  	sendParams interface{}
    19  	wantParams *json.RawMessage
    20  }{
    21  	{
    22  		sendParams: nil,
    23  		wantParams: nil,
    24  	},
    25  	{
    26  		sendParams: jsonNull,
    27  		wantParams: &jsonNull,
    28  	},
    29  	{
    30  		sendParams: false,
    31  		wantParams: rawJSONMessage("false"),
    32  	},
    33  	{
    34  		sendParams: 0,
    35  		wantParams: rawJSONMessage("0"),
    36  	},
    37  	{
    38  		sendParams: "",
    39  		wantParams: rawJSONMessage(`""`),
    40  	},
    41  	{
    42  		sendParams: rawJSONMessage(`{"foo":"bar"}`),
    43  		wantParams: rawJSONMessage(`{"foo":"bar"}`),
    44  	},
    45  }
    46  
    47  func TestConn_DispatchCall(t *testing.T) {
    48  	for _, test := range paramsTests {
    49  		t.Run(fmt.Sprintf("%s", test.sendParams), func(t *testing.T) {
    50  			testParams(t, test.wantParams, func(c *jsonrpc2.Conn) error {
    51  				_, err := c.DispatchCall(context.Background(), "f", test.sendParams)
    52  				return err
    53  			})
    54  		})
    55  	}
    56  }
    57  
    58  func TestConn_Notify(t *testing.T) {
    59  	for _, test := range paramsTests {
    60  		t.Run(fmt.Sprintf("%s", test.sendParams), func(t *testing.T) {
    61  			testParams(t, test.wantParams, func(c *jsonrpc2.Conn) error {
    62  				return c.Notify(context.Background(), "f", test.sendParams)
    63  			})
    64  		})
    65  	}
    66  }
    67  
    68  func TestConn_DisconnectNotify(t *testing.T) {
    69  
    70  	t.Run("EOF", func(t *testing.T) {
    71  		connA, connB := net.Pipe()
    72  		c := jsonrpc2.NewConn(context.Background(), jsonrpc2.NewPlainObjectStream(connB), nil)
    73  		// By closing connA, connB receives io.EOF
    74  		if err := connA.Close(); err != nil {
    75  			t.Error(err)
    76  		}
    77  		assertDisconnect(t, c, connB)
    78  	})
    79  
    80  	t.Run("Close", func(t *testing.T) {
    81  		_, connB := net.Pipe()
    82  		c := jsonrpc2.NewConn(context.Background(), jsonrpc2.NewPlainObjectStream(connB), nil)
    83  		if err := c.Close(); err != nil {
    84  			t.Error(err)
    85  		}
    86  		assertDisconnect(t, c, connB)
    87  	})
    88  
    89  	t.Run("Close async", func(t *testing.T) {
    90  		done := make(chan struct{})
    91  		_, connB := net.Pipe()
    92  		c := jsonrpc2.NewConn(context.Background(), jsonrpc2.NewPlainObjectStream(connB), nil)
    93  		go func() {
    94  			if err := c.Close(); err != nil && err != jsonrpc2.ErrClosed {
    95  				t.Error(err)
    96  			}
    97  			close(done)
    98  		}()
    99  		assertDisconnect(t, c, connB)
   100  		<-done
   101  	})
   102  
   103  	t.Run("protocol error", func(t *testing.T) {
   104  		connA, connB := net.Pipe()
   105  		c := jsonrpc2.NewConn(
   106  			context.Background(),
   107  			jsonrpc2.NewPlainObjectStream(connB),
   108  			noopHandler{},
   109  			// Suppress log message. This connection receives an invalid JSON
   110  			// message that causes an error to be written to the logger. We
   111  			// don't want this expected error to appear in os.Stderr though when
   112  			// running tests in verbose mode or when other tests fail.
   113  			jsonrpc2.SetLogger(log.New(io.Discard, "", 0)),
   114  		)
   115  		connA.Write([]byte("invalid json"))
   116  		assertDisconnect(t, c, connB)
   117  	})
   118  }
   119  
   120  func TestConn_Close(t *testing.T) {
   121  	t.Run("waiting for response", func(t *testing.T) {
   122  		connA, connB := net.Pipe()
   123  		nodeA := jsonrpc2.NewConn(
   124  			context.Background(),
   125  			jsonrpc2.NewPlainObjectStream(connA), noopHandler{},
   126  		)
   127  		defer nodeA.Close()
   128  		nodeB := jsonrpc2.NewConn(
   129  			context.Background(),
   130  			jsonrpc2.NewPlainObjectStream(connB),
   131  			noopHandler{},
   132  		)
   133  		defer nodeB.Close()
   134  
   135  		ready := make(chan struct{})
   136  		done := make(chan struct{})
   137  		go func() {
   138  			close(ready)
   139  			err := nodeB.Call(context.Background(), "m", nil, nil)
   140  			if err != jsonrpc2.ErrClosed {
   141  				t.Errorf("got error %v, want %v", err, jsonrpc2.ErrClosed)
   142  			}
   143  			close(done)
   144  		}()
   145  		// Wait for the request to be sent before we close the connection.
   146  		<-ready
   147  		if err := nodeB.Close(); err != nil && err != jsonrpc2.ErrClosed {
   148  			t.Error(err)
   149  		}
   150  		assertDisconnect(t, nodeB, connB)
   151  		<-done
   152  	})
   153  }
   154  
   155  func testParams(t *testing.T, want *json.RawMessage, fn func(c *jsonrpc2.Conn) error) {
   156  	wg := &sync.WaitGroup{}
   157  	handler := handlerFunc(func(ctx context.Context, conn *jsonrpc2.Conn, r *jsonrpc2.Request) {
   158  		assertRawJSONMessage(t, r.Params, want)
   159  		wg.Done()
   160  	})
   161  
   162  	client, server := newClientServer(handler)
   163  	defer client.Close()
   164  	defer server.Close()
   165  
   166  	wg.Add(1)
   167  	if err := fn(client); err != nil {
   168  		t.Error(err)
   169  	}
   170  	wg.Wait()
   171  }
   172  
   173  func assertDisconnect(t *testing.T, c *jsonrpc2.Conn, conn io.Writer) {
   174  	select {
   175  	case <-c.DisconnectNotify():
   176  	case <-time.After(200 * time.Millisecond):
   177  		t.Error("no disconnect notification")
   178  		return
   179  	}
   180  	// Assert that conn is closed by trying to write to it.
   181  	_, got := conn.Write(nil)
   182  	want := io.ErrClosedPipe
   183  	if got != want {
   184  		t.Errorf("got %s, want %s", got, want)
   185  	}
   186  }
   187  
   188  func assertRawJSONMessage(t *testing.T, got *json.RawMessage, want *json.RawMessage) {
   189  	// Assert pointers.
   190  	if got == nil || want == nil {
   191  		if got != want {
   192  			t.Errorf("pointer: got %s, want %s", got, want)
   193  		}
   194  		return
   195  	}
   196  	{
   197  		// If pointers are not nil, then assert values.
   198  		got := string(*got)
   199  		want := string(*want)
   200  		if got != want {
   201  			t.Errorf("value: got %q, want %q", got, want)
   202  		}
   203  	}
   204  }
   205  
   206  func newClientServer(handler jsonrpc2.Handler) (client *jsonrpc2.Conn, server *jsonrpc2.Conn) {
   207  	ctx := context.Background()
   208  	connA, connB := net.Pipe()
   209  	client = jsonrpc2.NewConn(
   210  		ctx,
   211  		jsonrpc2.NewPlainObjectStream(connA),
   212  		noopHandler{},
   213  	)
   214  	server = jsonrpc2.NewConn(
   215  		ctx,
   216  		jsonrpc2.NewPlainObjectStream(connB),
   217  		handler,
   218  	)
   219  	return client, server
   220  }