github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/devicetwin/dtmanager/twin_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 dtmanager
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"reflect"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/astaxie/beego/orm"
    28  	"github.com/golang/mock/gomock"
    29  	"k8s.io/klog"
    30  
    31  	"github.com/kubeedge/beehive/pkg/core/model"
    32  	"github.com/kubeedge/kubeedge/edge/mocks/beego"
    33  	"github.com/kubeedge/kubeedge/edge/pkg/common/dbm"
    34  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtclient"
    35  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcommon"
    36  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcontext"
    37  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dttype"
    38  )
    39  
    40  var (
    41  	deviceA = "DeviceA"
    42  	deviceB = "DeviceB"
    43  	deviceC = "DeviceC"
    44  	event1  = "Event1"
    45  	key1    = "key1"
    46  
    47  	typeDeleted = "deleted"
    48  	typeInt     = "int"
    49  	typeString  = "string"
    50  )
    51  
    52  // sendMsg sends message to receiverChannel and heartbeatChannel
    53  func (tw TwinWorker) sendMsg(msg *dttype.DTMessage, msgHeart string, actionType string, contentType interface{}) {
    54  	if tw.ReceiverChan != nil {
    55  		msg.Action = actionType
    56  		msg.Msg.Content = contentType
    57  		tw.ReceiverChan <- msg
    58  	}
    59  	if tw.HeartBeatChan != nil {
    60  		tw.HeartBeatChan <- msgHeart
    61  	}
    62  }
    63  
    64  // receiveMsg receives message from the commChannel
    65  func receiveMsg(commChannel chan interface{}, message *dttype.DTMessage) {
    66  	msg, ok := <-commChannel
    67  	if !ok {
    68  		klog.Errorf("No message received from communication channel")
    69  		return
    70  	}
    71  	*message = *msg.(*dttype.DTMessage)
    72  }
    73  
    74  // twinValueFunc returns a new TwinValue
    75  func twinValueFunc() *dttype.TwinValue {
    76  	var twinValue dttype.TwinValue
    77  	value := "value"
    78  	valueMetaData := &dttype.ValueMetadata{Timestamp: time.Now().UnixNano() / 1e6}
    79  	twinValue.Value = &value
    80  	twinValue.Metadata = valueMetaData
    81  	return &twinValue
    82  }
    83  
    84  // keyTwinUpdateFunc returns a new DeviceTwinUpdate
    85  func keyTwinUpdateFunc() dttype.DeviceTwinUpdate {
    86  	var keyTwinUpdate dttype.DeviceTwinUpdate
    87  	twinKey := make(map[string]*dttype.MsgTwin)
    88  	twinKey[key1] = &dttype.MsgTwin{
    89  		Expected: twinValueFunc(),
    90  		Actual:   twinValueFunc(),
    91  		Metadata: &dttype.TypeMetadata{Type: "nil"},
    92  	}
    93  	keyTwinUpdate.Twin = twinKey
    94  	keyTwinUpdate.BaseMessage = dttype.BaseMessage{EventID: event1}
    95  	return keyTwinUpdate
    96  }
    97  
    98  // twinWorkerFunc returns a new TwinWorker
    99  func twinWorkerFunc(receiverChannel chan interface{}, confirmChannel chan interface{}, heartBeatChannel chan interface{}, context dtcontext.DTContext, group string) TwinWorker {
   100  	return TwinWorker{
   101  		Worker{
   102  			receiverChannel,
   103  			confirmChannel,
   104  			heartBeatChannel,
   105  			&context,
   106  		},
   107  		group,
   108  	}
   109  }
   110  
   111  // contextFunc returns a new DTContext
   112  func contextFunc(deviceID string) dtcontext.DTContext {
   113  	context := dtcontext.DTContext{
   114  		DeviceList:  &sync.Map{},
   115  		DeviceMutex: &sync.Map{},
   116  		Mutex:       &sync.RWMutex{},
   117  	}
   118  	var testMutex sync.Mutex
   119  	context.DeviceMutex.Store(deviceID, &testMutex)
   120  	var device dttype.Device
   121  	context.DeviceList.Store(deviceID, &device)
   122  	return context
   123  }
   124  
   125  // msgTypeFunc returns a new Message
   126  func msgTypeFunc(content interface{}) *model.Message {
   127  	return &model.Message{
   128  		Content: content,
   129  	}
   130  }
   131  
   132  // TestStart is function to test Start
   133  func TestStart(t *testing.T) {
   134  	keyTwinUpdate := keyTwinUpdateFunc()
   135  	contentKeyTwin, _ := json.Marshal(keyTwinUpdate)
   136  
   137  	commChan := make(map[string]chan interface{})
   138  	commChannel := make(chan interface{})
   139  	commChan[dtcommon.CommModule] = commChannel
   140  
   141  	context := dtcontext.DTContext{
   142  		DeviceList:    &sync.Map{},
   143  		DeviceMutex:   &sync.Map{},
   144  		Mutex:         &sync.RWMutex{},
   145  		CommChan:      commChan,
   146  		ModulesHealth: &sync.Map{},
   147  	}
   148  	var testMutex sync.Mutex
   149  	context.DeviceMutex.Store(deviceB, &testMutex)
   150  	msgAttr := make(map[string]*dttype.MsgAttr)
   151  	device := dttype.Device{
   152  		ID:         "id1",
   153  		Name:       deviceB,
   154  		Attributes: msgAttr,
   155  		Twin:       keyTwinUpdate.Twin,
   156  	}
   157  	context.DeviceList.Store(deviceB, &device)
   158  
   159  	msg := &dttype.DTMessage{
   160  		Msg: &model.Message{
   161  			Header: model.MessageHeader{
   162  				ID:        "id1",
   163  				ParentID:  "pid1",
   164  				Timestamp: 0,
   165  				Sync:      false,
   166  			},
   167  			Router: model.MessageRoute{
   168  				Source:    "source",
   169  				Resource:  "resource",
   170  				Group:     "group",
   171  				Operation: "op",
   172  			},
   173  			Content: contentKeyTwin,
   174  		},
   175  		Action: dtcommon.TwinGet,
   176  		Type:   dtcommon.CommModule,
   177  	}
   178  	msgHeartPing := "ping"
   179  	msgHeartStop := "stop"
   180  	receiverChannel := make(chan interface{})
   181  	heartbeatChannel := make(chan interface{})
   182  
   183  	tests := []struct {
   184  		name        string
   185  		tw          TwinWorker
   186  		actionType  string
   187  		contentType interface{}
   188  		msgType     string
   189  	}{
   190  		{
   191  			name:        "TestStart(): Case 1: ReceiverChan case when error is nil",
   192  			tw:          twinWorkerFunc(receiverChannel, nil, nil, context, ""),
   193  			actionType:  dtcommon.TwinGet,
   194  			contentType: contentKeyTwin,
   195  		},
   196  		{
   197  			name:        "TestStart(): Case 2: ReceiverChan case error log; TwinModule deal event failed, not found callback",
   198  			tw:          twinWorkerFunc(receiverChannel, nil, nil, context, ""),
   199  			actionType:  dtcommon.SendToEdge,
   200  			contentType: contentKeyTwin,
   201  		},
   202  		{
   203  			name:       "TestStart(): Case 3: ReceiverChan case error log; TwinModule deal event failed",
   204  			tw:         twinWorkerFunc(receiverChannel, nil, nil, context, ""),
   205  			actionType: dtcommon.TwinGet,
   206  		},
   207  		{
   208  			name:    "TestStart(): Case 4: HeartBeatChan case when error is nil",
   209  			tw:      twinWorkerFunc(nil, nil, heartbeatChannel, context, "Group1"),
   210  			msgType: msgHeartPing,
   211  		},
   212  		{
   213  			name:    "TestStart(): Case 5: HeartBeatChan case when error is not nil",
   214  			tw:      twinWorkerFunc(nil, nil, heartbeatChannel, context, "Group1"),
   215  			msgType: msgHeartStop,
   216  		},
   217  	}
   218  	for _, test := range tests {
   219  		t.Run(test.name, func(t *testing.T) {
   220  			go test.tw.sendMsg(msg, test.msgType, test.actionType, test.contentType)
   221  			go test.tw.Start()
   222  			time.Sleep(100 * time.Millisecond)
   223  			message := &dttype.DTMessage{}
   224  			go receiveMsg(commChannel, message)
   225  			time.Sleep(100 * time.Millisecond)
   226  			if (test.tw.ReceiverChan != nil) && !reflect.DeepEqual(message.Identity, msg.Identity) && !reflect.DeepEqual(message.Type, msg.Type) {
   227  				t.Errorf("DTManager.TestStart() case failed: got = %v, Want = %v", message, msg)
   228  			}
   229  			if _, exist := context.ModulesHealth.Load("Group1"); test.tw.HeartBeatChan != nil && !exist {
   230  				t.Errorf("DTManager.TestStart() case failed: HeartBeatChan received no string")
   231  			}
   232  		})
   233  	}
   234  }
   235  
   236  // TestDealTwinSync is function to test dealTwinSync
   237  func TestDealTwinSync(t *testing.T) {
   238  	content, _ := json.Marshal(dttype.DeviceTwinUpdate{BaseMessage: dttype.BaseMessage{EventID: event1}})
   239  	contentKeyTwin, _ := json.Marshal(keyTwinUpdateFunc())
   240  	context := contextFunc(deviceB)
   241  
   242  	tests := []struct {
   243  		name     string
   244  		context  *dtcontext.DTContext
   245  		resource string
   246  		msg      interface{}
   247  		err      error
   248  	}{
   249  		{
   250  			name:    "TestDealTwinSync(): Case 1: msg not Message type",
   251  			context: &dtcontext.DTContext{},
   252  			msg: model.Message{
   253  				Content: dttype.BaseMessage{EventID: event1},
   254  			},
   255  			err: errors.New("msg not Message type"),
   256  		},
   257  		{
   258  			name:    "TestDealTwinSync(): Case 2: invalid message content",
   259  			context: &dtcontext.DTContext{},
   260  			msg:     msgTypeFunc(dttype.BaseMessage{EventID: event1}),
   261  			err:     errors.New("invalid message content"),
   262  		},
   263  		{
   264  			name:    "TestDealTwinSync(): Case 3: Unmarshal update request body failed",
   265  			context: &dtcontext.DTContext{},
   266  			msg:     msgTypeFunc(content),
   267  			err:     errors.New("Update twin error, the update request body not have key:twin"),
   268  		},
   269  		{
   270  			name:     "TestDealTwinSync(): Case 4: Success case",
   271  			context:  &context,
   272  			resource: deviceB,
   273  			msg:      msgTypeFunc(contentKeyTwin),
   274  			err:      nil,
   275  		},
   276  	}
   277  	for _, test := range tests {
   278  		t.Run(test.name, func(t *testing.T) {
   279  			if _, err := dealTwinSync(test.context, test.resource, test.msg); !reflect.DeepEqual(err, test.err) {
   280  				t.Errorf("DTManager.TestDealTwinSync() case failed: got = %v, Want = %v", err, test.err)
   281  			}
   282  		})
   283  	}
   284  }
   285  
   286  // TestDealTwinGet is function to test dealTwinGet
   287  func TestDealTwinGet(t *testing.T) {
   288  	contentKeyTwin, _ := json.Marshal(keyTwinUpdateFunc())
   289  	context := contextFunc(deviceB)
   290  
   291  	tests := []struct {
   292  		name     string
   293  		context  *dtcontext.DTContext
   294  		resource string
   295  		msg      interface{}
   296  		err      error
   297  	}{
   298  		{
   299  			name:    "TestDealTwinGet(): Case 1: msg not Message type",
   300  			context: &dtcontext.DTContext{},
   301  			msg: model.Message{
   302  				Content: dttype.BaseMessage{EventID: event1},
   303  			},
   304  			err: errors.New("msg not Message type"),
   305  		},
   306  		{
   307  			name:    "TestDealTwinGet(): Case 2: invalid message content",
   308  			context: &dtcontext.DTContext{},
   309  			msg:     msgTypeFunc(dttype.BaseMessage{EventID: event1}),
   310  			err:     errors.New("invalid message content"),
   311  		},
   312  		{
   313  			name:     "TestDealTwinGet(): Case 3: Success; Unmarshal twin info fails in DealGetTwin()",
   314  			context:  &context,
   315  			resource: deviceB,
   316  			msg:      msgTypeFunc([]byte("")),
   317  			err:      nil,
   318  		},
   319  		{
   320  			name:     "TestDealTwinGet(): Case 4: Success; Device not found while getting twin in DealGetTwin()",
   321  			context:  &context,
   322  			resource: deviceB,
   323  			msg:      msgTypeFunc(contentKeyTwin),
   324  			err:      nil,
   325  		},
   326  	}
   327  	for _, test := range tests {
   328  		t.Run(test.name, func(t *testing.T) {
   329  			if _, err := dealTwinGet(test.context, test.resource, test.msg); !reflect.DeepEqual(err, test.err) {
   330  				t.Errorf("DTManager.TestDealTwinGet() case failed: got = %v, Want = %v", err, test.err)
   331  			}
   332  		})
   333  	}
   334  }
   335  
   336  // TestDealTwinUpdate is function to test dealTwinUpdate
   337  func TestDealTwinUpdate(t *testing.T) {
   338  	content, _ := json.Marshal(dttype.DeviceTwinUpdate{BaseMessage: dttype.BaseMessage{EventID: event1}})
   339  	contentKeyTwin, _ := json.Marshal(keyTwinUpdateFunc())
   340  	context := contextFunc(deviceB)
   341  
   342  	tests := []struct {
   343  		name     string
   344  		context  *dtcontext.DTContext
   345  		resource string
   346  		msg      interface{}
   347  		err      error
   348  	}{
   349  		{
   350  			name:    "TestDealTwinUpdate(): Case 1: msg not Message type",
   351  			context: &dtcontext.DTContext{},
   352  			msg: model.Message{
   353  				Content: dttype.BaseMessage{EventID: event1},
   354  			},
   355  			err: errors.New("msg not Message type"),
   356  		},
   357  		{
   358  			name:    "TestDealTwinUpdate(): Case 2: invalid message content",
   359  			context: &dtcontext.DTContext{},
   360  			msg:     msgTypeFunc(dttype.BaseMessage{EventID: event1}),
   361  			err:     errors.New("invalid message content"),
   362  		},
   363  		{
   364  			name:     "TestDealTwinUpdate(): Case 3: Success; Unmarshal update request body fails in Updated()",
   365  			context:  &context,
   366  			resource: deviceB,
   367  			msg:      msgTypeFunc(content),
   368  			err:      nil,
   369  		},
   370  		{
   371  			name:     "TestDealTwinUpdate(): Case 4: Success; Begin to update twin of the device in Updated()",
   372  			context:  &context,
   373  			resource: deviceA,
   374  			msg:      msgTypeFunc(contentKeyTwin),
   375  			err:      nil,
   376  		},
   377  	}
   378  	for _, test := range tests {
   379  		t.Run(test.name, func(t *testing.T) {
   380  			if _, err := dealTwinUpdate(test.context, test.resource, test.msg); !reflect.DeepEqual(err, test.err) {
   381  				t.Errorf("DTManager.TestDealTwinUpdate() case failed: got = %v, Want = %v", err, test.err)
   382  			}
   383  		})
   384  	}
   385  }
   386  
   387  // TestDealDeviceTwin is function to test DealDeviceTwin
   388  func TestDealDeviceTwin(t *testing.T) {
   389  	var mockOrmer *beego.MockOrmer
   390  	var mockQuerySeter *beego.MockQuerySeter
   391  	mockCtrl := gomock.NewController(t)
   392  	defer mockCtrl.Finish()
   393  	mockOrmer = beego.NewMockOrmer(mockCtrl)
   394  	mockQuerySeter = beego.NewMockQuerySeter(mockCtrl)
   395  	dbm.DBAccess = mockOrmer
   396  
   397  	str := typeString
   398  	optionTrue := true
   399  	msgTwin := make(map[string]*dttype.MsgTwin)
   400  	msgTwin[key1] = &dttype.MsgTwin{
   401  		Expected: twinValueFunc(),
   402  		Metadata: &dttype.TypeMetadata{
   403  			Type: typeDeleted,
   404  		},
   405  	}
   406  	contextDeviceB := contextFunc(deviceB)
   407  	twinDeviceB := make(map[string]*dttype.MsgTwin)
   408  	twinDeviceB[deviceB] = &dttype.MsgTwin{
   409  		Expected: &dttype.TwinValue{
   410  			Value: &str,
   411  		},
   412  		Optional: &optionTrue,
   413  	}
   414  	deviceBTwin := dttype.Device{
   415  		Twin: twinDeviceB,
   416  	}
   417  	contextDeviceB.DeviceList.Store(deviceB, &deviceBTwin)
   418  
   419  	contextDeviceC := dtcontext.DTContext{
   420  		DeviceList:  &sync.Map{},
   421  		DeviceMutex: &sync.Map{},
   422  		Mutex:       &sync.RWMutex{},
   423  	}
   424  	var testMutex sync.Mutex
   425  	contextDeviceC.DeviceMutex.Store(deviceC, &testMutex)
   426  	twinDeviceC := make(map[string]*dttype.MsgTwin)
   427  
   428  	twinDeviceC[deviceC] = &dttype.MsgTwin{
   429  		Expected: &dttype.TwinValue{
   430  			Value: &str,
   431  		},
   432  		Optional: &optionTrue,
   433  	}
   434  	deviceCTwin := dttype.Device{Twin: twinDeviceC}
   435  	contextDeviceC.DeviceList.Store(deviceC, &deviceCTwin)
   436  
   437  	tests := []struct {
   438  		name             string
   439  		context          *dtcontext.DTContext
   440  		deviceID         string
   441  		eventID          string
   442  		msgTwin          map[string]*dttype.MsgTwin
   443  		dealType         int
   444  		err              error
   445  		filterReturn     orm.QuerySeter
   446  		allReturnInt     int64
   447  		allReturnErr     error
   448  		queryTableReturn orm.QuerySeter
   449  
   450  		rollbackNums int
   451  		beginNums    int
   452  		commitNums   int
   453  		filterNums   int
   454  		insertNums   int
   455  		deleteNums   int
   456  		updateNums   int
   457  		queryNums    int
   458  	}{
   459  		{
   460  			name:         "TestDealDeviceTwin(): Case 1: msgTwin is nil",
   461  			context:      &contextDeviceB,
   462  			deviceID:     deviceB,
   463  			dealType:     RestDealType,
   464  			err:          errors.New("Update twin error, the update request body not have key:twin"),
   465  			rollbackNums: 0,
   466  			beginNums:    1,
   467  			commitNums:   1,
   468  			filterNums:   0,
   469  			insertNums:   1,
   470  			deleteNums:   0,
   471  			updateNums:   0,
   472  			queryNums:    0,
   473  		},
   474  		{
   475  			name:             "TestDealDeviceTwin(): Case 2: Success Case",
   476  			context:          &contextDeviceC,
   477  			deviceID:         deviceC,
   478  			msgTwin:          msgTwin,
   479  			dealType:         RestDealType,
   480  			err:              nil,
   481  			filterReturn:     mockQuerySeter,
   482  			allReturnInt:     int64(1),
   483  			allReturnErr:     nil,
   484  			queryTableReturn: mockQuerySeter,
   485  			rollbackNums:     0,
   486  			beginNums:        1,
   487  			commitNums:       1,
   488  			filterNums:       0,
   489  			insertNums:       1,
   490  			deleteNums:       0,
   491  			updateNums:       0,
   492  			queryNums:        0,
   493  		},
   494  	}
   495  
   496  	for _, test := range tests {
   497  		t.Run(test.name, func(t *testing.T) {
   498  			mockOrmer.EXPECT().Rollback().Return(nil).Times(test.rollbackNums)
   499  			mockOrmer.EXPECT().Begin().Return(nil).MaxTimes(test.beginNums)
   500  			mockOrmer.EXPECT().Commit().Return(nil).MaxTimes(test.commitNums)
   501  			mockOrmer.EXPECT().Insert(gomock.Any()).Return(test.allReturnInt, test.allReturnErr).MaxTimes(test.insertNums)
   502  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(test.filterNums)
   503  			mockQuerySeter.EXPECT().Delete().Return(test.allReturnInt, test.allReturnErr).Times(test.deleteNums)
   504  			mockQuerySeter.EXPECT().Update(gomock.Any()).Return(test.allReturnInt, test.allReturnErr).Times(test.updateNums)
   505  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(test.queryNums)
   506  			if err := DealDeviceTwin(test.context, test.deviceID, test.eventID, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
   507  				t.Errorf("DTManager.TestDealDeviceTwin() case failed: got = %v, Want = %v", err, test.err)
   508  			}
   509  		})
   510  	}
   511  }
   512  
   513  // TestDealDeviceTwinResult is function to test DealDeviceTwin when dealTwinResult.Err is not nil
   514  func TestDealDeviceTwinResult(t *testing.T) {
   515  	var mockOrmer *beego.MockOrmer
   516  	var mockQuerySeter *beego.MockQuerySeter
   517  	mockCtrl := gomock.NewController(t)
   518  	defer mockCtrl.Finish()
   519  	mockOrmer = beego.NewMockOrmer(mockCtrl)
   520  	mockQuerySeter = beego.NewMockQuerySeter(mockCtrl)
   521  	dbm.DBAccess = mockOrmer
   522  
   523  	str := typeString
   524  	optionTrue := true
   525  	value := "value"
   526  	msgTwinValue := make(map[string]*dttype.MsgTwin)
   527  	msgTwinValue[deviceB] = &dttype.MsgTwin{
   528  		Expected: &dttype.TwinValue{Value: &value},
   529  		Metadata: &dttype.TypeMetadata{Type: "nil"},
   530  	}
   531  	contextDeviceA := contextFunc(deviceB)
   532  	twinDeviceA := make(map[string]*dttype.MsgTwin)
   533  	twinDeviceA[deviceA] = &dttype.MsgTwin{
   534  		Expected: &dttype.TwinValue{Value: &str},
   535  		Actual:   &dttype.TwinValue{Value: &str},
   536  		Optional: &optionTrue,
   537  		Metadata: &dttype.TypeMetadata{Type: typeDeleted},
   538  	}
   539  	deviceATwin := dttype.Device{Twin: twinDeviceA}
   540  	contextDeviceA.DeviceList.Store(deviceA, &deviceATwin)
   541  
   542  	tests := []struct {
   543  		name             string
   544  		context          *dtcontext.DTContext
   545  		deviceID         string
   546  		eventID          string
   547  		msgTwin          map[string]*dttype.MsgTwin
   548  		dealType         int
   549  		err              error
   550  		filterReturn     orm.QuerySeter
   551  		allReturnInt     int64
   552  		allReturnErr     error
   553  		queryTableReturn orm.QuerySeter
   554  	}{
   555  		{
   556  			name:             "TestDealDeviceTwinResult(): dealTwinResult error",
   557  			context:          &contextDeviceA,
   558  			deviceID:         deviceB,
   559  			msgTwin:          msgTwinValue,
   560  			dealType:         RestDealType,
   561  			err:              errors.New("the value type is not allowed"),
   562  			filterReturn:     mockQuerySeter,
   563  			allReturnInt:     int64(1),
   564  			allReturnErr:     nil,
   565  			queryTableReturn: mockQuerySeter,
   566  		},
   567  	}
   568  
   569  	fakeDevice := new([]dtclient.Device)
   570  	fakeDeviceArray := make([]dtclient.Device, 1)
   571  	fakeDeviceArray[0] = dtclient.Device{ID: deviceB}
   572  	fakeDevice = &fakeDeviceArray
   573  
   574  	fakeDeviceAttr := new([]dtclient.DeviceAttr)
   575  	fakeDeviceAttrArray := make([]dtclient.DeviceAttr, 1)
   576  	fakeDeviceAttrArray[0] = dtclient.DeviceAttr{DeviceID: deviceB}
   577  	fakeDeviceAttr = &fakeDeviceAttrArray
   578  
   579  	fakeDeviceTwin := new([]dtclient.DeviceTwin)
   580  	fakeDeviceTwinArray := make([]dtclient.DeviceTwin, 1)
   581  	fakeDeviceTwinArray[0] = dtclient.DeviceTwin{DeviceID: deviceB}
   582  	fakeDeviceTwin = &fakeDeviceTwinArray
   583  
   584  	for _, test := range tests {
   585  		t.Run(test.name, func(t *testing.T) {
   586  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDevice).Return(test.allReturnInt, test.allReturnErr).Times(1)
   587  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   588  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   589  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceAttr).Return(test.allReturnInt, test.allReturnErr).Times(1)
   590  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   591  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   592  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceTwin).Return(test.allReturnInt, test.allReturnErr).Times(1)
   593  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   594  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   595  			if err := DealDeviceTwin(test.context, test.deviceID, test.eventID, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
   596  				t.Errorf("DTManager.TestDealDeviceTwinResult() case failed: got = %v, Want = %v", err, test.err)
   597  			}
   598  		})
   599  	}
   600  }
   601  
   602  // TestDealDeviceTwinTrans is function to test DealDeviceTwin when DeviceTwinTrans() return error
   603  func TestDealDeviceTwinTrans(t *testing.T) {
   604  	var mockOrmer *beego.MockOrmer
   605  	var mockQuerySeter *beego.MockQuerySeter
   606  	mockCtrl := gomock.NewController(t)
   607  	defer mockCtrl.Finish()
   608  	mockOrmer = beego.NewMockOrmer(mockCtrl)
   609  	mockQuerySeter = beego.NewMockQuerySeter(mockCtrl)
   610  	dbm.DBAccess = mockOrmer
   611  
   612  	str := typeString
   613  	optionTrue := true
   614  	msgTwin := make(map[string]*dttype.MsgTwin)
   615  	msgTwin[key1] = &dttype.MsgTwin{
   616  		Expected: twinValueFunc(),
   617  		Metadata: &dttype.TypeMetadata{
   618  			Type: typeDeleted,
   619  		},
   620  	}
   621  
   622  	contextDeviceB := contextFunc(deviceB)
   623  	twinDeviceB := make(map[string]*dttype.MsgTwin)
   624  	twinDeviceB[deviceB] = &dttype.MsgTwin{
   625  		Expected: &dttype.TwinValue{
   626  			Value: &str,
   627  		},
   628  		Optional: &optionTrue,
   629  	}
   630  	deviceBTwin := dttype.Device{Twin: twinDeviceB}
   631  	contextDeviceB.DeviceList.Store(deviceB, &deviceBTwin)
   632  
   633  	tests := []struct {
   634  		name             string
   635  		context          *dtcontext.DTContext
   636  		deviceID         string
   637  		eventID          string
   638  		msgTwin          map[string]*dttype.MsgTwin
   639  		dealType         int
   640  		err              error
   641  		filterReturn     orm.QuerySeter
   642  		insertReturnInt  int64
   643  		insertReturnErr  error
   644  		deleteReturnInt  int64
   645  		deleteReturnErr  error
   646  		updateReturnInt  int64
   647  		updateReturnErr  error
   648  		allReturnInt     int64
   649  		allReturnErr     error
   650  		queryTableReturn orm.QuerySeter
   651  	}{
   652  		{
   653  			name:             "TestDealDeviceTwinTrans(): DeviceTwinTrans error",
   654  			context:          &contextDeviceB,
   655  			deviceID:         deviceB,
   656  			msgTwin:          msgTwin,
   657  			dealType:         RestDealType,
   658  			err:              errors.New("Failed DB Operation"),
   659  			filterReturn:     mockQuerySeter,
   660  			insertReturnInt:  int64(1),
   661  			insertReturnErr:  errors.New("Failed DB Operation"),
   662  			deleteReturnInt:  int64(1),
   663  			deleteReturnErr:  nil,
   664  			updateReturnInt:  int64(1),
   665  			updateReturnErr:  nil,
   666  			allReturnInt:     int64(1),
   667  			allReturnErr:     nil,
   668  			queryTableReturn: mockQuerySeter,
   669  		},
   670  	}
   671  
   672  	fakeDevice := new([]dtclient.Device)
   673  	fakeDeviceArray := make([]dtclient.Device, 1)
   674  	fakeDeviceArray[0] = dtclient.Device{ID: deviceB}
   675  	fakeDevice = &fakeDeviceArray
   676  
   677  	fakeDeviceAttr := new([]dtclient.DeviceAttr)
   678  	fakeDeviceAttrArray := make([]dtclient.DeviceAttr, 1)
   679  	fakeDeviceAttrArray[0] = dtclient.DeviceAttr{DeviceID: deviceB}
   680  	fakeDeviceAttr = &fakeDeviceAttrArray
   681  
   682  	fakeDeviceTwin := new([]dtclient.DeviceTwin)
   683  	fakeDeviceTwinArray := make([]dtclient.DeviceTwin, 1)
   684  	fakeDeviceTwinArray[0] = dtclient.DeviceTwin{DeviceID: deviceB}
   685  	fakeDeviceTwin = &fakeDeviceTwinArray
   686  
   687  	for _, test := range tests {
   688  		t.Run(test.name, func(t *testing.T) {
   689  			mockOrmer.EXPECT().Rollback().Return(nil).Times(5)
   690  			mockOrmer.EXPECT().Commit().Return(nil).Times(0)
   691  			mockOrmer.EXPECT().Begin().Return(nil).Times(5)
   692  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(0)
   693  			mockOrmer.EXPECT().Insert(gomock.Any()).Return(test.insertReturnInt, test.insertReturnErr).Times(5)
   694  			mockQuerySeter.EXPECT().Delete().Return(test.deleteReturnInt, test.deleteReturnErr).Times(0)
   695  			mockQuerySeter.EXPECT().Update(gomock.Any()).Return(test.updateReturnInt, test.updateReturnErr).Times(0)
   696  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDevice).Return(test.allReturnInt, test.allReturnErr).Times(1)
   697  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   698  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   699  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceAttr).Return(test.allReturnInt, test.allReturnErr).Times(1)
   700  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   701  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   702  			mockQuerySeter.EXPECT().All(gomock.Any()).SetArg(0, *fakeDeviceTwin).Return(test.allReturnInt, test.allReturnErr).Times(1)
   703  			mockQuerySeter.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(test.filterReturn).Times(1)
   704  			mockOrmer.EXPECT().QueryTable(gomock.Any()).Return(test.queryTableReturn).Times(1)
   705  			if err := DealDeviceTwin(test.context, test.deviceID, test.eventID, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
   706  				t.Errorf("DTManager.TestDealDeviceTwinTrans() case failed: got = %v, Want = %v", err, test.err)
   707  			}
   708  		})
   709  	}
   710  }
   711  
   712  // TestDealVersion is function to test dealVersion
   713  func TestDealVersion(t *testing.T) {
   714  	twinCloudEdgeVersion := dttype.TwinVersion{
   715  		CloudVersion: 1,
   716  		EdgeVersion:  1,
   717  	}
   718  	twinCloudVersion := dttype.TwinVersion{
   719  		CloudVersion: 1,
   720  		EdgeVersion:  0,
   721  	}
   722  
   723  	tests := []struct {
   724  		name       string
   725  		version    *dttype.TwinVersion
   726  		reqVersion *dttype.TwinVersion
   727  		dealType   int
   728  		errorWant  bool
   729  		err        error
   730  	}{
   731  		{
   732  			name:      "TestDealVersion(): Case 1: dealType=3",
   733  			version:   &dttype.TwinVersion{},
   734  			dealType:  SyncTwinDeleteDealType,
   735  			errorWant: true,
   736  			err:       nil,
   737  		},
   738  		{
   739  			name:       "TestDealVersion(): Case 2: dealType>=1 && version.EdgeVersion>reqVersion.EdgeVersion",
   740  			version:    &twinCloudEdgeVersion,
   741  			reqVersion: &twinCloudVersion,
   742  			dealType:   SyncDealType,
   743  			errorWant:  false,
   744  			err:        errors.New("Not allowed to sync due to version conflict"),
   745  		},
   746  	}
   747  	for _, test := range tests {
   748  		t.Run(test.name, func(t *testing.T) {
   749  			got, err := dealVersion(test.version, test.reqVersion, test.dealType)
   750  			if !reflect.DeepEqual(err, test.err) {
   751  				t.Errorf("DTManager.TestDealVersion() case failed: got = %v, Want = %v", err, test.err)
   752  				return
   753  			}
   754  			if !reflect.DeepEqual(got, test.errorWant) {
   755  				t.Errorf("DTManager.TestDealVersion() case failed: got = %v, want %v", got, test.errorWant)
   756  			}
   757  		})
   758  	}
   759  }
   760  
   761  // TestDealTwinDelete is function to test dealTwinDelete
   762  func TestDealTwinDelete(t *testing.T) {
   763  	optionTrue := true
   764  	optionFalse := false
   765  	str := typeString
   766  	doc := make(map[string]*dttype.TwinDoc)
   767  	doc[key1] = &dttype.TwinDoc{}
   768  	sync := make(map[string]*dttype.MsgTwin)
   769  	sync[key1] = &dttype.MsgTwin{
   770  		Expected:        twinValueFunc(),
   771  		Actual:          twinValueFunc(),
   772  		Optional:        &optionTrue,
   773  		Metadata:        &dttype.TypeMetadata{Type: typeDeleted},
   774  		ExpectedVersion: &dttype.TwinVersion{},
   775  		ActualVersion:   &dttype.TwinVersion{},
   776  	}
   777  	result := make(map[string]*dttype.MsgTwin)
   778  	result[key1] = &dttype.MsgTwin{}
   779  
   780  	tests := []struct {
   781  		name         string
   782  		returnResult *dttype.DealTwinResult
   783  		deviceID     string
   784  		key          string
   785  		twin         *dttype.MsgTwin
   786  		msgTwin      *dttype.MsgTwin
   787  		dealType     int
   788  		err          error
   789  	}{
   790  		{
   791  			name: "TestDealTwinDelete(): Case 1: msgTwin is not nil; isChange is false",
   792  			returnResult: &dttype.DealTwinResult{
   793  				Document:   doc,
   794  				SyncResult: sync,
   795  				Result:     result,
   796  			},
   797  			deviceID: deviceA,
   798  			key:      key1,
   799  			twin: &dttype.MsgTwin{
   800  				Optional: &optionTrue,
   801  				Metadata: &dttype.TypeMetadata{
   802  					Type: typeDeleted,
   803  				},
   804  				ExpectedVersion: &dttype.TwinVersion{},
   805  			},
   806  			msgTwin: &dttype.MsgTwin{
   807  				Expected: &dttype.TwinValue{
   808  					Value: &str,
   809  				},
   810  				Actual: &dttype.TwinValue{
   811  					Value: &str,
   812  				},
   813  				Optional: &optionFalse,
   814  				Metadata: &dttype.TypeMetadata{
   815  					Type: typeString,
   816  				},
   817  				ExpectedVersion: &dttype.TwinVersion{},
   818  				ActualVersion:   &dttype.TwinVersion{},
   819  			},
   820  			dealType: SyncDealType,
   821  			err:      nil,
   822  		},
   823  		{
   824  			name: "TestDealTwinDelete(): Case 2: hasTwinExpected is true; dealVersion() returns false",
   825  			returnResult: &dttype.DealTwinResult{
   826  				Document:   doc,
   827  				SyncResult: sync,
   828  				Result:     result,
   829  			},
   830  			deviceID: deviceA,
   831  			key:      key1,
   832  			twin: &dttype.MsgTwin{
   833  				Optional: &optionTrue,
   834  				Metadata: &dttype.TypeMetadata{
   835  					Type: typeString,
   836  				},
   837  				ExpectedVersion: &dttype.TwinVersion{
   838  					CloudVersion: 1,
   839  				},
   840  			},
   841  			msgTwin: &dttype.MsgTwin{
   842  				Expected: &dttype.TwinValue{
   843  					Value: &str,
   844  				},
   845  				Actual: &dttype.TwinValue{
   846  					Value: &str,
   847  				},
   848  				Optional: &optionFalse,
   849  				Metadata: &dttype.TypeMetadata{
   850  					Type: typeDeleted,
   851  				},
   852  				ExpectedVersion: &dttype.TwinVersion{
   853  					CloudVersion: 0,
   854  				},
   855  				ActualVersion: &dttype.TwinVersion{},
   856  			},
   857  			dealType: SyncDealType,
   858  			err:      nil,
   859  		},
   860  		{
   861  			name: "TestDealTwinDelete(): Case 3: hasTwinActual is true; dealVersion() returns false",
   862  			returnResult: &dttype.DealTwinResult{
   863  				Document:   doc,
   864  				SyncResult: sync,
   865  				Result:     result,
   866  			},
   867  			deviceID: deviceA,
   868  			key:      key1,
   869  			twin: &dttype.MsgTwin{
   870  				Optional: &optionTrue,
   871  				Metadata: &dttype.TypeMetadata{
   872  					Type: typeString,
   873  				},
   874  				ActualVersion: &dttype.TwinVersion{
   875  					CloudVersion: 1,
   876  				},
   877  			},
   878  			msgTwin: &dttype.MsgTwin{
   879  				Expected: &dttype.TwinValue{
   880  					Value: &str,
   881  				},
   882  				Actual: &dttype.TwinValue{
   883  					Value: &str,
   884  				},
   885  				Optional: &optionFalse,
   886  				Metadata: &dttype.TypeMetadata{
   887  					Type: typeDeleted,
   888  				},
   889  				ExpectedVersion: &dttype.TwinVersion{},
   890  				ActualVersion: &dttype.TwinVersion{
   891  					CloudVersion: 0,
   892  				},
   893  			},
   894  			dealType: SyncDealType,
   895  			err:      nil,
   896  		},
   897  		{
   898  			name:         "TestDealTwinDelete(): Case 4: hasTwinExpected is true; hasTwinActual is true",
   899  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
   900  			deviceID:     deviceA,
   901  			key:          key1,
   902  			twin: &dttype.MsgTwin{
   903  				Optional: &optionTrue,
   904  				Metadata: &dttype.TypeMetadata{
   905  					Type: typeString,
   906  				},
   907  				ExpectedVersion: &dttype.TwinVersion{},
   908  				ActualVersion:   &dttype.TwinVersion{},
   909  			},
   910  			dealType: RestDealType,
   911  			err:      nil,
   912  		},
   913  		{
   914  			name: "TestDealTwinDelete(): Case 5: hasTwinExpected is true; hasTwinActual is false",
   915  			returnResult: &dttype.DealTwinResult{
   916  				Document:   doc,
   917  				SyncResult: sync,
   918  				Result:     result,
   919  			},
   920  			deviceID: deviceA,
   921  			key:      key1,
   922  			twin: &dttype.MsgTwin{
   923  				Optional: &optionTrue,
   924  				Metadata: &dttype.TypeMetadata{
   925  					Type: typeString,
   926  				},
   927  				ExpectedVersion: &dttype.TwinVersion{},
   928  			},
   929  			msgTwin: &dttype.MsgTwin{
   930  				Expected: &dttype.TwinValue{
   931  					Value: &str,
   932  				},
   933  				Actual: &dttype.TwinValue{
   934  					Value: &str,
   935  				},
   936  				Optional: &optionFalse,
   937  				Metadata: &dttype.TypeMetadata{
   938  					Type: typeDeleted,
   939  				},
   940  				ExpectedVersion: &dttype.TwinVersion{},
   941  				ActualVersion:   &dttype.TwinVersion{},
   942  			},
   943  			dealType: SyncDealType,
   944  			err:      nil,
   945  		},
   946  	}
   947  	for _, test := range tests {
   948  		t.Run(test.name, func(t *testing.T) {
   949  			if err := dealTwinDelete(test.returnResult, test.deviceID, test.key, test.twin, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
   950  				t.Errorf("DTManager.TestDealTwinDelete() case failed: got = %+v, Want = %+v", err, test.err)
   951  			}
   952  		})
   953  	}
   954  }
   955  
   956  // TestDealTwinCompare is function to test dealTwinCompare
   957  func TestDealTwinCompare(t *testing.T) {
   958  	optionTrue := true
   959  	optionFalse := false
   960  	str := typeString
   961  	doc := make(map[string]*dttype.TwinDoc)
   962  	doc[key1] = &dttype.TwinDoc{}
   963  	sync := make(map[string]*dttype.MsgTwin)
   964  	sync[key1] = &dttype.MsgTwin{
   965  		Expected:        twinValueFunc(),
   966  		Actual:          twinValueFunc(),
   967  		Optional:        &optionTrue,
   968  		Metadata:        &dttype.TypeMetadata{Type: typeDeleted},
   969  		ExpectedVersion: &dttype.TwinVersion{},
   970  		ActualVersion:   &dttype.TwinVersion{},
   971  	}
   972  	result := make(map[string]*dttype.MsgTwin)
   973  	result[key1] = &dttype.MsgTwin{}
   974  
   975  	tests := []struct {
   976  		name         string
   977  		returnResult *dttype.DealTwinResult
   978  		deviceID     string
   979  		key          string
   980  		twin         *dttype.MsgTwin
   981  		msgTwin      *dttype.MsgTwin
   982  		dealType     int
   983  		err          error
   984  	}{
   985  		{
   986  			name:         "TestDealTwinCompare(): Case 1: msgTwin nil",
   987  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
   988  			deviceID:     deviceA,
   989  			key:          key1,
   990  			twin: &dttype.MsgTwin{
   991  				Optional: &optionTrue,
   992  				Metadata: &dttype.TypeMetadata{Type: typeDeleted},
   993  			},
   994  			dealType: RestDealType,
   995  			err:      nil,
   996  		},
   997  		{
   998  			name:         "TestDealTwinCompare(): Case 2: actualOk is false; actualErr is not nil",
   999  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
  1000  			deviceID:     deviceA,
  1001  			key:          key1,
  1002  			twin: &dttype.MsgTwin{
  1003  				Expected: &dttype.TwinValue{Value: &str},
  1004  				Actual:   &dttype.TwinValue{Value: &str},
  1005  				Optional: &optionTrue,
  1006  				Metadata: &dttype.TypeMetadata{Type: typeDeleted},
  1007  			},
  1008  			msgTwin: &dttype.MsgTwin{
  1009  				Actual:   &dttype.TwinValue{Value: &str},
  1010  				Optional: &optionFalse,
  1011  				Metadata: &dttype.TypeMetadata{Type: typeInt},
  1012  			},
  1013  			dealType: RestDealType,
  1014  			err:      errors.New("the value is not int"),
  1015  		},
  1016  		{
  1017  			name:         "TestDealTwinCompare(): Case 3: expectedOk is true; dealVersion() returns false",
  1018  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
  1019  			deviceID:     deviceA,
  1020  			key:          key1,
  1021  			twin: &dttype.MsgTwin{
  1022  				Expected: &dttype.TwinValue{Value: &str},
  1023  				Actual:   &dttype.TwinValue{Value: &str},
  1024  				Optional: &optionTrue,
  1025  				Metadata: &dttype.TypeMetadata{Type: typeString},
  1026  			},
  1027  			msgTwin: &dttype.MsgTwin{
  1028  				Expected:      &dttype.TwinValue{Value: &str},
  1029  				Actual:        &dttype.TwinValue{Value: &str},
  1030  				Optional:      &optionFalse,
  1031  				Metadata:      &dttype.TypeMetadata{Type: typeInt},
  1032  				ActualVersion: &dttype.TwinVersion{},
  1033  			},
  1034  			dealType: SyncDealType,
  1035  			err:      nil,
  1036  		},
  1037  		{
  1038  			name:         "TestDealTwinCompare(): Case 4: actualOk is true; dealVersion() returns false",
  1039  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
  1040  			deviceID:     deviceA,
  1041  			key:          key1,
  1042  			twin: &dttype.MsgTwin{
  1043  				Expected: &dttype.TwinValue{Value: &str},
  1044  				Actual:   &dttype.TwinValue{Value: &str},
  1045  				Optional: &optionTrue,
  1046  				Metadata: &dttype.TypeMetadata{Type: typeDeleted},
  1047  			},
  1048  			msgTwin: &dttype.MsgTwin{
  1049  				Actual:   &dttype.TwinValue{Value: &str},
  1050  				Optional: &optionFalse,
  1051  				Metadata: &dttype.TypeMetadata{Type: typeString},
  1052  			},
  1053  			dealType: SyncDealType,
  1054  			err:      nil,
  1055  		},
  1056  		{
  1057  			name:         "TestDealTwinCompare(): Case 5: expectedOk is true; actualOk is true",
  1058  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
  1059  			deviceID:     deviceA,
  1060  			key:          key1,
  1061  			twin: &dttype.MsgTwin{
  1062  				Optional: &optionTrue,
  1063  				Metadata: &dttype.TypeMetadata{Type: typeDeleted},
  1064  			},
  1065  			msgTwin: &dttype.MsgTwin{
  1066  				Expected:      &dttype.TwinValue{Value: &str},
  1067  				Actual:        &dttype.TwinValue{Value: &str},
  1068  				Optional:      &optionFalse,
  1069  				Metadata:      &dttype.TypeMetadata{Type: typeString},
  1070  				ActualVersion: &dttype.TwinVersion{},
  1071  			},
  1072  			dealType: RestDealType,
  1073  			err:      nil,
  1074  		},
  1075  		{
  1076  			name:         "TestDealTwinCompare(): Case 6: expectedOk is false; actualOk is false",
  1077  			returnResult: &dttype.DealTwinResult{Document: doc, SyncResult: sync, Result: result},
  1078  			deviceID:     deviceA,
  1079  			key:          key1,
  1080  			twin: &dttype.MsgTwin{
  1081  				Optional: &optionTrue,
  1082  				Metadata: &dttype.TypeMetadata{Type: typeDeleted},
  1083  			},
  1084  			msgTwin: &dttype.MsgTwin{
  1085  				Optional:      &optionFalse,
  1086  				ActualVersion: &dttype.TwinVersion{},
  1087  			},
  1088  			dealType: SyncDealType,
  1089  			err:      nil,
  1090  		},
  1091  	}
  1092  	for _, test := range tests {
  1093  		t.Run(test.name, func(t *testing.T) {
  1094  			if err := dealTwinCompare(test.returnResult, test.deviceID, test.key, test.twin, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
  1095  				t.Errorf("DTManager.TestDealTwinCompare() case failed: got = %+v, Want = %+v", err, test.err)
  1096  			}
  1097  		})
  1098  	}
  1099  }
  1100  
  1101  // TestDealTwinAdd is function to test dealTwinAdd
  1102  func TestDealTwinAdd(t *testing.T) {
  1103  	optionTrue := true
  1104  	str := typeString
  1105  	doc := make(map[string]*dttype.TwinDoc)
  1106  	doc[key1] = &dttype.TwinDoc{}
  1107  	sync := make(map[string]*dttype.MsgTwin)
  1108  	sync[key1] = &dttype.MsgTwin{}
  1109  	result := make(map[string]*dttype.MsgTwin)
  1110  	result[key1] = &dttype.MsgTwin{}
  1111  
  1112  	twinDelete := make(map[string]*dttype.MsgTwin)
  1113  	twinDelete[key1] = &dttype.MsgTwin{
  1114  		Metadata: &dttype.TypeMetadata{
  1115  			Type: typeDeleted,
  1116  		},
  1117  	}
  1118  	twinInt := make(map[string]*dttype.MsgTwin)
  1119  	twinInt[key1] = &dttype.MsgTwin{
  1120  		Metadata: &dttype.TypeMetadata{
  1121  			Type: typeInt,
  1122  		},
  1123  	}
  1124  
  1125  	tests := []struct {
  1126  		name         string
  1127  		returnResult *dttype.DealTwinResult
  1128  		deviceID     string
  1129  		key          string
  1130  		twins        map[string]*dttype.MsgTwin
  1131  		msgTwin      *dttype.MsgTwin
  1132  		dealType     int
  1133  		err          error
  1134  	}{
  1135  		{
  1136  			name: "TestDealTwinAdd(): Case 1: msgTwin nil",
  1137  			returnResult: &dttype.DealTwinResult{
  1138  				Document:   doc,
  1139  				SyncResult: sync,
  1140  				Result:     result,
  1141  			},
  1142  			deviceID: deviceA,
  1143  			key:      key1,
  1144  			dealType: RestDealType,
  1145  			err:      errors.New("The request body is wrong"),
  1146  		},
  1147  		{
  1148  			name: "TestDealTwinAdd(): Case 2: msgTwin.Expected is not nil; dealVersion() returns false",
  1149  			returnResult: &dttype.DealTwinResult{
  1150  				Document:   doc,
  1151  				SyncResult: sync,
  1152  				Result:     result,
  1153  			},
  1154  			deviceID: deviceA,
  1155  			key:      key1,
  1156  			twins:    twinDelete,
  1157  			msgTwin: &dttype.MsgTwin{
  1158  				Expected: &dttype.TwinValue{
  1159  					Value: &str,
  1160  				},
  1161  				Actual: &dttype.TwinValue{
  1162  					Value: &str,
  1163  				},
  1164  				Optional: &optionTrue,
  1165  				Metadata: &dttype.TypeMetadata{
  1166  					Type: typeDeleted,
  1167  				},
  1168  				ActualVersion: &dttype.TwinVersion{},
  1169  			},
  1170  			dealType: SyncDealType,
  1171  			err:      nil,
  1172  		},
  1173  		{
  1174  			name: "TestDealTwinAdd(): Case 3: msgTwin.Expected is not nil; ValidateValue() returns error",
  1175  			returnResult: &dttype.DealTwinResult{
  1176  				Document:   doc,
  1177  				SyncResult: sync,
  1178  				Result:     result,
  1179  			},
  1180  			deviceID: deviceA,
  1181  			key:      key1,
  1182  			twins:    twinDelete,
  1183  			msgTwin: &dttype.MsgTwin{
  1184  				Expected: &dttype.TwinValue{
  1185  					Value: &str,
  1186  				},
  1187  				Actual: &dttype.TwinValue{
  1188  					Value: &str,
  1189  				},
  1190  				Optional: &optionTrue,
  1191  				Metadata: &dttype.TypeMetadata{
  1192  					Type: typeInt,
  1193  				},
  1194  				ExpectedVersion: &dttype.TwinVersion{},
  1195  			},
  1196  			dealType: SyncDealType,
  1197  			err:      nil,
  1198  		},
  1199  		{
  1200  			name: "TestDealTwinAdd(): Case 4: msgTwin.Actual is not nil; dealVersion() returns false",
  1201  			returnResult: &dttype.DealTwinResult{
  1202  				Document:   doc,
  1203  				SyncResult: sync,
  1204  				Result:     result,
  1205  			},
  1206  			deviceID: deviceA,
  1207  			key:      key1,
  1208  			twins:    twinDelete,
  1209  			msgTwin: &dttype.MsgTwin{
  1210  				Expected: &dttype.TwinValue{
  1211  					Value: &str,
  1212  				},
  1213  				Actual: &dttype.TwinValue{
  1214  					Value: &str,
  1215  				},
  1216  				Optional: &optionTrue,
  1217  				Metadata: &dttype.TypeMetadata{
  1218  					Type: typeDeleted,
  1219  				},
  1220  				ExpectedVersion: &dttype.TwinVersion{},
  1221  			},
  1222  			dealType: SyncDealType,
  1223  			err:      nil,
  1224  		},
  1225  		{
  1226  			name: "TestDealTwinAdd(): Case 5: msgTwin.Actual is not nil; ValidateValue() returns error; dealType=0",
  1227  			returnResult: &dttype.DealTwinResult{
  1228  				Document:   doc,
  1229  				SyncResult: sync,
  1230  				Result:     result,
  1231  			},
  1232  			deviceID: deviceA,
  1233  			key:      key1,
  1234  			twins:    twinDelete,
  1235  			msgTwin: &dttype.MsgTwin{
  1236  				Actual: &dttype.TwinValue{
  1237  					Value: &str,
  1238  				},
  1239  				Optional: &optionTrue,
  1240  				Metadata: &dttype.TypeMetadata{
  1241  					Type: typeInt,
  1242  				},
  1243  				ExpectedVersion: &dttype.TwinVersion{},
  1244  				ActualVersion:   &dttype.TwinVersion{},
  1245  			},
  1246  			dealType: RestDealType,
  1247  			err:      errors.New("the value is not int"),
  1248  		},
  1249  		{
  1250  			name: "TestDealTwinAdd(): Case 6: msgTwin.Actual is not nil; ValidateValue() returns error; dealType=1",
  1251  			returnResult: &dttype.DealTwinResult{
  1252  				Document:   doc,
  1253  				SyncResult: sync,
  1254  				Result:     result,
  1255  			},
  1256  			deviceID: deviceA,
  1257  			key:      key1,
  1258  			twins:    twinDelete,
  1259  			msgTwin: &dttype.MsgTwin{
  1260  				Actual: &dttype.TwinValue{
  1261  					Value: &str,
  1262  				},
  1263  				Optional: &optionTrue,
  1264  				Metadata: &dttype.TypeMetadata{
  1265  					Type: typeInt,
  1266  				},
  1267  				ExpectedVersion: &dttype.TwinVersion{},
  1268  				ActualVersion:   &dttype.TwinVersion{},
  1269  			},
  1270  			dealType: SyncDealType,
  1271  			err:      nil,
  1272  		},
  1273  		{
  1274  			name: "TestDealTwinAdd(): Case 7: msgTwin.Expected is nil; msgTwin.Actual is nil",
  1275  			returnResult: &dttype.DealTwinResult{
  1276  				Document:   doc,
  1277  				SyncResult: sync,
  1278  				Result:     result,
  1279  			},
  1280  			deviceID: deviceA,
  1281  			key:      key1,
  1282  			twins:    twinInt,
  1283  			msgTwin: &dttype.MsgTwin{
  1284  				ExpectedVersion: &dttype.TwinVersion{},
  1285  				ActualVersion:   &dttype.TwinVersion{},
  1286  			},
  1287  			dealType: RestDealType,
  1288  			err:      nil,
  1289  		},
  1290  		{
  1291  			name: "TestDealTwinAdd(): Case 8: msgTwin.Expected is not nil; msgTwin.Actual is not nil",
  1292  			returnResult: &dttype.DealTwinResult{
  1293  				Document:   doc,
  1294  				SyncResult: sync,
  1295  				Result:     result,
  1296  			},
  1297  			deviceID: deviceA,
  1298  			key:      key1,
  1299  			twins:    twinDelete,
  1300  			msgTwin: &dttype.MsgTwin{
  1301  				Expected: &dttype.TwinValue{
  1302  					Value: &str,
  1303  				},
  1304  				Actual: &dttype.TwinValue{
  1305  					Value: &str,
  1306  				},
  1307  				Optional: &optionTrue,
  1308  				Metadata: &dttype.TypeMetadata{
  1309  					Type: typeDeleted,
  1310  				},
  1311  				ExpectedVersion: &dttype.TwinVersion{},
  1312  				ActualVersion:   &dttype.TwinVersion{},
  1313  			},
  1314  			dealType: SyncDealType,
  1315  			err:      nil,
  1316  		},
  1317  	}
  1318  	for _, test := range tests {
  1319  		t.Run(test.name, func(t *testing.T) {
  1320  			if err := dealTwinAdd(test.returnResult, test.deviceID, test.key, test.twins, test.msgTwin, test.dealType); !reflect.DeepEqual(err, test.err) {
  1321  				t.Errorf("DTManager.TestDealTwinAdd() case failed: got = %+v, Want = %+v", err, test.err)
  1322  			}
  1323  		})
  1324  	}
  1325  }
  1326  
  1327  // TestDealMsgTwin is function to test DealMsgTwin
  1328  func TestDealMsgTwin(t *testing.T) {
  1329  	value := "value"
  1330  	str := typeString
  1331  	optionTrue := true
  1332  	optionFalse := false
  1333  	add := make([]dtclient.DeviceTwin, 0)
  1334  	deletes := make([]dtclient.DeviceDelete, 0)
  1335  	update := make([]dtclient.DeviceTwinUpdate, 0)
  1336  	result := make(map[string]*dttype.MsgTwin)
  1337  	syncResult := make(map[string]*dttype.MsgTwin)
  1338  	syncResultDevice := make(map[string]*dttype.MsgTwin)
  1339  	syncResultDevice[deviceA] = &dttype.MsgTwin{}
  1340  	document := make(map[string]*dttype.TwinDoc)
  1341  	documentDevice := make(map[string]*dttype.TwinDoc)
  1342  	documentDevice[deviceA] = &dttype.TwinDoc{LastState: nil}
  1343  	documentDeviceTwin := make(map[string]*dttype.TwinDoc)
  1344  	documentDeviceTwin[deviceA] = &dttype.TwinDoc{
  1345  		LastState: &dttype.MsgTwin{
  1346  			Expected: &dttype.TwinValue{
  1347  				Value: &str,
  1348  			},
  1349  			Actual: &dttype.TwinValue{
  1350  				Value: &str,
  1351  			},
  1352  			Optional: &optionTrue,
  1353  			Metadata: &dttype.TypeMetadata{
  1354  				Type: typeDeleted,
  1355  			},
  1356  		},
  1357  	}
  1358  
  1359  	msgTwin := make(map[string]*dttype.MsgTwin)
  1360  	msgTwin[deviceB] = &dttype.MsgTwin{
  1361  		Expected: &dttype.TwinValue{
  1362  			Value: &value,
  1363  		},
  1364  		Metadata: &dttype.TypeMetadata{
  1365  			Type: "nil",
  1366  		},
  1367  	}
  1368  	msgTwinDevice := make(map[string]*dttype.MsgTwin)
  1369  	msgTwinDevice[deviceA] = nil
  1370  	msgTwinDeviceTwin := make(map[string]*dttype.MsgTwin)
  1371  	msgTwinDeviceTwin[deviceA] = &dttype.MsgTwin{
  1372  		Expected: &dttype.TwinValue{
  1373  			Value: &str,
  1374  		},
  1375  		Actual: &dttype.TwinValue{
  1376  			Value: &str,
  1377  		},
  1378  		Optional: &optionFalse,
  1379  		Metadata: &dttype.TypeMetadata{
  1380  			Type: typeInt,
  1381  		},
  1382  		ActualVersion: &dttype.TwinVersion{},
  1383  	}
  1384  
  1385  	context := contextFunc(deviceB)
  1386  	twin := make(map[string]*dttype.MsgTwin)
  1387  	twin[deviceA] = &dttype.MsgTwin{
  1388  		Expected: &dttype.TwinValue{
  1389  			Value: &str,
  1390  		},
  1391  		Actual: &dttype.TwinValue{
  1392  			Value: &str,
  1393  		},
  1394  		Optional: &optionTrue,
  1395  		Metadata: &dttype.TypeMetadata{
  1396  			Type: typeDeleted,
  1397  		},
  1398  	}
  1399  	device := dttype.Device{Twin: twin}
  1400  	context.DeviceList.Store(deviceA, &device)
  1401  
  1402  	tests := []struct {
  1403  		name     string
  1404  		context  *dtcontext.DTContext
  1405  		deviceID string
  1406  		msgTwins map[string]*dttype.MsgTwin
  1407  		dealType int
  1408  		want     dttype.DealTwinResult
  1409  	}{
  1410  		{
  1411  			name:     "TestDealMsgTwin(): Case1: invalid device id",
  1412  			context:  &context,
  1413  			deviceID: deviceC,
  1414  			msgTwins: msgTwin,
  1415  			dealType: RestDealType,
  1416  			want: dttype.DealTwinResult{
  1417  				Add:        add,
  1418  				Delete:     deletes,
  1419  				Update:     update,
  1420  				Result:     result,
  1421  				SyncResult: syncResult,
  1422  				Document:   document,
  1423  				Err:        errors.New("invalid device id"),
  1424  			},
  1425  		},
  1426  		{
  1427  			name:     "TestDealMsgTwin(): Case 2: dealTwinCompare error",
  1428  			context:  &context,
  1429  			deviceID: deviceA,
  1430  			msgTwins: msgTwinDeviceTwin,
  1431  			dealType: RestDealType,
  1432  			want: dttype.DealTwinResult{
  1433  				Add:        add,
  1434  				Delete:     deletes,
  1435  				Update:     update,
  1436  				Result:     result,
  1437  				SyncResult: syncResultDevice,
  1438  				Document:   documentDevice,
  1439  				Err:        errors.New("the value is not int"),
  1440  			},
  1441  		},
  1442  		{
  1443  			name:     "TestDealMsgTwin(): Case 3: Success case",
  1444  			context:  &context,
  1445  			deviceID: deviceA,
  1446  			msgTwins: msgTwinDevice,
  1447  			dealType: RestDealType,
  1448  			want: dttype.DealTwinResult{
  1449  				Add:        add,
  1450  				Delete:     deletes,
  1451  				Update:     update,
  1452  				Result:     result,
  1453  				SyncResult: syncResultDevice,
  1454  				Document:   documentDeviceTwin,
  1455  			},
  1456  		},
  1457  	}
  1458  	for _, tt := range tests {
  1459  		t.Run(tt.name, func(t *testing.T) {
  1460  			if got := DealMsgTwin(tt.context, tt.deviceID, tt.msgTwins, tt.dealType); !reflect.DeepEqual(got, tt.want) {
  1461  				t.Errorf("DTManager.DealMsgTwin() case failed: got = %+v, want = %+v", got, tt.want)
  1462  			}
  1463  		})
  1464  	}
  1465  }