...

Source file src/github.com/go-kit/kit/transport/http/jsonrpc/server_test.go

Documentation: github.com/go-kit/kit/transport/http/jsonrpc

     1  package jsonrpc_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/go-kit/kit/endpoint"
    16  	"github.com/go-kit/kit/transport/http/jsonrpc"
    17  )
    18  
    19  func addBody() io.Reader {
    20  	return body(`{"jsonrpc": "2.0", "method": "add", "params": [3, 2], "id": 1}`)
    21  }
    22  
    23  func body(in string) io.Reader {
    24  	return strings.NewReader(in)
    25  }
    26  
    27  func unmarshalResponse(body []byte) (resp jsonrpc.Response, err error) {
    28  	err = json.Unmarshal(body, &resp)
    29  	return
    30  }
    31  
    32  func expectErrorCode(t *testing.T, want int, body []byte) {
    33  	t.Helper()
    34  
    35  	r, err := unmarshalResponse(body)
    36  	if err != nil {
    37  		t.Fatalf("Can't decode response: %v (%s)", err, body)
    38  	}
    39  	if r.Error == nil {
    40  		t.Fatalf("Expected error on response. Got none: %s", body)
    41  	}
    42  	if have := r.Error.Code; want != have {
    43  		t.Fatalf("Unexpected error code. Want %d, have %d: %s", want, have, body)
    44  	}
    45  }
    46  
    47  func expectValidRequestID(t *testing.T, want int, body []byte) {
    48  	t.Helper()
    49  
    50  	r, err := unmarshalResponse(body)
    51  	if err != nil {
    52  		t.Fatalf("Can't decode response: %v (%s)", err, body)
    53  	}
    54  	have, err := r.ID.Int()
    55  	if err != nil {
    56  		t.Fatalf("Can't get requestID in response. err=%s, body=%s", err, body)
    57  	}
    58  	if want != have {
    59  		t.Fatalf("Request ID: want %d, have %d (%s)", want, have, body)
    60  	}
    61  }
    62  
    63  func expectNilRequestID(t *testing.T, body []byte) {
    64  	t.Helper()
    65  
    66  	r, err := unmarshalResponse(body)
    67  	if err != nil {
    68  		t.Fatalf("Can't decode response: %v (%s)", err, body)
    69  	}
    70  	if r.ID != nil {
    71  		t.Fatalf("Request ID: want nil, have %v", r.ID)
    72  	}
    73  }
    74  
    75  func nopDecoder(context.Context, json.RawMessage) (interface{}, error) { return struct{}{}, nil }
    76  func nopEncoder(context.Context, interface{}) (json.RawMessage, error) { return []byte("[]"), nil }
    77  
    78  type mockLogger struct {
    79  	Called   bool
    80  	LastArgs []interface{}
    81  }
    82  
    83  func (l *mockLogger) Log(keyvals ...interface{}) error {
    84  	l.Called = true
    85  	l.LastArgs = append(l.LastArgs, keyvals)
    86  	return nil
    87  }
    88  
    89  func TestServerBadDecode(t *testing.T) {
    90  	ecm := jsonrpc.EndpointCodecMap{
    91  		"add": jsonrpc.EndpointCodec{
    92  			Endpoint: endpoint.Nop,
    93  			Decode:   func(context.Context, json.RawMessage) (interface{}, error) { return struct{}{}, errors.New("oof") },
    94  			Encode:   nopEncoder,
    95  		},
    96  	}
    97  	logger := mockLogger{}
    98  	handler := jsonrpc.NewServer(ecm, jsonrpc.ServerErrorLogger(&logger))
    99  	server := httptest.NewServer(handler)
   100  	defer server.Close()
   101  	resp, _ := http.Post(server.URL, "application/json", addBody())
   102  	buf, _ := ioutil.ReadAll(resp.Body)
   103  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   104  		t.Errorf("want %d, have %d: %s", want, have, buf)
   105  	}
   106  	expectErrorCode(t, jsonrpc.InternalError, buf)
   107  	if !logger.Called {
   108  		t.Fatal("Expected logger to be called with error. Wasn't.")
   109  	}
   110  }
   111  
   112  func TestServerBadEndpoint(t *testing.T) {
   113  	ecm := jsonrpc.EndpointCodecMap{
   114  		"add": jsonrpc.EndpointCodec{
   115  			Endpoint: func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errors.New("oof") },
   116  			Decode:   nopDecoder,
   117  			Encode:   nopEncoder,
   118  		},
   119  	}
   120  	handler := jsonrpc.NewServer(ecm)
   121  	server := httptest.NewServer(handler)
   122  	defer server.Close()
   123  	resp, _ := http.Post(server.URL, "application/json", addBody())
   124  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   125  		t.Errorf("want %d, have %d", want, have)
   126  	}
   127  	buf, _ := ioutil.ReadAll(resp.Body)
   128  	expectErrorCode(t, jsonrpc.InternalError, buf)
   129  	expectValidRequestID(t, 1, buf)
   130  }
   131  
   132  func TestServerBadEncode(t *testing.T) {
   133  	ecm := jsonrpc.EndpointCodecMap{
   134  		"add": jsonrpc.EndpointCodec{
   135  			Endpoint: endpoint.Nop,
   136  			Decode:   nopDecoder,
   137  			Encode:   func(context.Context, interface{}) (json.RawMessage, error) { return []byte{}, errors.New("oof") },
   138  		},
   139  	}
   140  	handler := jsonrpc.NewServer(ecm)
   141  	server := httptest.NewServer(handler)
   142  	defer server.Close()
   143  	resp, _ := http.Post(server.URL, "application/json", addBody())
   144  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   145  		t.Errorf("want %d, have %d", want, have)
   146  	}
   147  	buf, _ := ioutil.ReadAll(resp.Body)
   148  	expectErrorCode(t, jsonrpc.InternalError, buf)
   149  	expectValidRequestID(t, 1, buf)
   150  }
   151  
   152  func TestServerErrorEncoder(t *testing.T) {
   153  	errTeapot := errors.New("teapot")
   154  	code := func(err error) int {
   155  		if errors.Is(err, errTeapot) {
   156  			return http.StatusTeapot
   157  		}
   158  		return http.StatusInternalServerError
   159  	}
   160  	ecm := jsonrpc.EndpointCodecMap{
   161  		"add": jsonrpc.EndpointCodec{
   162  			Endpoint: func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errTeapot },
   163  			Decode:   nopDecoder,
   164  			Encode:   nopEncoder,
   165  		},
   166  	}
   167  	handler := jsonrpc.NewServer(
   168  		ecm,
   169  		jsonrpc.ServerErrorEncoder(func(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(code(err)) }),
   170  	)
   171  	server := httptest.NewServer(handler)
   172  	defer server.Close()
   173  	resp, _ := http.Post(server.URL, "application/json", addBody())
   174  	if want, have := http.StatusTeapot, resp.StatusCode; want != have {
   175  		t.Errorf("want %d, have %d", want, have)
   176  	}
   177  }
   178  
   179  func TestCanRejectNonPostRequest(t *testing.T) {
   180  	ecm := jsonrpc.EndpointCodecMap{}
   181  	handler := jsonrpc.NewServer(ecm)
   182  	server := httptest.NewServer(handler)
   183  	defer server.Close()
   184  	resp, _ := http.Get(server.URL)
   185  	if want, have := http.StatusMethodNotAllowed, resp.StatusCode; want != have {
   186  		t.Errorf("want %d, have %d", want, have)
   187  	}
   188  }
   189  
   190  func TestCanRejectInvalidJSON(t *testing.T) {
   191  	ecm := jsonrpc.EndpointCodecMap{}
   192  	handler := jsonrpc.NewServer(ecm)
   193  	server := httptest.NewServer(handler)
   194  	defer server.Close()
   195  	resp, _ := http.Post(server.URL, "application/json", body("clearlynotjson"))
   196  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   197  		t.Errorf("want %d, have %d", want, have)
   198  	}
   199  	buf, _ := ioutil.ReadAll(resp.Body)
   200  	expectErrorCode(t, jsonrpc.ParseError, buf)
   201  	expectNilRequestID(t, buf)
   202  }
   203  
   204  func TestServerUnregisteredMethod(t *testing.T) {
   205  	ecm := jsonrpc.EndpointCodecMap{}
   206  	handler := jsonrpc.NewServer(ecm)
   207  	server := httptest.NewServer(handler)
   208  	defer server.Close()
   209  	resp, _ := http.Post(server.URL, "application/json", addBody())
   210  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   211  		t.Errorf("want %d, have %d", want, have)
   212  	}
   213  	buf, _ := ioutil.ReadAll(resp.Body)
   214  	expectErrorCode(t, jsonrpc.MethodNotFoundError, buf)
   215  }
   216  
   217  func TestServerHappyPath(t *testing.T) {
   218  	step, response := testServer(t)
   219  	step()
   220  	resp := <-response
   221  	defer resp.Body.Close() // nolint
   222  	buf, _ := ioutil.ReadAll(resp.Body)
   223  	if want, have := http.StatusOK, resp.StatusCode; want != have {
   224  		t.Errorf("want %d, have %d (%s)", want, have, buf)
   225  	}
   226  	r, err := unmarshalResponse(buf)
   227  	if err != nil {
   228  		t.Fatalf("Can't decode response. err=%s, body=%s", err, buf)
   229  	}
   230  	if r.JSONRPC != jsonrpc.Version {
   231  		t.Fatalf("JSONRPC Version: want=%s, got=%s", jsonrpc.Version, r.JSONRPC)
   232  	}
   233  	if r.Error != nil {
   234  		t.Fatalf("Unxpected error on response: %s", buf)
   235  	}
   236  }
   237  
   238  func TestMultipleServerBeforeCodec(t *testing.T) {
   239  	var done = make(chan struct{})
   240  	ecm := jsonrpc.EndpointCodecMap{
   241  		"add": jsonrpc.EndpointCodec{
   242  			Endpoint: endpoint.Nop,
   243  			Decode:   nopDecoder,
   244  			Encode:   nopEncoder,
   245  		},
   246  	}
   247  	handler := jsonrpc.NewServer(
   248  		ecm,
   249  		jsonrpc.ServerBeforeCodec(func(ctx context.Context, r *http.Request, req jsonrpc.Request) context.Context {
   250  			ctx = context.WithValue(ctx, "one", 1)
   251  
   252  			return ctx
   253  		}),
   254  		jsonrpc.ServerBeforeCodec(func(ctx context.Context, r *http.Request, req jsonrpc.Request) context.Context {
   255  			if _, ok := ctx.Value("one").(int); !ok {
   256  				t.Error("Value was not set properly when multiple ServerBeforeCodecs are used")
   257  			}
   258  
   259  			close(done)
   260  			return ctx
   261  		}),
   262  	)
   263  	server := httptest.NewServer(handler)
   264  	defer server.Close()
   265  	http.Post(server.URL, "application/json", addBody()) // nolint
   266  
   267  	select {
   268  	case <-done:
   269  	case <-time.After(time.Second):
   270  		t.Fatal("timeout waiting for finalizer")
   271  	}
   272  }
   273  
   274  func TestMultipleServerBefore(t *testing.T) {
   275  	var done = make(chan struct{})
   276  	ecm := jsonrpc.EndpointCodecMap{
   277  		"add": jsonrpc.EndpointCodec{
   278  			Endpoint: endpoint.Nop,
   279  			Decode:   nopDecoder,
   280  			Encode:   nopEncoder,
   281  		},
   282  	}
   283  	handler := jsonrpc.NewServer(
   284  		ecm,
   285  		jsonrpc.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {
   286  			ctx = context.WithValue(ctx, "one", 1)
   287  
   288  			return ctx
   289  		}),
   290  		jsonrpc.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {
   291  			if _, ok := ctx.Value("one").(int); !ok {
   292  				t.Error("Value was not set properly when multiple ServerBefores are used")
   293  			}
   294  
   295  			close(done)
   296  			return ctx
   297  		}),
   298  	)
   299  	server := httptest.NewServer(handler)
   300  	defer server.Close()
   301  	http.Post(server.URL, "application/json", addBody()) // nolint
   302  
   303  	select {
   304  	case <-done:
   305  	case <-time.After(time.Second):
   306  		t.Fatal("timeout waiting for finalizer")
   307  	}
   308  }
   309  
   310  func TestMultipleServerAfter(t *testing.T) {
   311  	var done = make(chan struct{})
   312  	ecm := jsonrpc.EndpointCodecMap{
   313  		"add": jsonrpc.EndpointCodec{
   314  			Endpoint: endpoint.Nop,
   315  			Decode:   nopDecoder,
   316  			Encode:   nopEncoder,
   317  		},
   318  	}
   319  	handler := jsonrpc.NewServer(
   320  		ecm,
   321  		jsonrpc.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {
   322  			ctx = context.WithValue(ctx, "one", 1)
   323  
   324  			return ctx
   325  		}),
   326  		jsonrpc.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {
   327  			if _, ok := ctx.Value("one").(int); !ok {
   328  				t.Error("Value was not set properly when multiple ServerAfters are used")
   329  			}
   330  
   331  			close(done)
   332  			return ctx
   333  		}),
   334  	)
   335  	server := httptest.NewServer(handler)
   336  	defer server.Close()
   337  	http.Post(server.URL, "application/json", addBody()) // nolint
   338  
   339  	select {
   340  	case <-done:
   341  	case <-time.After(time.Second):
   342  		t.Fatal("timeout waiting for finalizer")
   343  	}
   344  }
   345  
   346  func TestCanFinalize(t *testing.T) {
   347  	var done = make(chan struct{})
   348  	var finalizerCalled bool
   349  	ecm := jsonrpc.EndpointCodecMap{
   350  		"add": jsonrpc.EndpointCodec{
   351  			Endpoint: endpoint.Nop,
   352  			Decode:   nopDecoder,
   353  			Encode:   nopEncoder,
   354  		},
   355  	}
   356  	handler := jsonrpc.NewServer(
   357  		ecm,
   358  		jsonrpc.ServerFinalizer(func(ctx context.Context, code int, req *http.Request) {
   359  			finalizerCalled = true
   360  			close(done)
   361  		}),
   362  	)
   363  	server := httptest.NewServer(handler)
   364  	defer server.Close()
   365  	http.Post(server.URL, "application/json", addBody()) // nolint
   366  
   367  	select {
   368  	case <-done:
   369  	case <-time.After(time.Second):
   370  		t.Fatal("timeout waiting for finalizer")
   371  	}
   372  
   373  	if !finalizerCalled {
   374  		t.Fatal("Finalizer was not called.")
   375  	}
   376  }
   377  
   378  func testServer(t *testing.T) (step func(), resp <-chan *http.Response) {
   379  	var (
   380  		stepch   = make(chan bool)
   381  		endpoint = func(ctx context.Context, request interface{}) (response interface{}, err error) {
   382  			<-stepch
   383  			return struct{}{}, nil
   384  		}
   385  		response = make(chan *http.Response)
   386  		ecm      = jsonrpc.EndpointCodecMap{
   387  			"add": jsonrpc.EndpointCodec{
   388  				Endpoint: endpoint,
   389  				Decode:   nopDecoder,
   390  				Encode:   nopEncoder,
   391  			},
   392  		}
   393  		handler = jsonrpc.NewServer(ecm)
   394  	)
   395  	go func() {
   396  		server := httptest.NewServer(handler)
   397  		defer server.Close()
   398  		rb := strings.NewReader(`{"jsonrpc": "2.0", "method": "add", "params": [3, 2], "id": 1}`)
   399  		resp, err := http.Post(server.URL, "application/json", rb)
   400  		if err != nil {
   401  			t.Error(err)
   402  			return
   403  		}
   404  		response <- resp
   405  	}()
   406  	return func() { stepch <- true }, response
   407  }
   408  

View as plain text