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  }