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