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 }