Skip to content

Commit

Permalink
Fix #969 - Refactor LoRa Device model and LoRaDevice classes (#1056)
Browse files Browse the repository at this point in the history
* Fix #969 - Refactor LoRa Device model and LoRaDevice classes

* Fix unit tests

* Add device detail page unit tests

* Remove unused variables in unit tests
  • Loading branch information
kbeaugrand authored Aug 11, 2022
1 parent 728e11c commit 5edbd1a
Show file tree
Hide file tree
Showing 27 changed files with 682 additions and 456 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices
using MudBlazor.Services;
using NUnit.Framework;
using UnitTests.Mocks;
using AzureIoTHub.Portal.Models.v10.LoRaWAN;
using AzureIoTHub.Portal.Client.Pages.DeviceModels;

[TestFixture]
public class DeviceDetailPageTests : BlazorUnitTest
Expand All @@ -42,10 +44,10 @@ public override void Setup()

this.mockDialogService = MockRepository.Create<IDialogService>();
this.mockSnackbarService = MockRepository.Create<ISnackbar>();
this.mockDeviceModelsClientService = MockRepository.Create<IDeviceModelsClientService>();
this.mockLoRaWanDeviceModelsClientService = MockRepository.Create<ILoRaWanDeviceModelsClientService>();
this.mockDeviceTagSettingsClientService = MockRepository.Create<IDeviceTagSettingsClientService>();
this.mockDeviceClientService = MockRepository.Create<IDeviceClientService>();
this.mockDeviceModelsClientService = MockRepository.Create<IDeviceModelsClientService>();
this.mockLoRaWanDeviceModelsClientService = MockRepository.Create<ILoRaWanDeviceModelsClientService>();
this.mockLoRaWanDeviceClientService = MockRepository.Create<ILoRaWanDeviceClientService>();

_ = Services.AddSingleton(this.mockDialogService.Object);
Expand All @@ -65,6 +67,64 @@ public override void Setup()
this.mockNavigationManager = Services.GetRequiredService<FakeNavigationManager>();
}

[Test]
public void ShouldLoadDeviceDetails()
{
// Arrange
var deviceId = Guid.NewGuid().ToString();
var modelId = Guid.NewGuid().ToString();

_ = this.mockDeviceClientService
.Setup(service => service.GetDevice(deviceId))
.ReturnsAsync(new DeviceDetails() { ModelId = modelId });

_ = this.mockDeviceClientService
.Setup(service => service.GetDeviceProperties(deviceId))
.ReturnsAsync(new List<DevicePropertyValue>());

_ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId))
.ReturnsAsync(new DeviceModel());

_ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags())
.ReturnsAsync(new List<DeviceTag>());

// Act
var cut = RenderComponent<DeviceDetailPage>(ComponentParameter.CreateParameter("DeviceID", deviceId));

// Assert
_ = cut.WaitForElement("#returnButton");
}

[Test]
public void ShouldLoadLoRaDeviceDetails()
{

// Arrange
var deviceId = Guid.NewGuid().ToString();
var modelId = Guid.NewGuid().ToString();

_ = this.mockLoRaWanDeviceClientService
.Setup(service => service.GetDevice(deviceId))
.ReturnsAsync(new LoRaDeviceDetails() { ModelId = modelId });

_ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId))
.ReturnsAsync(new LoRaDeviceModel());

_ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModelCommands(modelId))
.ReturnsAsync(new List<DeviceModelCommand>());

_ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags())
.ReturnsAsync(new List<DeviceTag>());

// Act
var cut = RenderComponent<DeviceDetailPage>(
ComponentParameter.CreateParameter("DeviceID", deviceId),
ComponentParameter.CreateParameter(nameof(DeviceModelDetailPage.IsLoRa), true));

// Assert
_ = cut.WaitForElement("#returnButton");
}

[Test]
public void ReturnButtonMustNavigateToPreviousPage()
{
Expand Down Expand Up @@ -157,7 +217,7 @@ public void ClickOnSaveShouldPutDeviceDetails()

// Act
var cut = RenderComponent<DeviceDetailPage>(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID));
cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModel.Name)}>b").InnerHtml.Should().NotBeEmpty());
cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModel.Name)}").InnerHtml.Should().NotBeEmpty());

