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 }