blob: fd0285a713ec2426919c8045d94a4594bb5232a4 [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the"github.com/stretchr/testify/assert" "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package afrouter
18
19import (
20 "fmt"
21 "github.com/golang/protobuf/proto"
Scott Bakere7144bc2019-10-01 14:16:47 -070022 common_pb "github.com/opencord/voltha-protos/go/common"
23 voltha_pb "github.com/opencord/voltha-protos/go/voltha"
24 "github.com/stretchr/testify/assert"
25 "google.golang.org/grpc"
26 "testing"
27)
28
Scott Bakere7144bc2019-10-01 14:16:47 -070029// Build an method router configuration
30func MakeMethodTestConfig(numBackends int, numConnections int) (*RouteConfig, *RouterConfig) {
31
32 var backends []BackendConfig
33 for backendIndex := 0; backendIndex < numBackends; backendIndex++ {
34 var connections []ConnectionConfig
35 for connectionIndex := 0; connectionIndex < numConnections; connectionIndex++ {
36 connectionConfig := ConnectionConfig{
37 Name: fmt.Sprintf("rw_vcore%d%d", backendIndex, connectionIndex+1),
38 Addr: "foo",
39 Port: "123",
40 }
41 connections = append(connections, connectionConfig)
42 }
43
44 backendConfig := BackendConfig{
45 Name: fmt.Sprintf("rw_vcore%d", backendIndex),
46 Type: BackendSingleServer,
47 Connections: connections,
48 }
49
50 backends = append(backends, backendConfig)
51 }
52
53 backendClusterConfig := BackendClusterConfig{
54 Name: "vcore",
55 Backends: backends,
56 }
57
58 routeConfig := RouteConfig{
59 Name: "dev_manager",
60 Type: RouteTypeRpcAffinityMessage,
61 Association: AssociationRoundRobin,
62 BackendCluster: "vcore",
63 backendCluster: &backendClusterConfig,
64 RouteField: "id",
65 Methods: []string{"CreateDevice", "EnableDevice"},
66 NbBindingMethods: []string{"CreateDevice"},
67 }
68
69 routerConfig := RouterConfig{
70 Name: "vcore",
71 ProtoService: "VolthaService",
72 ProtoPackage: "voltha",
73 Routes: []RouteConfig{routeConfig},
Scott Baker4989fe92019-10-09 17:03:06 -070074 ProtoFile: TEST_PROTOFILE,
Scott Bakere7144bc2019-10-01 14:16:47 -070075 }
76 return &routeConfig, &routerConfig
77}
78
79// Route() requires an open connection, so pretend we have one.
80func PretendMethodOpenConnection(router Router, clusterName string, backendIndex int, connectionName string) {
81 cluster := router.FindBackendCluster(clusterName)
82
83 // Route Method expects an open connection
84 conn := cluster.backends[backendIndex].connections[connectionName]
85 cluster.backends[backendIndex].openConns[conn] = &grpc.ClientConn{}
86}
87
88// Common setup to run before each unit test
89func MethodTestSetup() {
90 // reset globals that need to be clean for each unit test
91
92 clusters = make(map[string]*cluster)
93 allRouters = make(map[string]Router)
94}
95
96// Test creation of a new AffinityRouter, and the Service(), Name(), FindBackendCluster(), and
97// methods.
98func TestMethodRouterInit(t *testing.T) {
99 MethodTestSetup()
100
101 _, routerConfig := MakeMethodTestConfig(1, 1)
102
103 router, err := newMethodRouter(routerConfig)
104
105 assert.NotNil(t, router)
106 assert.Nil(t, err)
107
108 assert.Equal(t, router.Service(), "VolthaService")
109 assert.Equal(t, router.Name(), "vcore")
110
111 cluster, err := router.BackendCluster("EnableDevice", NoMeta)
112 assert.Equal(t, cluster, clusters["vcore"])
113 assert.Nil(t, err)
114
115 assert.Equal(t, router.FindBackendCluster("vcore"), clusters["vcore"])
116}
117
118// Passing an invalid meta should return an error
119func TestMethodRouterBackendClusterInvalidMeta(t *testing.T) {
120 MethodTestSetup()
121
122 _, routerConfig := MakeMethodTestConfig(1, 1)
123
124 router, err := newMethodRouter(routerConfig)
125
126 assert.NotNil(t, router)
127 assert.Nil(t, err)
128
129 cluster, err := router.BackendCluster("EnableDevice", "wrongmeta")
130 assert.EqualError(t, err, "No backend cluster exists for method 'EnableDevice' using meta key 'wrongmeta'")
131 assert.Nil(t, cluster)
132}
133
134// Passing an invalid method name should return an error
135func TestMethodRouterBackendClusterInvalidMethod(t *testing.T) {
136 MethodTestSetup()
137
138 _, routerConfig := MakeMethodTestConfig(1, 1)
139
140 router, err := newMethodRouter(routerConfig)
141
142 assert.NotNil(t, router)
143 assert.Nil(t, err)
144
145 cluster, err := router.BackendCluster("WrongMethod", NoMeta)
146 assert.EqualError(t, err, "No backend cluster exists for method 'WrongMethod' using meta key 'nometa'")
147 assert.Nil(t, cluster)
148}
149
150// Search for a backend cluster that doesn't exist
151func TestMethodRouterFindBackendClusterNoExist(t *testing.T) {
152 MethodTestSetup()
153
154 _, routerConfig := MakeMethodTestConfig(1, 1)
155
156 router, err := newMethodRouter(routerConfig)
157
158 assert.NotNil(t, router)
159 assert.Nil(t, err)
160
161 assert.Nil(t, router.FindBackendCluster("wrong"))
162}
163
164// MethodRouter's route will cause another router's route, in this case AffinityRouter.
165func TestMethodRoute(t *testing.T) {
166 MethodTestSetup()
167
168 _, routerConfig := MakeMethodTestConfig(1, 1)
169
170 router, err := newMethodRouter(routerConfig)
171 assert.Nil(t, err)
172
173 PretendMethodOpenConnection(router, "vcore", 0, "rw_vcore01")
174
175 idMessage := &common_pb.ID{Id: "1234"}
176
177 idData, err := proto.Marshal(idMessage)
178 assert.Nil(t, err)
179
180 sel := &requestFrame{payload: idData,
181 err: nil,
182 metaKey: NoMeta,
183 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
184
185 backend, connection := router.Route(sel)
186
187 assert.Nil(t, sel.err)
188 assert.NotNil(t, backend)
189 assert.Equal(t, "rw_vcore0", backend.name)
190 assert.Nil(t, connection)
191
192 // Since we only have one backend, calling Route a second time should return the same one
193
194 backend, connection = router.Route(sel)
195
196 assert.Nil(t, sel.err)
197 assert.NotNil(t, backend)
198 assert.Equal(t, "rw_vcore0", backend.name)
199 assert.Nil(t, connection)
200}
201
202// Try to route to a nonexistent method
203func TestMethodRouteNonexistent(t *testing.T) {
204 MethodTestSetup()
205
206 _, routerConfig := MakeMethodTestConfig(1, 1)
207
208 router, err := newMethodRouter(routerConfig)
209 assert.Nil(t, err)
210
211 idMessage := &common_pb.ID{Id: "1234"}
212
213 idData, err := proto.Marshal(idMessage)
214 assert.Nil(t, err)
215
216 sel := &requestFrame{payload: idData,
217 err: nil,
218 metaKey: NoMeta,
219 methodInfo: newMethodDetails("/voltha.VolthaService/NonexistentMethod")}
220
221 backend, connection := router.Route(sel)
222
223 assert.Nil(t, backend)
224 assert.Nil(t, connection)
225
226 assert.EqualError(t, sel.err, "MethodRouter.Route unable to resolve meta nometa, method NonexistentMethod")
227}
228
229// Try to route to a nonexistent meta key
230func TestMethodRouteWrongMeta(t *testing.T) {
231 MethodTestSetup()
232
233 _, routerConfig := MakeMethodTestConfig(1, 1)
234
235 router, err := newMethodRouter(routerConfig)
236 assert.Nil(t, err)
237
238 idMessage := &common_pb.ID{Id: "1234"}
239
240 idData, err := proto.Marshal(idMessage)
241 assert.Nil(t, err)
242
243 sel := &requestFrame{payload: idData,
244 err: nil,
245 metaKey: "wrongkey",
246 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
247
248 backend, connection := router.Route(sel)
249
250 assert.Nil(t, backend)
251 assert.Nil(t, connection)
252
253 assert.EqualError(t, sel.err, "MethodRouter.Route unable to resolve meta wrongkey, method EnableDevice")
254}
255
256// Try to route to a the wrong type of key
257func TestMethodRouteWrongFrame(t *testing.T) {
258 MethodTestSetup()
259
260 _, routerConfig := MakeMethodTestConfig(1, 1)
261
262 router, err := newMethodRouter(routerConfig)
263 assert.Nil(t, err)
264
265 idMessage := &voltha_pb.Device{Id: "1234"}
266 idData, err := proto.Marshal(idMessage)
267 assert.Nil(t, err)
268
269 // Note that sel.backend must be set. As this is a response, it must
270 // have come from a backend and that backend must be known.
271
272 sel := &responseFrame{payload: idData,
273 metaKey: NoMeta,
274 backend: router.FindBackendCluster("vcore").backends[0],
275 method: "CreateDevice",
276 }
277
278 // Note: Does not return any error, but does print an error message. Returns nil.
279
280 backend, connection := router.Route(sel)
281
282 assert.Nil(t, backend)
283 assert.Nil(t, connection)
284}
285
286// MethodRouter calls another Router's ReplyHandler, in this case AffinityRouter
287func TestMethodRouteReply(t *testing.T) {
288 MethodTestSetup()
289
290 _, routerConfig := MakeMethodTestConfig(1, 1)
291
292 router, err := newRouter(routerConfig)
293 assert.Nil(t, err)
294
295 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
296
297 PretendMethodOpenConnection(router, "vcore", 0, "rw_vcore01")
298
299 idMessage := &voltha_pb.Device{Id: "1234"}
300 idData, err := proto.Marshal(idMessage)
301 assert.Nil(t, err)
302
303 // Note that sel.backend must be set. As this is a response, it must
304 // have come from a backend and that backend must be known.
305
306 sel := &responseFrame{payload: idData,
307 metaKey: NoMeta,
308 backend: router.FindBackendCluster("vcore").backends[0],
309 method: "CreateDevice",
310 }
311
312 // affinity should be unset as we have not routed yet
313 assert.Empty(t, aRouter.affinity)
314
315 err = router.ReplyHandler(sel)
316 assert.Nil(t, err)
317
318 // affinity should now be set
319 assert.NotEmpty(t, aRouter.affinity)
320 assert.Equal(t, router.FindBackendCluster("vcore").backends[0], aRouter.affinity["1234"])
321}
322
323// Call ReplyHandler with the wrong type of frame
324func TestMethodRouteReplyWrongFrame(t *testing.T) {
325 MethodTestSetup()
326
327 _, routerConfig := MakeMethodTestConfig(1, 1)
328
329 router, err := newRouter(routerConfig)
330 assert.Nil(t, err)
331
332 PretendMethodOpenConnection(router, "vcore", 0, "rw_vcore01")
333
334 idMessage := &common_pb.ID{Id: "1234"}
335
336 idData, err := proto.Marshal(idMessage)
337 assert.Nil(t, err)
338
339 sel := &requestFrame{payload: idData,
340 err: nil,
341 metaKey: "wrongkey",
342 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
343
344 err = router.ReplyHandler(sel)
345 assert.EqualError(t, err, "MethodRouter.ReplyHandler called with non-reponseFrame")
346}
347
348// Call ReplyHandler with an invalid method name
349func TestMethodRouteReplyWrongMethod(t *testing.T) {
350 MethodTestSetup()
351
352 _, routerConfig := MakeMethodTestConfig(1, 1)
353
354 router, err := newRouter(routerConfig)
355 assert.Nil(t, err)
356
357 PretendMethodOpenConnection(router, "vcore", 0, "rw_vcore01")
358
359 idMessage := &common_pb.ID{Id: "1234"}
360
361 idData, err := proto.Marshal(idMessage)
362 assert.Nil(t, err)
363
364 sel := &requestFrame{payload: idData,
365 err: nil,
366 metaKey: "wrongkey",
367 methodInfo: newMethodDetails("/voltha.VolthaService/WrongMethod")}
368
369 err = router.ReplyHandler(sel)
370 assert.EqualError(t, err, "MethodRouter.ReplyHandler called with non-reponseFrame")
371}
372
373func TestMethodIsMethodStreaming(t *testing.T) {
374 MethodTestSetup()
375
376 _, routerConfig := MakeMethodTestConfig(1, 1)
377
378 router, err := newRouter(routerConfig)
379 assert.Nil(t, err)
380
381 request, response := router.IsStreaming("EnableDevice")
382 assert.False(t, request)
383 assert.False(t, response)
384}