go.uber.org/yarpc@v1.72.1/router_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package yarpc
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"sort"
    27  	"testing"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"go.uber.org/yarpc/api/middleware"
    32  	"go.uber.org/yarpc/api/middleware/middlewaretest"
    33  	"go.uber.org/yarpc/api/transport"
    34  	"go.uber.org/yarpc/api/transport/transporttest"
    35  )
    36  
    37  func TestMapRouter(t *testing.T) {
    38  	mockCtrl := gomock.NewController(t)
    39  	defer mockCtrl.Finish()
    40  
    41  	m := NewMapRouter("myservice")
    42  
    43  	foo := transporttest.NewMockUnaryHandler(mockCtrl)
    44  	bar := transporttest.NewMockUnaryHandler(mockCtrl)
    45  	bazJSON := transporttest.NewMockUnaryHandler(mockCtrl)
    46  	bazThrift := transporttest.NewMockUnaryHandler(mockCtrl)
    47  	m.Register([]transport.Procedure{
    48  		{
    49  			Name:        "foo",
    50  			HandlerSpec: transport.NewUnaryHandlerSpec(foo),
    51  		},
    52  		{
    53  			Name:        "bar",
    54  			Service:     "anotherservice",
    55  			HandlerSpec: transport.NewUnaryHandlerSpec(bar),
    56  		},
    57  		{
    58  			Name:        "baz",
    59  			Encoding:    "json",
    60  			HandlerSpec: transport.NewUnaryHandlerSpec(bazJSON),
    61  		},
    62  		{
    63  			Name:        "baz",
    64  			Encoding:    "thrift",
    65  			HandlerSpec: transport.NewUnaryHandlerSpec(bazThrift),
    66  		},
    67  	})
    68  
    69  	tests := []struct {
    70  		service, procedure, encoding string
    71  		want                         transport.UnaryHandler
    72  	}{
    73  		{"myservice", "foo", "", foo},
    74  		{"", "foo", "", foo},
    75  		{"anotherservice", "foo", "", nil},
    76  		{"", "bar", "", nil},
    77  		{"myservice", "bar", "", nil},
    78  		{"anotherservice", "bar", "", bar},
    79  		{"myservice", "baz", "thrift", bazThrift},
    80  		{"", "baz", "thrift", bazThrift},
    81  		{"myservice", "baz", "json", bazJSON},
    82  		{"", "baz", "json", bazJSON},
    83  		{"myservice", "baz", "proto", nil},
    84  		{"", "baz", "proto", nil},
    85  		{"unknownservice", "", "", nil},
    86  	}
    87  
    88  	for _, tt := range tests {
    89  		got, err := m.Choose(context.Background(), &transport.Request{
    90  			Service:   tt.service,
    91  			Procedure: tt.procedure,
    92  			Encoding:  transport.Encoding(tt.encoding),
    93  		})
    94  		if tt.want != nil {
    95  			assert.NoError(t, err,
    96  				"Choose(%q, %q, %q) failed", tt.service, tt.procedure, tt.encoding)
    97  			assert.True(t, tt.want == got.Unary(), // want == match, not deep equals
    98  				"Choose(%q, %q, %q) did not match", tt.service, tt.procedure, tt.encoding)
    99  		} else {
   100  			assert.Error(t, err, fmt.Sprintf("Expected error for (%q, %q, %q)", tt.service, tt.procedure, tt.encoding))
   101  		}
   102  	}
   103  }
   104  
   105  func TestMapRouter_Procedures(t *testing.T) {
   106  	mockCtrl := gomock.NewController(t)
   107  	defer mockCtrl.Finish()
   108  
   109  	m := NewMapRouter("myservice")
   110  
   111  	bar := transport.NewUnaryHandlerSpec(transporttest.NewMockUnaryHandler(mockCtrl))
   112  	foo := transport.NewUnaryHandlerSpec(transporttest.NewMockUnaryHandler(mockCtrl))
   113  	aww := transport.NewUnaryHandlerSpec(transporttest.NewMockUnaryHandler(mockCtrl))
   114  	m.Register([]transport.Procedure{
   115  		{
   116  			Name:        "bar",
   117  			Service:     "anotherservice",
   118  			HandlerSpec: bar,
   119  		},
   120  		{
   121  			Name:        "foo",
   122  			Encoding:    "json",
   123  			HandlerSpec: foo,
   124  		},
   125  		{
   126  			Name:        "aww",
   127  			Service:     "anotherservice",
   128  			HandlerSpec: aww,
   129  		},
   130  	})
   131  
   132  	expectedOrderedProcedures := []transport.Procedure{
   133  		{
   134  			Name:        "aww",
   135  			Service:     "anotherservice",
   136  			HandlerSpec: aww,
   137  		},
   138  		{
   139  			Name:        "bar",
   140  			Service:     "anotherservice",
   141  			HandlerSpec: bar,
   142  		},
   143  		{
   144  			Name:        "foo",
   145  			Encoding:    "json",
   146  			Service:     "myservice",
   147  			HandlerSpec: foo,
   148  		},
   149  	}
   150  
   151  	serviceProcedures := m.Procedures()
   152  
   153  	assert.Equal(t, expectedOrderedProcedures, serviceProcedures)
   154  }
   155  
   156  func TestEmptyProcedureRegistration(t *testing.T) {
   157  	m := NewMapRouter("test-service-name")
   158  
   159  	procedures := []transport.Procedure{
   160  		{
   161  			Name:    "",
   162  			Service: "test",
   163  		},
   164  	}
   165  
   166  	assert.Panics(t,
   167  		func() { m.Register(procedures) },
   168  		"expected router panic")
   169  }
   170  
   171  func TestAmbiguousProcedureRegistration(t *testing.T) {
   172  	m := NewMapRouter("test-service-name")
   173  
   174  	procedures := []transport.Procedure{
   175  		{
   176  			Name:    "foo",
   177  			Service: "test",
   178  		},
   179  		{
   180  			Name:    "foo",
   181  			Service: "test",
   182  		},
   183  	}
   184  
   185  	assert.Panics(t,
   186  		func() { m.Register(procedures) },
   187  		"expected router panic")
   188  }
   189  
   190  func TestEncodingBeforeWildcardProcedureRegistration(t *testing.T) {
   191  	m := NewMapRouter("test-service-name")
   192  
   193  	procedures := []transport.Procedure{
   194  		{
   195  			Name:     "foo",
   196  			Service:  "test",
   197  			Encoding: "json",
   198  		},
   199  		{
   200  			Name:    "foo",
   201  			Service: "test",
   202  		},
   203  	}
   204  
   205  	assert.Panics(t,
   206  		func() { m.Register(procedures) },
   207  		"expected router panic")
   208  }
   209  
   210  func TestWildcardBeforeEncodingProcedureRegistration(t *testing.T) {
   211  	m := NewMapRouter("test-service-name")
   212  
   213  	procedures := []transport.Procedure{
   214  		{
   215  			Name:    "foo",
   216  			Service: "test",
   217  		},
   218  		{
   219  			Name:     "foo",
   220  			Service:  "test",
   221  			Encoding: "json",
   222  		},
   223  	}
   224  
   225  	assert.Panics(t,
   226  		func() { m.Register(procedures) },
   227  		"expected router panic")
   228  }
   229  
   230  func TestIgnoreRouterWithMiddleware(t *testing.T) {
   231  	mockCtrl := gomock.NewController(t)
   232  	defer mockCtrl.Finish()
   233  
   234  	ctx := context.Background()
   235  	req := &transport.Request{}
   236  	expectedSpec := transport.HandlerSpec{}
   237  
   238  	routerMiddleware := middlewaretest.NewMockRouter(mockCtrl)
   239  	routerMiddleware.EXPECT().Choose(ctx, req, gomock.Any()).Times(1).Return(expectedSpec, nil)
   240  
   241  	router := middleware.ApplyRouteTable(NewMapRouter("service"), routerMiddleware)
   242  
   243  	actualSpec, err := router.Choose(ctx, req)
   244  
   245  	assert.Equal(t, expectedSpec, actualSpec, "handler spec returned from route table did not match")
   246  	assert.Nil(t, err)
   247  }
   248  
   249  func TestProcedureSort(t *testing.T) {
   250  	ps := sortableProcedures{
   251  		{
   252  			Service:  "moe",
   253  			Name:     "echo",
   254  			Encoding: "json",
   255  		},
   256  		{
   257  			Service: "moe",
   258  		},
   259  		{
   260  			Service: "larry",
   261  		},
   262  		{
   263  			Service: "moe",
   264  			Name:    "ping",
   265  		},
   266  		{
   267  			Service:  "moe",
   268  			Name:     "echo",
   269  			Encoding: "raw",
   270  		},
   271  		{
   272  			Service: "moe",
   273  			Name:    "poke",
   274  		},
   275  		{
   276  			Service: "curly",
   277  		},
   278  	}
   279  	sort.Sort(ps)
   280  	assert.Equal(t, sortableProcedures{
   281  		{
   282  			Service: "curly",
   283  		},
   284  		{
   285  			Service: "larry",
   286  		},
   287  		{
   288  			Service: "moe",
   289  		},
   290  		{
   291  			Service:  "moe",
   292  			Name:     "echo",
   293  			Encoding: "json",
   294  		},
   295  		{
   296  			Service:  "moe",
   297  			Name:     "echo",
   298  			Encoding: "raw",
   299  		},
   300  		{
   301  			Service: "moe",
   302  			Name:    "ping",
   303  		},
   304  		{
   305  			Service: "moe",
   306  			Name:    "poke",
   307  		},
   308  	}, ps, "should order procedures lexicographically on (service, procedure, encoding)")
   309  }
   310  
   311  func TestUnknownServiceName(t *testing.T) {
   312  	mockCtrl := gomock.NewController(t)
   313  	defer mockCtrl.Finish()
   314  
   315  	m := NewMapRouter("service1")
   316  	foo := transporttest.NewMockUnaryHandler(mockCtrl)
   317  
   318  	m.Register([]transport.Procedure{
   319  		{
   320  			Name:        "foo",
   321  			HandlerSpec: transport.NewUnaryHandlerSpec(foo),
   322  			Service:     "service2",
   323  			Encoding:    "json",
   324  		},
   325  	})
   326  
   327  	_, err := m.Choose(context.Background(), &transport.Request{
   328  		Service:   "wrongService",
   329  		Procedure: "foo",
   330  		Encoding:  "json",
   331  	})
   332  	assert.Contains(t, err.Error(), `unrecognized service name "wrongService", available services: "service1", "service2"`)
   333  }