var saveButton = cut.WaitForElement("#saveButton");
saveButton.Click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,7 @@ private LoRaDeviceModel SetupMockLoRaWANDeviceModel(DeviceModelCommand[] command
Name = this.mockModelId,
Description = Guid.NewGuid().ToString(),
IsBuiltin = false,
ImageUrl = new Uri($"http://fake.local/{this.mockModelId}"),
SupportLoRaFeatures = true
ImageUrl = new Uri($"http://fake.local/{this.mockModelId}")
};

_ = this.mockLoRaWanDeviceModelsClientService.Setup(service =>
Expand All @@ -412,8 +411,7 @@ private LoRaDeviceModel SetupMockLoRaWANDeviceModelThrowingException()
Name = this.mockModelId,
Description = Guid.NewGuid().ToString(),
IsBuiltin = false,
ImageUrl = new Uri($"http://fake.local/{this.mockModelId}"),
SupportLoRaFeatures = true
ImageUrl = new Uri($"http://fake.local/{this.mockModelId}")
};

_ = this.mockLoRaWanDeviceModelsClientService.Setup(service =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,14 @@ public void DuplicateSharedDeviceShouldReturnDuplicatedLoraWanDevice()
var appKey = Fixture.Create<string>();

// Act
var result = this.deviceLayoutService.DuplicateSharedDevice(new LoRaDeviceDetails
var loraWanDevice = this.deviceLayoutService.DuplicateSharedDevice(new LoRaDeviceDetails
{
DeviceID = deviceId,
DeviceName = deviceName,
AppKey = appKey
});

// Assert
var loraWanDevice = (LoRaDeviceDetails) result;

_ = loraWanDevice.DeviceID.Should().BeEmpty();
_ = loraWanDevice.DeviceName.Should().Be($"{deviceName} - copy");
_ = loraWanDevice.AppKey.Should().BeEmpty();
Expand All @@ -109,7 +107,7 @@ public void ResetSharedDeviceShouldReturnNewDevice()
var expectedDevice = new DeviceDetails();

// Act
var result = this.deviceLayoutService.ResetSharedDevice();
var result = this.deviceLayoutService.ResetSharedDevice<DeviceDetails>();

// Assert
_ = result.Should().BeEquivalentTo(expectedDevice);
Expand All @@ -129,7 +127,7 @@ public void ResetSharedDeviceShouldReturnNewDeviceWithExpectedTags()
}

// Act
var result = this.deviceLayoutService.ResetSharedDevice(expectedTags);
var result = this.deviceLayoutService.ResetSharedDevice<DeviceDetails>(expectedTags);

// Assert
_ = result.Should().BeEquivalentTo(expectedDevice);
Expand All @@ -142,36 +140,24 @@ public void ResetSharedDeviceModelShouldReturnNewDeviceModel()
var expectedDeviceModel = new DeviceModel();

// Act
var result = this.deviceLayoutService.ResetSharedDeviceModel();
var result = this.deviceLayoutService.ResetSharedDeviceModel<DeviceModel>();

// Assert
_ = result.Should().BeEquivalentTo(expectedDeviceModel);
}

[Test]
public void GetSharedDeviceShouldReturnDevice()
public void GetSharedDeviceShouldReturnNull()
{
// Arrange
var expectedDevice = new DeviceDetails();

// Act
var result = this.deviceLayoutService.GetSharedDevice();

// Assert
_ = result.Should().BeEquivalentTo(expectedDevice);
Assert.IsNull(this.deviceLayoutService.GetSharedDevice());
}

