From 209e74932dda781693116605be6926da1c3ce377 Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Tue, 18 Oct 2022 15:28:05 +0200 Subject: [PATCH 1/9] Sync GatewayIDs from Azure --- .../Server/Jobs/SyncGatewayIDJob.cs | 53 +++++++++++++++++++ .../Server/Services/ExternalDeviceService.cs | 21 ++++++++ .../Server/Services/IExternalDeviceService.cs | 2 + src/AzureIoTHub.Portal/Server/Startup.cs | 9 ++++ .../Shared/Models/v1.0/GatewayIdList.cs | 12 +++++ 5 files changed, 97 insertions(+) create mode 100644 src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs create mode 100644 src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs diff --git a/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs new file mode 100644 index 000000000..8cfd7446a --- /dev/null +++ b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs @@ -0,0 +1,53 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AzureIoTHub.Portal.Server.Jobs +{ + using System; + using System.Threading.Tasks; + using AzureIoTHub.Portal.Server.Services; + using AzureIoTHub.Portal.Shared.Models.v1._0; + using Microsoft.Extensions.Logging; + using Quartz; + + [DisallowConcurrentExecution] + public class SyncGatewayIDJob : IJob + { + private readonly IExternalDeviceService externalDeviceService; + private readonly GatewayIdList gatewayIdList; + + private readonly ILogger logger; + + public SyncGatewayIDJob( + IExternalDeviceService externalDeviceService, + GatewayIdList gatewayIdList, + ILogger logger) + { + this.externalDeviceService = externalDeviceService; + this.gatewayIdList = gatewayIdList; + this.logger = logger; + } + + public async Task Execute(IJobExecutionContext context) + { + try + { + this.logger.LogInformation("Start of sync job"); + + await SyncGatewayID(); + + this.logger.LogInformation("End of sync job"); + } + catch (Exception e) + { + this.logger.LogError(e, "Sync job has failed"); + } + } + + private async Task SyncGatewayID() + { + var test = await this.externalDeviceService.GetAllGatewayID(); + this.gatewayIdList.GatewayIds = test; + } + } +} diff --git a/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs b/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs index efe7c6d44..1ce1e5562 100644 --- a/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs +++ b/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs @@ -689,5 +689,26 @@ public async Task CreateNewTwinFromDeviceId(string deviceId) DeviceId = deviceId }; } + + public async Task> GetAllGatewayID() + { + var query = this.registryManager.CreateQuery($"SELECT DeviceID FROM devices.modules WHERE devices.modules.moduleId = 'LoRaWanNetworkSrvModule'"); + var list = new List(); + + while (query.HasMoreResults) + { + try + { + var value = await query.GetNextAsJsonAsync(); + list.Add(JObject.Parse(value.SingleOrDefault())["deviceId"]?.ToString()); + } + catch (Exception e) + { + throw new InternalServerErrorException($"Unable to get GatewayIDs", e); + } + } + + return list; + } } } diff --git a/src/AzureIoTHub.Portal/Server/Services/IExternalDeviceService.cs b/src/AzureIoTHub.Portal/Server/Services/IExternalDeviceService.cs index e07d65849..274f32f34 100644 --- a/src/AzureIoTHub.Portal/Server/Services/IExternalDeviceService.cs +++ b/src/AzureIoTHub.Portal/Server/Services/IExternalDeviceService.cs @@ -68,5 +68,7 @@ Task> GetAllEdgeDevice( Task RetrieveLastConfiguration(Twin twin); Task CreateNewTwinFromDeviceId(string deviceId); + + Task> GetAllGatewayID(); } } diff --git a/src/AzureIoTHub.Portal/Server/Startup.cs b/src/AzureIoTHub.Portal/Server/Startup.cs index 84a3653d4..7400b4425 100644 --- a/src/AzureIoTHub.Portal/Server/Startup.cs +++ b/src/AzureIoTHub.Portal/Server/Startup.cs @@ -105,6 +105,7 @@ public void ConfigureServices(IServiceCollection services) _ = services.AddSingleton(configuration); _ = services.AddSingleton(new PortalMetric()); + _ = services.AddSingleton(new GatewayIdList()); _ = services.AddRazorPages(); @@ -332,6 +333,14 @@ Specify the authorization token got from your IDP as a header. .WithSimpleSchedule(s => s .WithIntervalInMinutes(configuration.SyncDatabaseJobRefreshIntervalInMinutes) .RepeatForever())); + + _ = q.AddJob(j => j.WithIdentity(nameof(SyncGatewayIDJob))) + .AddTrigger(t => t + .WithIdentity($"{nameof(SyncGatewayIDJob)}") + .ForJob(nameof(SyncGatewayIDJob)) + .WithSimpleSchedule(s => s + .WithIntervalInMinutes(configuration.SyncDatabaseJobRefreshIntervalInMinutes) + .RepeatForever())); }); // Add the Quartz.NET hosted service diff --git a/src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs b/src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs new file mode 100644 index 000000000..7f895e8f0 --- /dev/null +++ b/src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs @@ -0,0 +1,12 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AzureIoTHub.Portal.Shared.Models.v1._0 +{ + using System.Collections.Generic; + + public class GatewayIdList + { + public List GatewayIds { get; set; } + } +} From 7de7afd23cd31e72806b4db311134861be99e54b Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Wed, 19 Oct 2022 09:34:13 +0200 Subject: [PATCH 2/9] Add MudBlazorAutocomplete for GatewayID on LoRaWAN Device pages --- .../Devices/LoRaWAN/CreateLoraDevice.razor | 43 +++++++++++++++++ .../Devices/LoRaWAN/EditLoraDevice.razor | 47 +++++++++++++++++-- .../Services/ILoRaWanDeviceClientService.cs | 3 ++ .../Services/LoRaWanDeviceClientService.cs | 7 +++ .../v1.0/LoRaWAN/LoRaWANDevicesController.cs | 12 ++++- .../Server/Jobs/SyncGatewayIDJob.cs | 13 +++-- src/AzureIoTHub.Portal/Server/Startup.cs | 2 +- .../LoRaGatewayIDList.cs} | 2 +- 8 files changed, 114 insertions(+), 15 deletions(-) rename src/AzureIoTHub.Portal/Shared/Models/v1.0/{GatewayIdList.cs => LoRaWAN/LoRaGatewayIDList.cs} (89%) diff --git a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor index 3acb7b778..4af4917ce 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor @@ -1,6 +1,7 @@ @using AzureIoTHub.Portal.Client.Validators @using AzureIoTHub.Portal.Models.v10.LoRaWAN @using AzureIoTHub.Portal.Models.v10 +@inject ILoRaWanDeviceClientService LoRaWanDeviceClientService @@ -40,6 +41,22 @@ } + + + + @@ -48,6 +65,9 @@ @code { + [CascadingParameter] + public Error Error { get; set; } + [Parameter] public LoRaDeviceDetails LoRaDevice { get; set; } @@ -57,4 +77,27 @@ [Parameter] public LoRaDeviceModelDto LoraModelDto { get; set; } + private List GatewayIdList = new(); + + protected override async Task OnInitializedAsync() + { + try + { + GatewayIdList = (await LoRaWanDeviceClientService.GetGatewayIdList()).GatewayIds; + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + } + + private async Task> SearchGatewayID(string value) + { + // if text is null or empty, show complete list + if (string.IsNullOrEmpty(value)) + return GatewayIdList; + + return GatewayIdList + .Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase)); + } } diff --git a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor index 6c29cd71e..4aac3faf5 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor @@ -54,7 +54,7 @@ - - + + + @@ -157,8 +170,8 @@ @code { [CascadingParameter] - public Error Error {get; set;} - + public Error Error { get; set; } + [Parameter] public LoRaDeviceDetails LoRaDevice { get; set; } @@ -173,6 +186,30 @@ private bool isProcessing; + private List GatewayIdList = new(); + + protected override async Task OnInitializedAsync() + { + try + { + GatewayIdList = (await LoRaWanDeviceClientService.GetGatewayIdList()).GatewayIds; + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + } + + private async Task> SearchGatewayID(string value) + { + // if text is null or empty, show complete list + if (string.IsNullOrEmpty(value)) + return GatewayIdList; + + return GatewayIdList + .Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase)); + } + private async Task ExecuteMethod(DeviceModelCommandDto method) { try diff --git a/src/AzureIoTHub.Portal/Client/Services/ILoRaWanDeviceClientService.cs b/src/AzureIoTHub.Portal/Client/Services/ILoRaWanDeviceClientService.cs index 7ac7b39de..d405c9d1c 100644 --- a/src/AzureIoTHub.Portal/Client/Services/ILoRaWanDeviceClientService.cs +++ b/src/AzureIoTHub.Portal/Client/Services/ILoRaWanDeviceClientService.cs @@ -5,6 +5,7 @@ namespace AzureIoTHub.Portal.Client.Services { using System.Threading.Tasks; using AzureIoTHub.Portal.Models.v10.LoRaWAN; + using AzureIoTHub.Portal.Shared.Models.v1._0; public interface ILoRaWanDeviceClientService { @@ -15,5 +16,7 @@ public interface ILoRaWanDeviceClientService Task UpdateDevice(LoRaDeviceDetails device); Task ExecuteCommand(string deviceId, string commandId); + + Task GetGatewayIdList(); } } diff --git a/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs b/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs index 9a220764d..29fea8346 100644 --- a/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs +++ b/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs @@ -3,9 +3,11 @@ namespace AzureIoTHub.Portal.Client.Services { + using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; + using AzureIoTHub.Portal.Shared.Models.v1._0; using Portal.Models.v10.LoRaWAN; public class LoRaWanDeviceClientService : ILoRaWanDeviceClientService @@ -36,5 +38,10 @@ public Task ExecuteCommand(string deviceId, string commandId) { return this.http.PostAsJsonAsync($"api/lorawan/devices/{deviceId}/_command/{commandId}", string.Empty); } + + public Task GetGatewayIdList() + { + return this.http.GetFromJsonAsync($"api/lorawan/devices/gateways"); + } } } diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs index 913f5bc50..3eec0907a 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs @@ -4,6 +4,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10 { using System.Threading.Tasks; + using AzureIoTHub.Portal.Shared.Models.v1._0; using Filters; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -21,14 +22,17 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10 public class LoRaWANDevicesController : DevicesControllerBase { private readonly ILoRaWANCommandService loRaWanCommandService; + private readonly LoRaGatewayIDList gatewayIdList; public LoRaWANDevicesController( ILogger logger, ILoRaWANCommandService loRaWanCommandService, - IDeviceService deviceService) + IDeviceService deviceService, + LoRaGatewayIDList gatewayIdList) : base(logger, deviceService) { this.loRaWanCommandService = loRaWanCommandService; + this.gatewayIdList = gatewayIdList; } /// @@ -112,5 +116,11 @@ public override Task> GetCredentials(string { return Task.FromResult>(NotFound()); } + + [HttpGet("gateways", Name = "Get Gateways")] + public ActionResult GetGateways() + { + return this.gatewayIdList; + } } } diff --git a/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs index 8cfd7446a..5854800fb 100644 --- a/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs +++ b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs @@ -14,13 +14,13 @@ namespace AzureIoTHub.Portal.Server.Jobs public class SyncGatewayIDJob : IJob { private readonly IExternalDeviceService externalDeviceService; - private readonly GatewayIdList gatewayIdList; + private readonly LoRaGatewayIDList gatewayIdList; private readonly ILogger logger; public SyncGatewayIDJob( IExternalDeviceService externalDeviceService, - GatewayIdList gatewayIdList, + LoRaGatewayIDList gatewayIdList, ILogger logger) { this.externalDeviceService = externalDeviceService; @@ -32,22 +32,21 @@ public async Task Execute(IJobExecutionContext context) { try { - this.logger.LogInformation("Start of sync job"); + this.logger.LogInformation("Start of sync GatewayID job"); await SyncGatewayID(); - this.logger.LogInformation("End of sync job"); + this.logger.LogInformation("End of sync GatewayID job"); } catch (Exception e) { - this.logger.LogError(e, "Sync job has failed"); + this.logger.LogError(e, "Sync GatewayID job has failed"); } } private async Task SyncGatewayID() { - var test = await this.externalDeviceService.GetAllGatewayID(); - this.gatewayIdList.GatewayIds = test; + this.gatewayIdList.GatewayIds = await this.externalDeviceService.GetAllGatewayID(); } } } diff --git a/src/AzureIoTHub.Portal/Server/Startup.cs b/src/AzureIoTHub.Portal/Server/Startup.cs index 7400b4425..90bb698a5 100644 --- a/src/AzureIoTHub.Portal/Server/Startup.cs +++ b/src/AzureIoTHub.Portal/Server/Startup.cs @@ -105,7 +105,7 @@ public void ConfigureServices(IServiceCollection services) _ = services.AddSingleton(configuration); _ = services.AddSingleton(new PortalMetric()); - _ = services.AddSingleton(new GatewayIdList()); + _ = services.AddSingleton(new LoRaGatewayIDList()); _ = services.AddRazorPages(); diff --git a/src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs b/src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaGatewayIDList.cs similarity index 89% rename from src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs rename to src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaGatewayIDList.cs index 7f895e8f0..4b6529953 100644 --- a/src/AzureIoTHub.Portal/Shared/Models/v1.0/GatewayIdList.cs +++ b/src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaGatewayIDList.cs @@ -5,7 +5,7 @@ namespace AzureIoTHub.Portal.Shared.Models.v1._0 { using System.Collections.Generic; - public class GatewayIdList + public class LoRaGatewayIDList { public List GatewayIds { get; set; } } From 970fb3a3951ee3a1bd2d3cb247055b27e766019d Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Wed, 19 Oct 2022 11:30:31 +0200 Subject: [PATCH 3/9] Fix compilation issue in unit test --- .../v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs index 2d4eece2d..961053799 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs @@ -47,10 +47,16 @@ public void SetUp() private LoRaWANDevicesController CreateLoRaWANDevicesController() { + var loRaGatewayIDList = new LoRaGatewayIDList + { + GatewayIds = new List(){ Guid.NewGuid().ToString(), Guid.NewGuid().ToString() } + }; + return new LoRaWANDevicesController( this.mockLogger.Object, this.mockLoRaWANCommandService.Object, - this.mockDeviceService.Object) + this.mockDeviceService.Object, + loRaGatewayIDList) { Url = this.mockUrlHelper.Object }; From f54a7c0c70bc29a1a84e73bfac6f6ce933d4246e Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Wed, 19 Oct 2022 11:44:08 +0200 Subject: [PATCH 4/9] Fix missing Setup in unit tests on Create/EditLoraDevice pages --- .../Devices/LoRaWan/CreateLoraDeviceTests.cs | 13 +++++++++++ .../Devices/LoRaWan/EditLoraDeviceTests.cs | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs index 1398d1999..b55a76f15 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs @@ -5,10 +5,13 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices { using System; using AzureIoTHub.Portal.Client.Pages.Devices.LoRaWAN; + using AzureIoTHub.Portal.Client.Services; using AzureIoTHub.Portal.Client.Validators; using AzureIoTHub.Portal.Models.v10.LoRaWAN; + using AzureIoTHub.Portal.Shared.Models.v1._0; using Bunit; using Microsoft.Extensions.DependencyInjection; + using Moq; using MudBlazor.Services; using NUnit.Framework; using UnitTests.Bases; @@ -17,10 +20,14 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices [TestFixture] public class CreateLoraDeviceTests : BlazorUnitTest { + private Mock mockLoRaWanDeviceClientService; public override void Setup() { base.Setup(); + this.mockLoRaWanDeviceClientService = MockRepository.Create(); + + _ = Services.AddSingleton(this.mockLoRaWanDeviceClientService.Object); Services.Add(new ServiceDescriptor(typeof(IResizeObserver), new MockResizeObserver())); } @@ -44,6 +51,9 @@ public void WhenUseOTAAShouldDisplayOTAATextboxes() var validator = new LoRaDeviceDetailsValidator(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( @@ -76,6 +86,9 @@ public void WhenNotUseOTAAShouldDisplayABPTextboxes() }; var validator = new LoRaDeviceDetailsValidator(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoRaDevice), deviceDetails), diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs index ed6bfadaf..741103880 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs @@ -20,6 +20,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices using UnitTests.Mocks; using System.Linq; using System.Threading.Tasks; + using AzureIoTHub.Portal.Shared.Models.v1._0; [TestFixture] public class EditLoraDeviceTests : BlazorUnitTest @@ -58,6 +59,9 @@ public void WhenUseOTAAShouldDisplayOTAATextboxes() var validator = new LoRaDeviceDetailsValidator(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), @@ -89,6 +93,9 @@ public void WhenNotUseOTAAShouldDisplayABPTextboxes() }; var validator = new LoRaDeviceDetailsValidator(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), @@ -122,6 +129,9 @@ public void EditLoRaDevicePageShouldBeRenderedProperly() }; var validator = new LoRaDeviceDetailsValidator(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), @@ -153,6 +163,9 @@ public void WhenDeviceNeverConnectedCommandsShouldBeDisabled() ModelId = model.ModelId }; + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), device), @@ -187,6 +200,9 @@ public void WhenDeviceAlteadyConnectedCommandsShouldBeEnabled() AlreadyLoggedInOnce = true }; + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), device), @@ -226,6 +242,9 @@ public void WhenClickToSendCommandShouldExecuteCommandToService() _ = this.mockSnackbarService.Setup(c => c.Add(It.IsAny(), Severity.Success, It.IsAny>())) .Returns((Snackbar)null); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + // Act var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), device), @@ -261,6 +280,8 @@ public void EditLoRaDeviceDetailPageShouldBeRenderedProperly() var validator = new LoRaDeviceDetailsValidator(); var LoraDevice = new LoRaDeviceDetails(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); // Act var cut = RenderComponent( @@ -295,6 +316,8 @@ public void EditLoRaDeviceDetailPageWithReportedProperties() var validator = new LoRaDeviceDetailsValidator(); var LoraDevice = new LoRaDeviceDetails(); + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); // Act var cut = RenderComponent( From a170491d11b57996f003d8509ae8ac56f849284b Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Wed, 19 Oct 2022 16:01:58 +0200 Subject: [PATCH 5/9] Change GetAllGatewayID() behavior and add corresponding unit tests --- .../Services/ExternalDeviceServiceTests.cs | 56 +++++++++++++++++++ .../Server/Services/ExternalDeviceService.cs | 18 +++--- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ExternalDeviceServiceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ExternalDeviceServiceTests.cs index dbbedaa69..ac6354b99 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ExternalDeviceServiceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ExternalDeviceServiceTests.cs @@ -1863,5 +1863,61 @@ public void WhenDeviceTwinIsNullGetEdgeDeviceCredentialsShouldThrowResourceNotFo this.mockRepository.VerifyAll(); } + + + [Test] + public async Task GetAllGatewayIDExpectedBehaviorShouldReturnList() + { + // Arrange + var service = CreateService(); + + var mockQuery = this.mockRepository.Create(); + + _ = mockQuery.Setup(c => c.GetNextAsJsonAsync()) + .ReturnsAsync(new[] + { + /*lang=json*/ + "{ deviceId: 'value1'}", + /*lang=json*/ + "{ deviceId: 'value2'}", + }); + + _ = this.mockRegistryManager.Setup(c => c.CreateQuery( + It.Is(x => x == $"SELECT DeviceID FROM devices.modules WHERE devices.modules.moduleId = 'LoRaWanNetworkSrvModule'"))) + .Returns(mockQuery.Object); + + // Act + var result = await service.GetAllGatewayID(); + + // Assert + Assert.IsNotNull(result); + Assert.IsAssignableFrom>(result); + Assert.AreEqual(2, result.Count); + Assert.AreEqual("value1", result[0]); + this.mockRepository.VerifyAll(); + } + + [Test] + public async Task GetAllGatewayIDIssueOccuringShouldThrowInternalServerErrorException() + { + // Arrange + var service = CreateService(); + + var mockQuery = this.mockRepository.Create(); + + _ = mockQuery.Setup(c => c.GetNextAsJsonAsync()) + .ThrowsAsync(new Exception("test")); + + _ = this.mockRegistryManager.Setup(c => c.CreateQuery( + It.Is(x => x == $"SELECT DeviceID FROM devices.modules WHERE devices.modules.moduleId = 'LoRaWanNetworkSrvModule'"))) + .Returns(mockQuery.Object); + + // Act + var act = () => service.GetAllGatewayID(); + + // Assert + _ = await act.Should().ThrowAsync(); + this.mockRepository.VerifyAll(); + } } } diff --git a/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs b/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs index 1ce1e5562..6986148b4 100644 --- a/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs +++ b/src/AzureIoTHub.Portal/Server/Services/ExternalDeviceService.cs @@ -695,19 +695,15 @@ public async Task> GetAllGatewayID() var query = this.registryManager.CreateQuery($"SELECT DeviceID FROM devices.modules WHERE devices.modules.moduleId = 'LoRaWanNetworkSrvModule'"); var list = new List(); - while (query.HasMoreResults) + try { - try - { - var value = await query.GetNextAsJsonAsync(); - list.Add(JObject.Parse(value.SingleOrDefault())["deviceId"]?.ToString()); - } - catch (Exception e) - { - throw new InternalServerErrorException($"Unable to get GatewayIDs", e); - } + var value = (await query.GetNextAsJsonAsync()).ToList(); + list.AddRange(value.Select(x => JObject.Parse(x)["deviceId"]?.ToString())); + } + catch (Exception e) + { + throw new InternalServerErrorException($"Unable to get GatewayIDs", e); } - return list; } } From 1b2523c524fd68092a79931ebc25cc6e671d2231 Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Wed, 19 Oct 2022 17:19:09 +0200 Subject: [PATCH 6/9] Add test on LoRaWANDevicesController --- .../LoRaWAN/LoRaWANDevicesControllerTests.cs | 30 ++++++++++++++++++- .../v1.0/LoRaWAN/LoRaWANDevicesController.cs | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs index 961053799..7ce0ad900 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesControllerTests.cs @@ -49,7 +49,7 @@ private LoRaWANDevicesController CreateLoRaWANDevicesController() { var loRaGatewayIDList = new LoRaGatewayIDList { - GatewayIds = new List(){ Guid.NewGuid().ToString(), Guid.NewGuid().ToString() } + GatewayIds = new List(){ "GatewayId1", "GatewayId2" } }; return new LoRaWANDevicesController( @@ -274,5 +274,33 @@ public async Task GetEnrollmentCredentialsShouldAlwaysReturn404() Assert.IsAssignableFrom(response.Result); this.mockRepository.VerifyAll(); } + + [Test] + public void GetGatewaysExpectedBehavior() + { + // Arrange + var devicesController = CreateLoRaWANDevicesController(); + + var loRaGatewayIDList = new LoRaGatewayIDList + { + GatewayIds = new List(){ "GatewayId1", "GatewayId2" } + }; + + // Act + var result = devicesController.GetGateways(); + + // Assert + Assert.IsNotNull(result); + Assert.IsAssignableFrom(result.Result); + + var okObjectResult = result.Result as OkObjectResult; + Assert.IsNotNull(okObjectResult); + Assert.IsNotNull(okObjectResult.Value); + + Assert.IsAssignableFrom(okObjectResult.Value); + _ = okObjectResult.Value.Should().BeEquivalentTo(loRaGatewayIDList); + + this.mockRepository.VerifyAll(); + } } } diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs index 3eec0907a..af267658f 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs @@ -120,7 +120,7 @@ public override Task> GetCredentials(string [HttpGet("gateways", Name = "Get Gateways")] public ActionResult GetGateways() { - return this.gatewayIdList; + return Ok(this.gatewayIdList); } } } From 4e9b1e757735ed9f121e364fc413d8a1c6bbed47 Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Thu, 20 Oct 2022 10:20:53 +0200 Subject: [PATCH 7/9] Add tests on SyncGatewayIDJob --- .../Server/Jobs/SyncGatewayIDJobTests.cs | 76 +++++++++++++++++++ .../Server/Jobs/SyncGatewayIDJob.cs | 4 +- 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/AzureIoTHub.Portal.Tests.Unit/Server/Jobs/SyncGatewayIDJobTests.cs diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Jobs/SyncGatewayIDJobTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Jobs/SyncGatewayIDJobTests.cs new file mode 100644 index 000000000..de83d3569 --- /dev/null +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Jobs/SyncGatewayIDJobTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AzureIoTHub.Portal.Tests.Unit.Server.Jobs +{ + using System; + using System.Collections.Generic; + using AzureIoTHub.Portal.Server.Jobs; + using AzureIoTHub.Portal.Server.Services; + using AzureIoTHub.Portal.Shared.Models.v1._0; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using Moq; + using NUnit.Framework; + using Quartz; + using UnitTests.Bases; + + public class SyncGatewayIDJobTests : BackendUnitTest + { + private IJob syncGatewayIDJob; + private Mock> mockLogger; + private Mock mockExternalDeviceService; + private LoRaGatewayIDList loRaGatewayIDList; + + public override void Setup() + { + base.Setup(); + + this.mockExternalDeviceService = MockRepository.Create(); + this.mockLogger = MockRepository.Create>(); + this.loRaGatewayIDList = new LoRaGatewayIDList(); + + _ = ServiceCollection.AddSingleton(this.mockExternalDeviceService.Object); + _ = ServiceCollection.AddSingleton(); + + Services = ServiceCollection.BuildServiceProvider(); + + this.syncGatewayIDJob = new SyncGatewayIDJob(this.mockExternalDeviceService.Object, this.loRaGatewayIDList, this.mockLogger.Object); + } + + [Test] + public void ExecuteExpectedBehaviorShouldSynchronizeGatewayID() + { + // Arrange + _ = this.mockLogger.Setup(x => x.Log(LogLevel.Information, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); + + _ = this.mockExternalDeviceService.Setup(service => service.GetAllGatewayID()) + .ReturnsAsync(new List() { "GatewayID01", "GatewayID02" }); + + // Act + _ = this.syncGatewayIDJob.Execute(null); + + // Assert + Assert.AreEqual(2, this.loRaGatewayIDList.GatewayIds.Count); + Assert.AreEqual("GatewayID01", this.loRaGatewayIDList.GatewayIds[0]); + this.MockRepository.VerifyAll(); + } + + [Test] + public void ExecuteExceptionThrownShouldRecordLogError() + { + // Arrange + _ = this.mockLogger.Setup(x => x.Log(LogLevel.Information, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); + _ = this.mockLogger.Setup(x => x.Log(LogLevel.Error, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); + _ = this.mockExternalDeviceService.Setup(service => service.GetAllGatewayID()) + .Throws(new Exception()); + + // Act + _ = this.syncGatewayIDJob.Execute(null); + + // Assert + Assert.IsNull(this.loRaGatewayIDList.GatewayIds); + this.MockRepository.VerifyAll(); + } + } +} diff --git a/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs index 5854800fb..f4330f730 100644 --- a/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs +++ b/src/AzureIoTHub.Portal/Server/Jobs/SyncGatewayIDJob.cs @@ -16,12 +16,12 @@ public class SyncGatewayIDJob : IJob private readonly IExternalDeviceService externalDeviceService; private readonly LoRaGatewayIDList gatewayIdList; - private readonly ILogger logger; + private readonly ILogger logger; public SyncGatewayIDJob( IExternalDeviceService externalDeviceService, LoRaGatewayIDList gatewayIdList, - ILogger logger) + ILogger logger) { this.externalDeviceService = externalDeviceService; this.gatewayIdList = gatewayIdList; From 4c9607bd782afb89bc4a3213b5d504130cb27228 Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Thu, 20 Oct 2022 10:31:50 +0200 Subject: [PATCH 8/9] Add test on LoRaWanDeviceClientService --- .../LoRaWanDeviceClientServiceTests.cs | 19 +++++++++++++++++++ .../Services/LoRaWanDeviceClientService.cs | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Services/LoRaWanDeviceClientServiceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Services/LoRaWanDeviceClientServiceTests.cs index 2c235f09d..d758909bd 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Services/LoRaWanDeviceClientServiceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Services/LoRaWanDeviceClientServiceTests.cs @@ -15,6 +15,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Services using Models.v10.LoRaWAN; using NUnit.Framework; using RichardSzalay.MockHttp; + using AzureIoTHub.Portal.Shared.Models.v1._0; [TestFixture] public class LoRaWanDeviceClientServiceTests : BlazorUnitTest @@ -118,5 +119,23 @@ public async Task ExecuteCommandShouldExecuteCommand() MockHttpClient.VerifyNoOutstandingRequest(); MockHttpClient.VerifyNoOutstandingExpectation(); } + + [Test] + public async Task GetGatewayIdListShouldReturnGatewayIdList() + { + // Arrange + var expectedLoRaGatewayIDList = Fixture.Create(); + + _ = MockHttpClient.When(HttpMethod.Get, $"/api/lorawan/devices/gateways") + .RespondJson(expectedLoRaGatewayIDList); + + // Act + var result = await this.loRaWanDeviceClientService.GetGatewayIdList(); + + // Assert + _ = result.Should().BeEquivalentTo(expectedLoRaGatewayIDList); + MockHttpClient.VerifyNoOutstandingRequest(); + MockHttpClient.VerifyNoOutstandingExpectation(); + } } } diff --git a/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs b/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs index 29fea8346..bb3102027 100644 --- a/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs +++ b/src/AzureIoTHub.Portal/Client/Services/LoRaWanDeviceClientService.cs @@ -3,7 +3,6 @@ namespace AzureIoTHub.Portal.Client.Services { - using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; From c7dcd5b60c7204ab15fc4dc7e91260f3c404ae2b Mon Sep 17 00:00:00 2001 From: Audrey Serra Date: Thu, 20 Oct 2022 15:05:09 +0200 Subject: [PATCH 9/9] Add unit test on front-end --- .../Devices/LoRaWan/CreateLoraDeviceTests.cs | 156 ++++++++++++++++-- .../Devices/LoRaWan/EditLoraDeviceTests.cs | 141 +++++++++++++--- .../Devices/LoRaWAN/CreateLoraDevice.razor | 2 +- .../Devices/LoRaWAN/EditLoraDevice.razor | 2 +- 4 files changed, 259 insertions(+), 42 deletions(-) diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs index b55a76f15..4f585bb80 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/CreateLoraDeviceTests.cs @@ -4,14 +4,21 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices { using System; + using System.Collections.Generic; + using AngleSharp.Dom; + using AutoFixture; + using AzureIoTHub.Portal.Client.Exceptions; + using AzureIoTHub.Portal.Client.Models; using AzureIoTHub.Portal.Client.Pages.Devices.LoRaWAN; using AzureIoTHub.Portal.Client.Services; using AzureIoTHub.Portal.Client.Validators; using AzureIoTHub.Portal.Models.v10.LoRaWAN; using AzureIoTHub.Portal.Shared.Models.v1._0; using Bunit; + using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Moq; + using MudBlazor; using MudBlazor.Services; using NUnit.Framework; using UnitTests.Bases; @@ -41,14 +48,7 @@ public void WhenUseOTAAShouldDisplayOTAATextboxes() UseOTAA = true }; - var deviceDetails = new LoRaDeviceDetails() - { - DeviceID = Guid.NewGuid().ToString(), - ModelId = mockLoRaModel.ModelId, - AppEUI = Guid.NewGuid().ToString(), - AppKey = Guid.NewGuid().ToString() - }; - + var deviceDetails = new LoRaDeviceDetails(); var validator = new LoRaDeviceDetailsValidator(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) @@ -76,14 +76,7 @@ public void WhenNotUseOTAAShouldDisplayABPTextboxes() UseOTAA = false }; - var deviceDetails= new LoRaDeviceDetails() - { - DeviceID = Guid.NewGuid().ToString(), - ModelId = mockLoRaModel.ModelId, - AppSKey = Guid.NewGuid().ToString(), - NwkSKey = Guid.NewGuid().ToString(), - DevAddr = Guid.NewGuid().ToString(), - }; + var deviceDetails= new LoRaDeviceDetails(); var validator = new LoRaDeviceDetailsValidator(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) @@ -97,10 +90,139 @@ public void WhenNotUseOTAAShouldDisplayABPTextboxes() // Assert - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.AppSKey, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.AppSKey)}").GetAttribute("value"))); cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.NwkSKey, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.NwkSKey)}").GetAttribute("value"))); cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.DevAddr, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.DevAddr)}").GetAttribute("value"))); } + + [Test] + public void CreateLoRaDevicePageShouldBeRenderedProperly() + { + // Arrange + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails(); + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(Fixture.Create); + + // Act + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraValidator), validator)); + + // Assert + cut.WaitForAssertion(() => cut.FindAll(".mud-expand-panel").Count.Should().Be(1)); + Assert.AreEqual(3, cut.Instance.GatewayIdList.Count); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingGatewayIDList() + { + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails(); + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + // Act + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraValidator), validator)); + + // Assert + Assert.AreEqual(0, cut.Instance.GatewayIdList.Count); + cut.WaitForAssertion(() => cut.Markup.Should().NotBeNullOrEmpty()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ClickingOnMudAutocompleteShouldDisplaySearch() + { + // Arrange + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails(); + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(Fixture.Create); + + // Act + var popoverProvider = RenderComponent(); + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraValidator), validator)); + var autocompleteComponent = cut.FindComponent>(); + + // Act + autocompleteComponent.Find(TagNames.Input).Click(); + + // Assert + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); + cut.WaitForAssertion(() => autocompleteComponent.Instance.IsOpen.Should().BeTrue()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void TypingOnMudAutocompleteShouldTriggerSearch() + { + // Arrange + var query = "Gateway"; + + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails(); + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList() + { + GatewayIds = new List() + { + "GatewayID01", "GatewayID02", "TestValue" + } + }); + + // Act + var popoverProvider = RenderComponent(); + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(CreateLoraDevice.LoraValidator), validator)); + var autocompleteComponent = cut.FindComponent>(); + + // Act + autocompleteComponent.Find(TagNames.Input).Click(); + autocompleteComponent.Find(TagNames.Input).Input(query); + + // Assert + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(2)); + cut.WaitForAssertion(() => autocompleteComponent.Instance.IsOpen.Should().BeTrue()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } } } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs index 741103880..2de53fabd 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/LoRaWan/EditLoraDeviceTests.cs @@ -5,10 +5,17 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices { using System; using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using AngleSharp.Dom; + using AutoFixture; + using AzureIoTHub.Portal.Client.Exceptions; + using AzureIoTHub.Portal.Client.Models; using AzureIoTHub.Portal.Client.Pages.Devices.LoRaWAN; using AzureIoTHub.Portal.Client.Services; using AzureIoTHub.Portal.Client.Validators; using AzureIoTHub.Portal.Models.v10.LoRaWAN; + using AzureIoTHub.Portal.Shared.Models.v1._0; using Bunit; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; @@ -18,9 +25,6 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices using NUnit.Framework; using UnitTests.Bases; using UnitTests.Mocks; - using System.Linq; - using System.Threading.Tasks; - using AzureIoTHub.Portal.Shared.Models.v1._0; [TestFixture] public class EditLoraDeviceTests : BlazorUnitTest @@ -60,7 +64,7 @@ public void WhenUseOTAAShouldDisplayOTAATextboxes() var validator = new LoRaDeviceDetailsValidator(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -94,7 +98,7 @@ public void WhenNotUseOTAAShouldDisplayABPTextboxes() var validator = new LoRaDeviceDetailsValidator(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -130,7 +134,7 @@ public void EditLoRaDevicePageShouldBeRenderedProperly() var validator = new LoRaDeviceDetailsValidator(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -140,6 +144,44 @@ public void EditLoRaDevicePageShouldBeRenderedProperly() // Assert cut.WaitForAssertion(() => cut.FindAll(".mud-expand-panel").Count.Should().Be(3)); + Assert.AreEqual(deviceDetails.Deduplication, Enum.Parse(cut.WaitForElement($"#{nameof(LoRaDeviceDetails.Deduplication)}").GetAttribute("value"))); + Assert.AreEqual(deviceDetails.ClassType, Enum.Parse(cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ClassType)}").GetAttribute("value"))); + Assert.AreEqual(3, cut.Instance.GatewayIdList.Count); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingGatewayIDList() + { + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails() + { + DeviceID = Guid.NewGuid().ToString(), + ModelId = mockLoRaModel.ModelId, + AppSKey = Guid.NewGuid().ToString(), + NwkSKey = Guid.NewGuid().ToString(), + DevAddr = Guid.NewGuid().ToString(), + }; + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + // Act + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDeviceModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoraValidator), validator)); + + // Assert + Assert.AreEqual(0, cut.Instance.GatewayIdList.Count); + cut.WaitForAssertion(() => cut.Markup.Should().NotBeNullOrEmpty()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); } [Test] @@ -164,7 +206,7 @@ public void WhenDeviceNeverConnectedCommandsShouldBeDisabled() }; _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -201,7 +243,7 @@ public void WhenDeviceAlteadyConnectedCommandsShouldBeEnabled() }; _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -243,7 +285,7 @@ public void WhenClickToSendCommandShouldExecuteCommandToService() .Returns((Snackbar)null); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); // Act var cut = RenderComponent( @@ -260,7 +302,7 @@ public void WhenClickToSendCommandShouldExecuteCommandToService() } [Test] - public void EditLoRaDeviceDetailPageShouldBeRenderedProperly() + public void EditLoRaDeviceDetailPageWithReportedProperties() { // Arrange var mockLoRaModel = new LoRaDeviceModelDto @@ -281,24 +323,69 @@ public void EditLoRaDeviceDetailPageShouldBeRenderedProperly() var LoraDevice = new LoRaDeviceDetails(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(Fixture.Create); + + // Act + var cut = RenderComponent( + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDeviceModelDto), mockLoRaModel), + ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoraValidator), validator)); + + // Assert + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.DataRate, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.DataRate)}").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.TxPower, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.TxPower)}").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.NbRep, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.NbRep)}").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRX1DROffset, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRX1DROffset)}").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRX2DataRate, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRX2DataRate)}").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRXDelay, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRXDelay)}").GetAttribute("value"))); + } + + [Test] + public void ClickingOnMudAutocompleteShouldDisplaySearch() + { + // Arrange + var mockLoRaModel = new LoRaDeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + UseOTAA = false + }; + + var deviceDetails= new LoRaDeviceDetails() + { + DeviceID = Guid.NewGuid().ToString(), + ModelId = mockLoRaModel.ModelId, + AppSKey = Guid.NewGuid().ToString(), + NwkSKey = Guid.NewGuid().ToString(), + DevAddr = Guid.NewGuid().ToString(), + }; + var validator = new LoRaDeviceDetailsValidator(); + + _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) + .ReturnsAsync(Fixture.Create); // Act + var popoverProvider = RenderComponent(); var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDeviceModelDto), mockLoRaModel), ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoraValidator), validator)); + var autocompleteComponent = cut.FindComponent>(); + // Act + autocompleteComponent.Find(TagNames.Input).Click(); - // Assert - Assert.AreEqual(LoraDevice.Deduplication, Enum.Parse(cut.WaitForElement($"#{nameof(LoRaDeviceDetails.Deduplication)}").GetAttribute("value"))); - Assert.AreEqual(LoraDevice.ClassType, Enum.Parse(cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ClassType)}").GetAttribute("value"))); + // Assert + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); + cut.WaitForAssertion(() => autocompleteComponent.Instance.IsOpen.Should().BeTrue()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); } [Test] - public void EditLoRaDeviceDetailPageWithReportedProperties() + public void TypingOnMudAutocompleteShouldTriggerSearch() { // Arrange + var query = "Gateway"; + var mockLoRaModel = new LoRaDeviceModelDto { ModelId = Guid.NewGuid().ToString(), @@ -314,24 +401,32 @@ public void EditLoRaDeviceDetailPageWithReportedProperties() DevAddr = Guid.NewGuid().ToString(), }; var validator = new LoRaDeviceDetailsValidator(); - var LoraDevice = new LoRaDeviceDetails(); _ = this.mockLoRaWanDeviceClientService.Setup(c => c.GetGatewayIdList()) - .ReturnsAsync(new LoRaGatewayIDList()); + .ReturnsAsync(new LoRaGatewayIDList() + { + GatewayIds = new List() + { + "GatewayID01", "GatewayID02", "TestValue" + } + }); // Act + var popoverProvider = RenderComponent(); var cut = RenderComponent( ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDevice), deviceDetails), ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoRaDeviceModelDto), mockLoRaModel), ComponentParameter.CreateParameter(nameof(EditLoraDevice.LoraValidator), validator)); + var autocompleteComponent = cut.FindComponent>(); + + // Act + autocompleteComponent.Find(TagNames.Input).Click(); + autocompleteComponent.Find(TagNames.Input).Input(query); // Assert - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.DataRate, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.DataRate)}").GetAttribute("value"))); - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.TxPower, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.TxPower)}").GetAttribute("value"))); - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.NbRep, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.NbRep)}").GetAttribute("value"))); - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRX1DROffset, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRX1DROffset)}").GetAttribute("value"))); - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRX2DataRate, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRX2DataRate)}").GetAttribute("value"))); - cut.WaitForAssertion(() => Assert.AreEqual(deviceDetails.ReportedRXDelay, cut.WaitForElement($"#{nameof(LoRaDeviceDetails.ReportedRXDelay)}").GetAttribute("value"))); + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(2)); + cut.WaitForAssertion(() => autocompleteComponent.Instance.IsOpen.Should().BeTrue()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); } } } diff --git a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor index 4af4917ce..1d085d63d 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/CreateLoraDevice.razor @@ -77,7 +77,7 @@ [Parameter] public LoRaDeviceModelDto LoraModelDto { get; set; } - private List GatewayIdList = new(); + public List GatewayIdList = new(); protected override async Task OnInitializedAsync() { diff --git a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor index 4aac3faf5..d42a33ca8 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/Devices/LoRaWAN/EditLoraDevice.razor @@ -186,7 +186,7 @@ private bool isProcessing; - private List GatewayIdList = new(); + public List GatewayIdList = new(); protected override async Task OnInitializedAsync() {