github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/devicetwin/process_test.go (about) 1 /* 2 Copyright 2019 The KubeEdge Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package devicetwin 18 19 import ( 20 "encoding/base64" 21 "encoding/json" 22 "errors" 23 "reflect" 24 "testing" 25 26 "github.com/astaxie/beego/orm" 27 "github.com/golang/mock/gomock" 28 29 beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 30 "github.com/kubeedge/beehive/pkg/core/model" 31 "github.com/kubeedge/kubeedge/edge/mocks/beego" 32 "github.com/kubeedge/kubeedge/edge/pkg/common/dbm" 33 "github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtclient" 34 "github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcommon" 35 "github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcontext" 36 "github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtmodule" 37 "github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dttype" 38 ) 39 40 // createFakeDevice() is function to create fake device. 41 func createFakeDevice() *[]dtclient.Device { 42 fakeDevice := new([]dtclient.Device) 43 fakeDeviceArray := make([]dtclient.Device, 1) 44 fakeDeviceArray[0] = dtclient.Device{ID: "Test"} 45 fakeDevice = &fakeDeviceArray 46 return fakeDevice 47 } 48 49 // createFakeAttribute() is function to create fake device attribute. 50 func createFakeDeviceAttribute() *[]dtclient.DeviceAttr { 51 fakeDeviceAttr := new([]dtclient.DeviceAttr) 52 fakeDeviceAttrArray := make([]dtclient.DeviceAttr, 1) 53 fakeDeviceAttrArray[0] = dtclient.DeviceAttr{DeviceID: "Test"} 54 fakeDeviceAttr = &fakeDeviceAttrArray 55 return fakeDeviceAttr 56 } 57 58 // createFakeDeviceTwin() is function to create fake devicetwin. 59 func createFakeDeviceTwin() *[]dtclient.DeviceTwin { 60 fakeDeviceTwin := new([]dtclient.DeviceTwin) 61 fakeDeviceTwinArray := make([]dtclient.DeviceTwin, 1) 62 fakeDeviceTwinArray[0] = dtclient.DeviceTwin{DeviceID: "Test"} 63 fakeDeviceTwin = &fakeDeviceTwinArray 64 return fakeDeviceTwin 65 } 66 67 //TestRegisterDTModule is function to test RegisterDTmodule(). 68 func TestRegisterDTModule(t *testing.T) { 69 beehiveContext.InitContext(beehiveContext.MsgCtxTypeChannel) 70 dtContexts, _ := dtcontext.InitDTContext() 71 var moduleRegistered bool 72 dtc := &DeviceTwin{ 73 HeartBeatToModule: make(map[string]chan interface{}), 74 DTContexts: dtContexts, 75 DTModules: make(map[string]dtmodule.DTModule), 76 } 77 tests := []struct { 78 name string 79 moduleName string 80 }{ 81 { 82 name: "MemModule", 83 moduleName: "MemModule", 84 }, 85 { 86 name: "TwinModule", 87 moduleName: "TwinModule", 88 }, 89 { 90 name: "CommModule", 91 moduleName: "CommModule", 92 }, 93 { 94 name: "DeviceModule", 95 moduleName: "DeviceModule", 96 }, 97 } 98 for _, tt := range tests { 99 t.Run(tt.name, func(t *testing.T) { 100 dtc.RegisterDTModule(tt.moduleName) 101 moduleRegistered = false 102 for _, name := range dtc.DTModules { 103 if name.Name == tt.moduleName { 104 moduleRegistered = true 105 break 106 } 107 } 108 if moduleRegistered == false { 109 t.Errorf("RegisterDTModule failed to register the module %v", tt.moduleName) 110 } 111 }) 112 } 113 } 114 115 //TestDTController_distributeMsg is function to test distributeMsg(). 116 func TestDTController_distributeMsg(t *testing.T) { 117 beehiveContext.InitContext(beehiveContext.MsgCtxTypeChannel) 118 dtContexts, _ := dtcontext.InitDTContext() 119 dtc := &DeviceTwin{ 120 HeartBeatToModule: make(map[string]chan interface{}), 121 DTModules: make(map[string]dtmodule.DTModule), 122 DTContexts: dtContexts, 123 } 124 125 payload := dttype.MembershipUpdate{ 126 AddDevices: []dttype.Device{ 127 { 128 ID: "DeviceA", 129 Name: "Router", 130 State: "unknown", 131 }, 132 }, 133 } 134 var msg = &model.Message{ 135 Header: model.MessageHeader{ 136 ParentID: DeviceTwinModuleName, 137 }, 138 Content: payload, 139 Router: model.MessageRoute{ 140 Source: "edgemgr", 141 Resource: "membership/detail", 142 }, 143 } 144 tests := []struct { 145 name string 146 message interface{} 147 wantErr error 148 }{ 149 { 150 //Failure Case 151 name: "distributeMsgTest-NilMessage", 152 message: "", 153 wantErr: errors.New("Distribute message, msg is nil"), 154 }, 155 { 156 //Failure Case 157 name: "distributeMsgTest-ClassifyMsgFail", 158 message: model.Message{ 159 Router: model.MessageRoute{ 160 Source: "bus", 161 Resource: "membership/detail", 162 }, 163 }, 164 wantErr: errors.New("Not found action"), 165 }, 166 { 167 //Failure Case 168 name: "distributeMsgTest-ActualMessage-NoChanel", 169 message: *msg, 170 wantErr: errors.New("Not found chan to communicate"), 171 }, 172 } 173 for _, tt := range tests { 174 t.Run(tt.name, func(t *testing.T) { 175 if err := dtc.distributeMsg(tt.message); !reflect.DeepEqual(err, tt.wantErr) { 176 t.Errorf("DTController.distributeMsg() error = %v, wantError %v", err, tt.wantErr) 177 } 178 }) 179 } 180 181 //Successful Case 182 dh := make(chan interface{}, 1) 183 ch := make(chan interface{}, 1) 184 mh := make(chan interface{}, 1) 185 deh := make(chan interface{}, 1) 186 th := make(chan interface{}, 1) 187 dtc.DTContexts.CommChan["DeviceStateUpdate"] = dh 188 dtc.DTContexts.CommChan["CommModule"] = ch 189 dtc.DTContexts.CommChan["MemModule"] = mh 190 dtc.DTContexts.CommChan["DeviceModule"] = deh 191 dtc.DTContexts.CommChan["TwinModule"] = th 192 name := "distributeMsgTest-ActualMessage-Success" 193 t.Run(name, func(t *testing.T) { 194 if err := dtc.distributeMsg(*msg); !reflect.DeepEqual(err, nil) { 195 t.Errorf("DTController.distributeMsg() error = %v, wantError %v", err, nil) 196 } 197 }) 198 } 199 200 //TestSyncSqlite is function to test SyncSqlite(). 201 func TestSyncSqlite(t *testing.T) { 202 // ormerMock is mocked Ormer implementation. 203 var ormerMock *beego.MockOrmer 204 // querySeterMock is mocked QuerySeter implementation. 205 var querySeterMock *beego.MockQuerySeter 206 beehiveContext.InitContext(beehiveContext.MsgCtxTypeChannel) 207 mockCtrl := gomock.NewController(t) 208 defer mockCtrl.Finish() 209 ormerMock = beego.NewMockOrmer(mockCtrl) 210 querySeterMock = beego.NewMockQuerySeter(mockCtrl) 211 dbm.DBAccess = ormerMock 212 213 dtContexts, _ := dtcontext.InitDTContext() 214 // fakeDevice is used to set the argument of All function 215 fakeDevice := createFakeDevice() 216 // fakeDeviceAttr is used to set the argument of All function 217 fakeDeviceAttr := createFakeDeviceAttribute() 218 // fakeDeviceTwin is used to set the argument of All function 219 fakeDeviceTwin := createFakeDeviceTwin() 220 tests := []struct { 221 name string 222 context *dtcontext.DTContext 223 wantErr error 224 filterReturn orm.QuerySeter 225 queryTableReturn orm.QuerySeter 226 allReturnIntDevice int64 227 allReturnErrDevice error 228 allReturnIntAttribute int64 229 allReturnErrAttribute error 230 allReturnIntTwin int64 231 allReturnErrTwin error 232 queryTableMockTimes int 233 filterMockTimes int 234 deviceMockTimes int 235 attributeMockTimes int 236 twinMockTimes int 237 }{ 238 { 239 //Failure Case 240 name: "SyncSqliteTest-QuerySqliteFailed", 241 context: dtContexts, 242 wantErr: errors.New("Query sqlite failed while syncing sqlite"), 243 filterReturn: querySeterMock, 244 queryTableReturn: querySeterMock, 245 allReturnIntDevice: int64(0), 246 allReturnErrDevice: errors.New("Query sqlite failed while syncing sqlite"), 247 allReturnIntAttribute: int64(0), 248 allReturnErrAttribute: nil, 249 allReturnIntTwin: int64(0), 250 allReturnErrTwin: nil, 251 queryTableMockTimes: int(1), 252 filterMockTimes: int(0), 253 deviceMockTimes: int(1), 254 attributeMockTimes: int(0), 255 twinMockTimes: int(0), 256 }, 257 { 258 //Success Case 259 name: "SyncSqliteTest-QuerySqliteSuccess", 260 context: dtContexts, 261 wantErr: nil, 262 filterReturn: querySeterMock, 263 queryTableReturn: querySeterMock, 264 allReturnIntDevice: int64(1), 265 allReturnErrDevice: nil, 266 allReturnIntAttribute: int64(1), 267 allReturnErrAttribute: nil, 268 allReturnIntTwin: int64(1), 269 allReturnErrTwin: nil, 270 queryTableMockTimes: int(4), 271 filterMockTimes: int(3), 272 deviceMockTimes: int(2), 273 attributeMockTimes: int(1), 274 twinMockTimes: int(1), 275 }, 276 } 277 for _, test := range tests { 278 t.Run(test.name, func(t *testing.T) { 279 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDevice).Return(test.allReturnIntDevice, test.allReturnErrDevice).Times(test.deviceMockTimes) 280 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceAttr).Return(test.allReturnIntAttribute, test.allReturnErrAttribute).Times(test.attributeMockTimes) 281 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceTwin).Return(test.allReturnIntTwin, test.allReturnErrTwin).Times(test.twinMockTimes) 282 querySeterMock.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(test.filterMockTimes) 283 ormerMock.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(test.queryTableMockTimes) 284 if err := SyncSqlite(test.context); !reflect.DeepEqual(err, test.wantErr) { 285 t.Errorf("SyncSqlite() error = %v, wantError %v", err, test.wantErr) 286 } 287 }) 288 } 289 } 290 291 //TestSyncDeviceFromSqlite is function to test SyncDeviceFromSqlite(). 292 func TestSyncDeviceFromSqlite(t *testing.T) { 293 // ormerMock is mocked Ormer implementation. 294 var ormerMock *beego.MockOrmer 295 // querySeterMock is mocked QuerySeter implementation. 296 var querySeterMock *beego.MockQuerySeter 297 298 beehiveContext.InitContext(beehiveContext.MsgCtxTypeChannel) 299 mockCtrl := gomock.NewController(t) 300 defer mockCtrl.Finish() 301 ormerMock = beego.NewMockOrmer(mockCtrl) 302 querySeterMock = beego.NewMockQuerySeter(mockCtrl) 303 dbm.DBAccess = ormerMock 304 305 dtContext, _ := dtcontext.InitDTContext() 306 // fakeDevice is used to set the argument of All function 307 fakeDevice := createFakeDevice() 308 // fakeDeviceAttr is used to set the argument of All function 309 fakeDeviceAttr := createFakeDeviceAttribute() 310 // fakeDeviceTwin is used to set the argument of All function 311 fakeDeviceTwin := createFakeDeviceTwin() 312 tests := []struct { 313 name string 314 context *dtcontext.DTContext 315 deviceID string 316 wantErr error 317 filterReturn orm.QuerySeter 318 queryTableReturn orm.QuerySeter 319 allReturnIntDevice int64 320 allReturnErrDevice error 321 allReturnIntAttribute int64 322 allReturnErrAttribute error 323 allReturnIntTwin int64 324 allReturnErrTwin error 325 queryTableMockTimes int 326 filterMockTimes int 327 deviceMockTimes int 328 attributeMockTimes int 329 twinMockTimes int 330 }{ 331 { 332 //Failure Case 333 name: "TestSyncDeviceFromSqlite-QueryDeviceFailure", 334 context: dtContext, 335 deviceID: "DeviceA", 336 wantErr: errors.New("Query Device Failed"), 337 filterReturn: querySeterMock, 338 queryTableReturn: querySeterMock, 339 allReturnIntDevice: int64(0), 340 allReturnErrDevice: errors.New("Query Device Failed"), 341 allReturnIntAttribute: int64(0), 342 allReturnErrAttribute: nil, 343 allReturnIntTwin: int64(0), 344 allReturnErrTwin: nil, 345 queryTableMockTimes: int(1), 346 filterMockTimes: int(1), 347 deviceMockTimes: int(1), 348 attributeMockTimes: int(0), 349 twinMockTimes: int(0), 350 }, 351 { 352 //Failure Case 353 name: "TestSyncDeviceFromSqlite-QueryDeviceAttributeFailed", 354 context: dtContext, 355 deviceID: "DeviceB", 356 wantErr: errors.New("query device attr failed"), 357 filterReturn: querySeterMock, 358 queryTableReturn: querySeterMock, 359 allReturnIntDevice: int64(1), 360 allReturnErrDevice: nil, 361 allReturnIntAttribute: int64(0), 362 allReturnErrAttribute: errors.New("query device attr failed"), 363 allReturnIntTwin: int64(0), 364 allReturnErrTwin: nil, 365 queryTableMockTimes: int(2), 366 filterMockTimes: int(2), 367 deviceMockTimes: int(1), 368 attributeMockTimes: int(1), 369 twinMockTimes: int(0), 370 }, 371 { 372 //Failure Case 373 name: "TestSyncDeviceFromSqlite-QueryDeviceTwinFailed", 374 context: dtContext, 375 deviceID: "DeviceC", 376 wantErr: errors.New("query device twin failed"), 377 filterReturn: querySeterMock, 378 queryTableReturn: querySeterMock, 379 allReturnIntDevice: int64(1), 380 allReturnErrDevice: nil, 381 allReturnIntAttribute: int64(1), 382 allReturnErrAttribute: nil, 383 allReturnIntTwin: int64(0), 384 allReturnErrTwin: errors.New("query device twin failed"), 385 queryTableMockTimes: int(3), 386 filterMockTimes: int(3), 387 deviceMockTimes: int(1), 388 attributeMockTimes: int(1), 389 twinMockTimes: int(1), 390 }, 391 { 392 //Success Case 393 name: "TestSyncDeviceFromSqlite-SuccessCase", 394 context: dtContext, 395 deviceID: "DeviceD", 396 wantErr: nil, 397 filterReturn: querySeterMock, 398 queryTableReturn: querySeterMock, 399 allReturnIntDevice: int64(1), 400 allReturnErrDevice: nil, 401 allReturnIntAttribute: int64(1), 402 allReturnErrAttribute: nil, 403 allReturnIntTwin: int64(1), 404 allReturnErrTwin: nil, 405 queryTableMockTimes: int(3), 406 filterMockTimes: int(3), 407 deviceMockTimes: int(1), 408 attributeMockTimes: int(1), 409 twinMockTimes: int(1), 410 }, 411 } 412 for _, test := range tests { 413 t.Run(test.name, func(t *testing.T) { 414 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDevice).Return(test.allReturnIntDevice, test.allReturnErrDevice).Times(test.deviceMockTimes) 415 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceAttr).Return(test.allReturnIntAttribute, test.allReturnErrAttribute).Times(test.attributeMockTimes) 416 querySeterMock.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceTwin).Return(test.allReturnIntTwin, test.allReturnErrTwin).Times(test.twinMockTimes) 417 querySeterMock.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(test.filterMockTimes) 418 ormerMock.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(test.queryTableMockTimes) 419 if err := SyncDeviceFromSqlite(test.context, test.deviceID); !reflect.DeepEqual(err, test.wantErr) { 420 t.Errorf("SyncDeviceFromSqlite() error = %v, wantError %v", err, test.wantErr) 421 } 422 }) 423 } 424 } 425 426 //Test_classifyMsg is function to test classifyMsg(). 427 func Test_classifyMsg(t *testing.T) { 428 //Encoded resource with LifeCycleConnectETPrefix 429 connectTopic := dtcommon.LifeCycleConnectETPrefix + "testtopic" 430 encodedConnectTopicResource := base64.URLEncoding.EncodeToString([]byte(connectTopic)) 431 //Encoded resource with LifeCycleDisconnectETPrefix 432 disconnectTopic := dtcommon.LifeCycleDisconnectETPrefix + "testtopic" 433 encodedDisconnectResource := base64.URLEncoding.EncodeToString([]byte(disconnectTopic)) 434 //Encoded resource with other Prefix 435 otherTopic := "/membership/detail/result" 436 otherEncodedTopic := base64.URLEncoding.EncodeToString([]byte(otherTopic)) 437 //Encoded eventbus resource 438 eventbusTopic := "$hw/events/device/+/state/update" 439 eventbusResource := base64.URLEncoding.EncodeToString([]byte(eventbusTopic)) 440 //Creating content for model.message type 441 payload := dttype.MembershipUpdate{ 442 AddDevices: []dttype.Device{ 443 { 444 ID: "DeviceA", 445 Name: "Router", 446 State: "unknown", 447 }, 448 }, 449 } 450 content, _ := json.Marshal(payload) 451 tests := []struct { 452 name string 453 message *dttype.DTMessage 454 wantBool bool 455 }{ 456 { 457 //Failure Case 458 name: "classifyMsgTest-UnencodedMessageResource", 459 message: &dttype.DTMessage{ 460 Msg: &model.Message{ 461 Router: model.MessageRoute{ 462 Source: "bus", 463 Resource: "membership/detail", 464 }, 465 }, 466 }, 467 wantBool: false, 468 }, 469 { 470 //Success Case 471 name: "classifyMsgTest-Source:bus-Prefix:LifeCycleConnectETPrefix", 472 message: &dttype.DTMessage{ 473 Msg: &model.Message{ 474 Router: model.MessageRoute{ 475 Source: "bus", 476 Resource: encodedConnectTopicResource, 477 }, 478 Content: string(content), 479 }, 480 }, 481 wantBool: true, 482 }, 483 { 484 //Success Case 485 name: "classifyMsgTest-Source:bus-Prefix:LifeCycleDisconnectETPrefix", 486 message: &dttype.DTMessage{ 487 Msg: &model.Message{ 488 Router: model.MessageRoute{ 489 Source: "bus", 490 Resource: encodedDisconnectResource, 491 }, 492 Content: string(content), 493 }, 494 }, 495 wantBool: true, 496 }, 497 { 498 //Failure Case 499 name: "classifyMessageTest-Source:bus-Prefix:OtherPrefix", 500 message: &dttype.DTMessage{ 501 Msg: &model.Message{ 502 Router: model.MessageRoute{ 503 Source: "bus", 504 Resource: otherEncodedTopic, 505 }, 506 Content: string(content), 507 }, 508 }, 509 wantBool: false, 510 }, 511 { 512 //Success Case 513 name: "classifyMessageTest-Source:bus-Resource:eventbus", 514 message: &dttype.DTMessage{ 515 Msg: &model.Message{ 516 Router: model.MessageRoute{ 517 Source: "bus", 518 Resource: eventbusResource, 519 }, 520 Content: string(content), 521 }, 522 }, 523 wantBool: true, 524 }, 525 { 526 //Success Case 527 name: "classifyMessageTest-Source:edgemgr-Resource:membership/detail", 528 message: &dttype.DTMessage{ 529 Msg: &model.Message{ 530 Router: model.MessageRoute{ 531 Source: "edgemgr", 532 Resource: "membership/detail", 533 }, 534 Content: string(content), 535 }, 536 }, 537 wantBool: true, 538 }, 539 { 540 //Success Case 541 name: "classifyMessageTest-Source:edgemgr-Resource:membership", 542 message: &dttype.DTMessage{ 543 Msg: &model.Message{ 544 Router: model.MessageRoute{ 545 Source: "edgemgr", 546 Resource: "membership", 547 }, 548 Content: string(content), 549 }, 550 }, 551 wantBool: true, 552 }, 553 { 554 //Success Case 555 name: "classifyMessageTest-Source:edgemgr-Resourcetwin:cloud_updated", 556 message: &dttype.DTMessage{ 557 Msg: &model.Message{ 558 Router: model.MessageRoute{ 559 Source: "edgemgr", 560 Resource: "twin/cloud_updated", 561 }, 562 Content: string(content), 563 }, 564 }, 565 wantBool: true, 566 }, 567 { 568 //Success Case 569 name: "classifyMessageTest-Source:edgemgr-Resource:device/updated-Operation:updated", 570 message: &dttype.DTMessage{ 571 Msg: &model.Message{ 572 Router: model.MessageRoute{ 573 Source: "edgemgr", 574 Resource: "device/updated", 575 Operation: "updated", 576 }, 577 Content: string(content), 578 }, 579 }, 580 wantBool: true, 581 }, 582 { 583 //Failure Case 584 name: "calssifyMessageTest-Source:edgemgr-no resource and operation", 585 message: &dttype.DTMessage{ 586 Msg: &model.Message{ 587 Router: model.MessageRoute{ 588 Source: "edgemgr", 589 }, 590 Content: string(content), 591 }, 592 }, 593 wantBool: false, 594 }, 595 { 596 //Success Case 597 name: "classifyMessageTest-Source:edgehub-Resource:node/connection", 598 message: &dttype.DTMessage{ 599 Msg: &model.Message{ 600 Router: model.MessageRoute{ 601 Source: "edgehub", 602 Resource: "node/connection", 603 }, 604 Content: string(content), 605 }, 606 }, 607 wantBool: true, 608 }, 609 { 610 //Failure Case 611 name: "classifyMessageTest-Source:edgehub-Resource:node", 612 message: &dttype.DTMessage{ 613 Msg: &model.Message{ 614 Router: model.MessageRoute{ 615 Source: "edgehub", 616 Resource: "node", 617 }, 618 Content: string(content), 619 }, 620 }, 621 wantBool: false, 622 }, 623 } 624 for _, tt := range tests { 625 t.Run(tt.name, func(t *testing.T) { 626 if got := classifyMsg(tt.message); got != tt.wantBool { 627 t.Errorf("classifyMsg() = %v, wantError %v", got, tt.wantBool) 628 } 629 }) 630 } 631 }