[Test]
public void GetSharedDeviceModelShouldReturnDeviceModel()
public void GetSharedDeviceModelShouldReturnNull()
{
// Arrange
var expectedDeviceModel = new DeviceModel();

// Act
var result = this.deviceLayoutService.GetSharedDeviceModel();

// Assert
_ = result.Should().BeEquivalentTo(expectedDeviceModel);
Assert.IsNull(this.deviceLayoutService.GetSharedDeviceModel());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ public void CreateDeviceModelStateUnderTestExpectedBehavior(bool isBuiltin, bool
this.mockRepository.VerifyAll();
}

[TestCase(false, false)]
[TestCase(true, false)]
[TestCase(true, true)]
[TestCase(false, true)]
public void UpdateTableEntityStateUnderTestExpectedBehavior(bool isBuiltin, bool supportLora)
[TestCase(false)]
[TestCase(true)]
[TestCase(true)]
[TestCase(false)]
public void UpdateTableEntityStateUnderTestExpectedBehavior(bool isBuiltin)
{
// Arrange
var loRaDeviceModelMapper = CreateLoRaDeviceModelMapper();
Expand All @@ -119,9 +119,7 @@ public void UpdateTableEntityStateUnderTestExpectedBehavior(bool isBuiltin, bool
Description = Guid.NewGuid().ToString(),
ImageUrl = new Uri("http://fake.local"),
SensorDecoder = Guid.NewGuid().ToString(),
IsBuiltin = isBuiltin,
SupportLoRaFeatures = supportLora,
UseOTAA = true
IsBuiltin = isBuiltin
};

// Act
Expand All @@ -133,7 +131,7 @@ public void UpdateTableEntityStateUnderTestExpectedBehavior(bool isBuiltin, bool
Assert.AreEqual(model.ModelId, entity.RowKey);
Assert.AreEqual(model.Name, entity[nameof(LoRaDeviceModel.Name)]);
Assert.AreEqual(model.Description, entity[nameof(LoRaDeviceModel.Description)]);
Assert.AreEqual(supportLora, entity[nameof(LoRaDeviceModel.SupportLoRaFeatures)]);
Assert.AreEqual(true, entity[nameof(LoRaDeviceModel.SupportLoRaFeatures)]);
Assert.AreEqual(isBuiltin, entity[nameof(LoRaDeviceModel.IsBuiltin)]);
Assert.AreEqual(model.SensorDecoder, entity[nameof(LoRaDeviceModel.SensorDecoder)]);
this.mockRepository.VerifyAll();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@using AzureIoTHub.Portal.Models
@using AzureIoTHub.Portal.Models.v10
@using AzureIoTHub.Portal.Models.v10.LoRaWAN
@using AzureIoTHub.Portal.Shared.Models

@attribute [Authorize]
@inject NavigationManager NavigationManager
Expand Down Expand Up @@ -42,7 +43,7 @@
</MudItem>
<MudItem xs="12" sm="8" md="9">
<MudTabs Elevation="1" Rounded="true" PanelClass="mt-6 scrollable-tab-content" id="tabs">
<MudTabPanel Text="General" Style=@(standardValidator.Validate(Model).IsValid ? "" : "color: red")>
<MudTabPanel Text="General" Style=@(this.CheckGeneralValidation() ? "color: red": "")>
<MudExpansionPanels MultiExpansion="true" id="general">
<MudGrid>
<MudItem xs="12">
Expand All @@ -62,12 +63,12 @@
<MudText>
<b>LoRa Device</b>
</MudText>
<MudSwitch @bind-Checked="@IsLoRa" Color="Color.Secondary" id="@nameof(Model.SupportLoRaFeatures)" Label="Support LoRa"></MudSwitch>

<MudSwitch @bind-Checked="@IsLoRa" id="SupportLoRaFeatures" Color="Color.Secondary" Label="Support LoRa"></MudSwitch>
@if (IsLoRa)
{
<MudText Typo="Typo.subtitle1" Class="mud-input-helper-text">The device <b>is</b> a LoRa Device.</MudText>

}
</MudItem>
}
Expand Down Expand Up @@ -124,18 +125,18 @@
Required="true">
@foreach (DevicePropertyType item in Enum.GetValues(typeof(DevicePropertyType)))
{
<MudSelectItem Value="@item">@item</MudSelectItem>
<MudSelectItem Value="@item">@item</MudSelectItem>
}
</MudSelect>
</MudItem>
<MudItem md="1" xs="3">
<MudSwitch id="@nameof(item.IsWritable)" Label="Writable" @bind-Checked="@item.IsWritable" Color="Color.Secondary" />

</MudItem>
<MudItem md="1" xs="3">
<MudIconButton id="deletePropertyButton" Icon="@Icons.Filled.Delete" OnClick="@(() => Properties.Remove(item))">Remove</MudIconButton>
</MudItem>
</MudGrid>
</MudSelect>
</MudItem>
<MudItem md="1" xs="3">
<MudSwitch id="@nameof(item.IsWritable)" Label="Writable" @bind-Checked="@item.IsWritable" Color="Color.Secondary" />

</MudItem>
<MudItem md="1" xs="3">
<MudIconButton id="deletePropertyButton" Icon="@Icons.Filled.Delete" OnClick="@(() => Properties.Remove(item))">Remove</MudIconButton>
</MudItem>
</MudGrid>
}
</MudItem>
</MudGrid>
Expand All @@ -148,9 +149,9 @@
</MudTabPanel>
@if (IsLoRa)
{
<MudTabPanel Class="LoRaWANTab" Text="LoRaWAN" Style=@(loraValidator.Validate(Model as LoRaDeviceModel).IsValid ? "" : "color: red")>
<MudTabPanel Class="LoRaWANTab" Text="LoRaWAN" Style=@(this.CheckLoRaValidation() ? "color: red": "")>
<CreateLoraDeviceModel LoRaDeviceModel="@(Model as LoRaDeviceModel)"
Commands="Commands"/>
Commands="Commands" />
</MudTabPanel>
}
</MudTabs>
Expand Down Expand Up @@ -209,7 +210,7 @@
};
}

