dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts@v1.0.2/dtos/requests/device_test.go (about)

     1  //
     2  // Copyright (C) 2020-2023 IOTech Ltd
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  
     6  package requests
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"testing"
    12  
    13  	"dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/common"
    14  	"dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/dtos"
    15  	dtoCommon "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/dtos/common"
    16  	"dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/models"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var testDeviceLabels = []string{"MODBUS", "TEMP"}
    23  var testDeviceLocation = "{40lat;45long}"
    24  var testAutoEvents = []dtos.AutoEvent{
    25  	{SourceName: "TestDevice", Interval: "300ms", OnChange: true},
    26  }
    27  var testAutoEventsWithInvalidFrequency = []dtos.AutoEvent{
    28  	{SourceName: "TestDevice", Interval: "300", OnChange: true},
    29  }
    30  var testProtocols = map[string]dtos.ProtocolProperties{
    31  	"modbus-ip": {
    32  		"Address": "localhost",
    33  		"Port":    "1502",
    34  		"UnitID":  "1",
    35  	},
    36  }
    37  var testAddDevice = AddDeviceRequest{
    38  	BaseRequest: dtoCommon.BaseRequest{
    39  		RequestId:   ExampleUUID,
    40  		Versionable: dtoCommon.NewVersionable(),
    41  	},
    42  	Device: dtos.Device{
    43  		Name:           TestDeviceName,
    44  		ServiceName:    TestDeviceServiceName,
    45  		ProfileName:    TestDeviceProfileName,
    46  		AdminState:     models.Locked,
    47  		OperatingState: models.Up,
    48  		Labels:         testDeviceLabels,
    49  		Location:       testDeviceLocation,
    50  		AutoEvents:     testAutoEvents,
    51  		Protocols:      testProtocols,
    52  	},
    53  }
    54  
    55  var testUpdateDevice = UpdateDeviceRequest{
    56  	BaseRequest: dtoCommon.BaseRequest{
    57  		RequestId:   ExampleUUID,
    58  		Versionable: dtoCommon.NewVersionable(),
    59  	},
    60  	Device: mockUpdateDevice(),
    61  }
    62  
    63  func mockUpdateDevice() dtos.UpdateDevice {
    64  	testId := ExampleUUID
    65  	testName := TestDeviceName
    66  	testDescription := TestDescription
    67  	testAdminState := models.Locked
    68  	testOperatingState := models.Up
    69  	testDeviceServiceName := TestDeviceServiceName
    70  	testProfileName := TestDeviceProfileName
    71  	d := dtos.UpdateDevice{}
    72  	d.Id = &testId
    73  	d.Name = &testName
    74  	d.Description = &testDescription
    75  	d.AdminState = &testAdminState
    76  	d.OperatingState = &testOperatingState
    77  	d.ServiceName = &testDeviceServiceName
    78  	d.ProfileName = &testProfileName
    79  	d.Labels = testDeviceLabels
    80  	d.Location = testDeviceLocation
    81  	d.AutoEvents = testAutoEvents
    82  	d.Protocols = testProtocols
    83  	return d
    84  }
    85  
    86  func TestAddDeviceRequest_Validate(t *testing.T) {
    87  	emptyString := " "
    88  	valid := testAddDevice
    89  	invalidFrequency := testAddDevice
    90  	invalidFrequency.Device.AutoEvents = testAutoEventsWithInvalidFrequency
    91  	noReqId := testAddDevice
    92  	noReqId.RequestId = ""
    93  	invalidReqId := testAddDevice
    94  	invalidReqId.RequestId = "abc"
    95  	noDeviceName := testAddDevice
    96  	noDeviceName.Device.Name = emptyString
    97  	noServiceName := testAddDevice
    98  	noServiceName.Device.ServiceName = emptyString
    99  	noProfileName := testAddDevice
   100  	noProfileName.Device.ProfileName = emptyString
   101  	noProtocols := testAddDevice
   102  	noProtocols.Device.Protocols = map[string]dtos.ProtocolProperties{}
   103  	noAutoEventFrequency := testAddDevice
   104  	noAutoEventFrequency.Device.AutoEvents = []dtos.AutoEvent{
   105  		{SourceName: "TestDevice", OnChange: true},
   106  	}
   107  	noAutoEventResource := testAddDevice
   108  	noAutoEventResource.Device.AutoEvents = []dtos.AutoEvent{
   109  		{Interval: "300ms", OnChange: true},
   110  	}
   111  	tests := []struct {
   112  		name        string
   113  		Device      AddDeviceRequest
   114  		expectError bool
   115  	}{
   116  		{"valid AddDeviceRequest", valid, false},
   117  		{"invalid AddDeviceRequest, invalid autoEvent frequency", invalidFrequency, true},
   118  		{"valid AddDeviceRequest, no Request Id", noReqId, false},
   119  		{"invalid AddDeviceRequest, Request Id is not an uuid", invalidReqId, true},
   120  		{"invalid AddDeviceRequest, no DeviceName", noDeviceName, true},
   121  		{"invalid AddDeviceRequest, no ServiceName", noServiceName, true},
   122  		{"invalid AddDeviceRequest, no ProfileName", noProfileName, true},
   123  		{"invalid AddDeviceRequest, no Protocols", noProtocols, true},
   124  		{"invalid AddDeviceRequest, no AutoEvent frequency", noAutoEventFrequency, true},
   125  		{"invalid AddDeviceRequest, no AutoEvent resource", noAutoEventResource, true},
   126  	}
   127  	for _, tt := range tests {
   128  		t.Run(tt.name, func(t *testing.T) {
   129  			err := tt.Device.Validate()
   130  			assert.Equal(t, tt.expectError, err != nil, "Unexpected addDeviceRequest validation result.", err)
   131  		})
   132  	}
   133  
   134  	type testForNameField struct {
   135  		name        string
   136  		Device      AddDeviceRequest
   137  		expectError bool
   138  	}
   139  
   140  	deviceNameWithUnreservedChars := testAddDevice
   141  	deviceNameWithUnreservedChars.Device.Name = nameWithUnreservedChars
   142  	profileNameWithUnreservedChars := testAddDevice
   143  	profileNameWithUnreservedChars.Device.ProfileName = nameWithUnreservedChars
   144  	serviceNameWithUnreservedChars := testAddDevice
   145  	serviceNameWithUnreservedChars.Device.ServiceName = nameWithUnreservedChars
   146  
   147  	// Following tests verify if name fields containing unreserved characters should pass edgex-dto-rfc3986-unreserved-chars check
   148  	testsForNameFields := []testForNameField{
   149  		{"Valid AddDeviceRequest with device name containing unreserved chars", deviceNameWithUnreservedChars, false},
   150  		{"Valid AddDeviceRequest with profile name containing unreserved chars", profileNameWithUnreservedChars, false},
   151  		{"Valid AddDeviceRequest with service name containing unreserved chars", serviceNameWithUnreservedChars, false},
   152  	}
   153  
   154  	// Following tests verify if name fields containing reserved characters should not be detected with an error
   155  	for _, n := range namesWithReservedChar {
   156  		deviceNameWithReservedChar := testAddDevice
   157  		deviceNameWithReservedChar.Device.Name = n
   158  		profileNameWithReservedChar := testAddDevice
   159  		profileNameWithReservedChar.Device.ProfileName = n
   160  		serviceNameWithReservedChar := testAddDevice
   161  		serviceNameWithReservedChar.Device.ServiceName = n
   162  
   163  		testsForNameFields = append(testsForNameFields,
   164  			testForNameField{"Valid AddDeviceRequest with device name containing reserved char", deviceNameWithReservedChar, false},
   165  			testForNameField{"Valid AddDeviceRequest with device name containing reserved char", profileNameWithReservedChar, false},
   166  			testForNameField{"Valid AddDeviceRequest with device name containing reserved char", serviceNameWithReservedChar, false},
   167  		)
   168  	}
   169  
   170  	for _, tt := range testsForNameFields {
   171  		t.Run(tt.name, func(t *testing.T) {
   172  			err := tt.Device.Validate()
   173  			if tt.expectError {
   174  				assert.Error(t, err, fmt.Sprintf("expect error but not : %s", tt.name))
   175  			} else {
   176  				assert.NoError(t, err, fmt.Sprintf("unexpected error occurs : %s", tt.name))
   177  			}
   178  		})
   179  	}
   180  }
   181  
   182  func TestAddDevice_UnmarshalJSON(t *testing.T) {
   183  	valid := testAddDevice
   184  	resultTestBytes, _ := json.Marshal(testAddDevice)
   185  	type args struct {
   186  		data []byte
   187  	}
   188  	tests := []struct {
   189  		name      string
   190  		addDevice AddDeviceRequest
   191  		args      args
   192  		wantErr   bool
   193  	}{
   194  		{"unmarshal AddDeviceRequest with success", valid, args{resultTestBytes}, false},
   195  		{"unmarshal invalid AddDeviceRequest, empty data", AddDeviceRequest{}, args{[]byte{}}, true},
   196  		{"unmarshal invalid AddDeviceRequest, string data", AddDeviceRequest{}, args{[]byte("Invalid AddDeviceRequest")}, true},
   197  	}
   198  	fmt.Println(string(resultTestBytes))
   199  	for _, tt := range tests {
   200  		t.Run(tt.name, func(t *testing.T) {
   201  			var expected = tt.addDevice
   202  			err := tt.addDevice.UnmarshalJSON(tt.args.data)
   203  			if tt.wantErr {
   204  				require.Error(t, err)
   205  			} else {
   206  				require.NoError(t, err)
   207  				assert.Equal(t, expected, tt.addDevice, "Unmarshal did not result in expected AddDeviceRequest.")
   208  			}
   209  		})
   210  	}
   211  }
   212  
   213  func Test_AddDeviceReqToDeviceModels(t *testing.T) {
   214  	requests := []AddDeviceRequest{testAddDevice}
   215  	expectedDeviceModel := []models.Device{
   216  		{
   217  			Name:           TestDeviceName,
   218  			ServiceName:    TestDeviceServiceName,
   219  			ProfileName:    TestDeviceProfileName,
   220  			AdminState:     models.Locked,
   221  			OperatingState: models.Up,
   222  			Labels:         testDeviceLabels,
   223  			Location:       testDeviceLocation,
   224  			AutoEvents: []models.AutoEvent{
   225  				{SourceName: "TestDevice", Interval: "300ms", OnChange: true},
   226  			},
   227  			Protocols: map[string]models.ProtocolProperties{
   228  				"modbus-ip": {
   229  					"Address": "localhost",
   230  					"Port":    "1502",
   231  					"UnitID":  "1",
   232  				},
   233  			},
   234  		},
   235  	}
   236  	resultModels := AddDeviceReqToDeviceModels(requests)
   237  	assert.Equal(t, expectedDeviceModel, resultModels, "AddDeviceReqToDeviceModels did not result in expected Device model.")
   238  }
   239  
   240  func TestUpdateDeviceRequest_UnmarshalJSON(t *testing.T) {
   241  	valid := testUpdateDevice
   242  	resultTestBytes, _ := json.Marshal(testUpdateDevice)
   243  	type args struct {
   244  		data []byte
   245  	}
   246  	tests := []struct {
   247  		name    string
   248  		req     UpdateDeviceRequest
   249  		args    args
   250  		wantErr bool
   251  	}{
   252  		{"unmarshal UpdateDeviceRequest with success", valid, args{resultTestBytes}, false},
   253  		{"unmarshal invalid UpdateDeviceRequest, empty data", UpdateDeviceRequest{}, args{[]byte{}}, true},
   254  		{"unmarshal invalid UpdateDeviceRequest, string data", UpdateDeviceRequest{}, args{[]byte("Invalid UpdateDeviceRequest")}, true},
   255  	}
   256  	fmt.Println(string(resultTestBytes))
   257  	for _, tt := range tests {
   258  		t.Run(tt.name, func(t *testing.T) {
   259  			var expected = tt.req
   260  			err := tt.req.UnmarshalJSON(tt.args.data)
   261  			if tt.wantErr {
   262  				require.Error(t, err)
   263  			} else {
   264  				require.NoError(t, err)
   265  				assert.Equal(t, expected, tt.req, "Unmarshal did not result in expected UpdateDeviceRequest.", err)
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  func TestUpdateDeviceRequest_Validate(t *testing.T) {
   272  	emptyString := " "
   273  	invalidUUID := "invalidUUID"
   274  
   275  	valid := testUpdateDevice
   276  	noReqId := valid
   277  	noReqId.RequestId = ""
   278  	invalidReqId := valid
   279  	invalidReqId.RequestId = invalidUUID
   280  
   281  	// id
   282  	validOnlyId := valid
   283  	validOnlyId.Device.Name = nil
   284  	invalidId := valid
   285  	invalidId.Device.Id = &invalidUUID
   286  	// name
   287  	validOnlyName := valid
   288  	validOnlyName.Device.Id = nil
   289  	nameAndEmptyId := valid
   290  	nameAndEmptyId.Device.Id = &emptyString
   291  	invalidEmptyName := valid
   292  	invalidEmptyName.Device.Name = &emptyString
   293  	// no id and name
   294  	noIdAndName := valid
   295  	noIdAndName.Device.Id = nil
   296  	noIdAndName.Device.Name = nil
   297  	// description
   298  	validNilDescription := valid
   299  	validNilDescription.Device.Description = nil
   300  	invalidEmptyDescription := valid
   301  	invalidEmptyDescription.Device.Description = &emptyString
   302  	// ServiceName
   303  	validNilServiceName := valid
   304  	validNilServiceName.Device.ServiceName = nil
   305  	invalidEmptyServiceName := valid
   306  	invalidEmptyServiceName.Device.ServiceName = &emptyString
   307  	// ProfileName
   308  	validNilProfileName := valid
   309  	validNilProfileName.Device.ProfileName = nil
   310  	invalidEmptyProfileName := valid
   311  	invalidEmptyProfileName.Device.ProfileName = &emptyString
   312  
   313  	invalidState := "invalid state"
   314  	invalidAdminState := valid
   315  	invalidAdminState.Device.AdminState = &invalidState
   316  	invalidOperatingState := valid
   317  	invalidOperatingState.Device.OperatingState = &invalidState
   318  	invalidFrequency := valid
   319  	invalidFrequency.Device.AutoEvents = testAutoEventsWithInvalidFrequency
   320  	emptyProtocols := valid
   321  	emptyProtocols.Device.Protocols = map[string]dtos.ProtocolProperties{}
   322  
   323  	tests := []struct {
   324  		name        string
   325  		req         UpdateDeviceRequest
   326  		expectError bool
   327  	}{
   328  		{"valid", valid, false},
   329  		{"valid, no Request Id", noReqId, false},
   330  		{"invalid, Request Id is not an uuid", invalidReqId, true},
   331  
   332  		{"valid, only id", validOnlyId, false},
   333  		{"invalid, invalid Id", invalidId, true},
   334  		{"valid, only name", validOnlyName, false},
   335  		{"valid, name and empty Id", nameAndEmptyId, false},
   336  		{"invalid, empty name", invalidEmptyName, true},
   337  
   338  		{"invalid, no Id and name", noIdAndName, true},
   339  
   340  		{"valid, nil description", validNilDescription, false},
   341  
   342  		{"valid, nil service name", validNilServiceName, false},
   343  		{"invalid, empty service name", invalidEmptyServiceName, true},
   344  
   345  		{"valid, nil profile name", validNilProfileName, false},
   346  		{"invalid, empty profile name", invalidEmptyProfileName, true},
   347  
   348  		{"invalid, invalid admin state", invalidAdminState, true},
   349  		{"invalid, invalid operating state", invalidOperatingState, true},
   350  		{"invalid, invalid autoEvent frequency", invalidFrequency, true},
   351  
   352  		{"invalid, empty protocols", emptyProtocols, true},
   353  	}
   354  	for _, tt := range tests {
   355  		t.Run(tt.name, func(t *testing.T) {
   356  			err := tt.req.Validate()
   357  			assert.Equal(t, tt.expectError, err != nil, "Unexpected updateDeviceRequest validation result.", err)
   358  		})
   359  	}
   360  }
   361  
   362  func TestUpdateDeviceRequest_UnmarshalJSON_NilField(t *testing.T) {
   363  	reqJson := `{
   364  		"apiVersion" : "v3",
   365          "requestId":"7a1707f0-166f-4c4b-bc9d-1d54c74e0137",
   366  		"device":{"apiVersion":"v3", "name":"TestDevice"}
   367  	}`
   368  	var req UpdateDeviceRequest
   369  
   370  	err := req.UnmarshalJSON([]byte(reqJson))
   371  
   372  	require.NoError(t, err)
   373  	// Nil field checking is used to update with patch
   374  	assert.Nil(t, req.Device.Description)
   375  	assert.Nil(t, req.Device.AdminState)
   376  	assert.Nil(t, req.Device.OperatingState)
   377  	assert.Nil(t, req.Device.ServiceName)
   378  	assert.Nil(t, req.Device.ProfileName)
   379  	assert.Nil(t, req.Device.Labels)
   380  	assert.Nil(t, req.Device.Location)
   381  	assert.Nil(t, req.Device.AutoEvents)
   382  	assert.Nil(t, req.Device.Protocols)
   383  	assert.Nil(t, req.Device.Tags)
   384  	assert.Nil(t, req.Device.Properties)
   385  }
   386  
   387  func TestUpdateDeviceRequest_UnmarshalJSON_EmptySlice(t *testing.T) {
   388  	reqJson := `{
   389  		"apiVersion" : "v3",
   390          "requestId":"7a1707f0-166f-4c4b-bc9d-1d54c74e0137",
   391  		"device":{
   392  			"apiVersion":"v3",
   393  			"name":"TestDevice",
   394  			"labels":[],
   395  			"autoEvents":[]
   396  		}
   397  	}`
   398  	var req UpdateDeviceRequest
   399  
   400  	err := req.UnmarshalJSON([]byte(reqJson))
   401  
   402  	require.NoError(t, err)
   403  	// Empty slice is used to remove the data
   404  	assert.NotNil(t, req.Device.Labels)
   405  	assert.NotNil(t, req.Device.AutoEvents)
   406  }
   407  
   408  func TestReplaceDeviceModelFieldsWithDTO(t *testing.T) {
   409  	device := models.Device{
   410  		Id:   "7a1707f0-166f-4c4b-bc9d-1d54c74e0137",
   411  		Name: "test device profile",
   412  	}
   413  	patch := mockUpdateDevice()
   414  
   415  	ReplaceDeviceModelFieldsWithDTO(&device, patch)
   416  
   417  	assert.Equal(t, TestDescription, device.Description)
   418  	assert.Equal(t, models.AdminState(models.Locked), device.AdminState)
   419  	assert.Equal(t, models.OperatingState(models.Up), device.OperatingState)
   420  	assert.Equal(t, TestDeviceServiceName, device.ServiceName)
   421  	assert.Equal(t, TestDeviceProfileName, device.ProfileName)
   422  	assert.Equal(t, testLabels, device.Labels)
   423  	assert.Equal(t, testDeviceLocation, device.Location)
   424  	assert.Equal(t, dtos.ToAutoEventModels(testAutoEvents), device.AutoEvents)
   425  	assert.Equal(t, dtos.ToProtocolModels(testProtocols), device.Protocols)
   426  }
   427  
   428  func TestNewAddDeviceRequest(t *testing.T) {
   429  	expectedApiVersion := common.ApiVersion
   430  
   431  	actual := NewAddDeviceRequest(dtos.Device{})
   432  
   433  	assert.Equal(t, expectedApiVersion, actual.ApiVersion)
   434  }
   435  
   436  func TestNewUpdateDeviceRequest(t *testing.T) {
   437  	expectedApiVersion := common.ApiVersion
   438  
   439  	actual := NewUpdateDeviceRequest(dtos.UpdateDevice{})
   440  
   441  	assert.Equal(t, expectedApiVersion, actual.ApiVersion)
   442  }