internal DeviceModel Model { get; set; } = new DeviceModel
internal IDeviceModel Model { get; set; } = new DeviceModel
{
ModelId = Guid.NewGuid().ToString()
};
Expand All @@ -218,6 +219,26 @@
private MultipartFormDataContent content;
private string imageDataUrl;

private bool CheckLoRaValidation()
{
if (IsLoRa && this.Model is LoRaDeviceModel loRaDeviceModel)
{
return !this.loraValidator.Validate(loRaDeviceModel).IsValid;
}

return true;
}

private bool CheckGeneralValidation()
{
if (!IsLoRa && this.Model is DeviceModel deviceModel)
{
return !this.standardValidator.Validate(deviceModel).IsValid;
}

return CheckLoRaValidation();
}

private void DeleteAvatar()
{
content = null;
Expand Down Expand Up @@ -268,20 +289,20 @@
}

// Check validation error in commands
foreach(var cmd in Commands)
foreach (var cmd in Commands)
{
if (!CommandValidator.Validate(cmd).IsValid)
cmdValidationError = true;
}
}

if (!standardValidator.Validate(Model).IsValid
|| (IsLoRa && (!this.loraValidator.Validate(this.Model as LoRaDeviceModel).IsValid ||
duplicated ||
cmdValidationError)))
if (!IsLoRa ? !standardValidator.Validate(Model as DeviceModel).IsValid :
(!this.loraValidator.Validate(this.Model as LoRaDeviceModel).IsValid
|| duplicated
|| cmdValidationError))
{
Snackbar.Add("One or more validation errors occurred", Severity.Error);

isProcessing = false;

return;
Expand All @@ -291,17 +312,13 @@
{
if (IsLoRa)
{
var loraDeviceModel = Model as LoRaDeviceModel;

loraDeviceModel.SupportLoRaFeatures = true;

await LoRaWanDeviceModelsClientService.CreateDeviceModel(loraDeviceModel);
await LoRaWanDeviceModelsClientService.CreateDeviceModel(Model as LoRaDeviceModel);

await LoRaWanDeviceModelsClientService.SetDeviceModelCommands(Model.ModelId, Commands);
}
else
{
await DeviceModelsClientService.CreateDeviceModel(Model);
await DeviceModelsClientService.CreateDeviceModel(Model as DeviceModel);

await DeviceModelsClientService.SetDeviceModelModelProperties(Model.ModelId, Properties);
}
Expand Down Expand Up @@ -333,4 +350,4 @@
isProcessing = false;
}
}
}
}
Loading

0 comments on commit 5edbd1a

Please sign in to comment.