Skip to content

Commit

Permalink
Test LoRaWan save Action
Browse files Browse the repository at this point in the history
  • Loading branch information
kbeaugrand committed Mar 13, 2022
1 parent 0cab54f commit 7ec9a02
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 66 deletions.
88 changes: 50 additions & 38 deletions src/AzureIoTHub.Portal.Server.Tests.Unit/Helpers/MockHttpHelper.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,87 @@
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using RichardSzalay.MockHttp;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
// 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.Tests.Unit.Helpers
{
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using RichardSzalay.MockHttp;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;

public static class MockHttpHelper
{
public static MockHttpMessageHandler AddMockHttpClient(this TestServiceProvider services)
{
var mockHttpHandler = new MockHttpMessageHandler();
var httpClient = mockHttpHandler.ToHttpClient();
httpClient.BaseAddress = new Uri("http://fake.local");
services.AddSingleton<HttpClient>(httpClient);

_ = services.AddSingleton<HttpClient>(httpClient);

return mockHttpHandler;
}

public static MockedRequest RespondJson<T>(this MockedRequest request, T content)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(JsonSerializer.Serialize(content));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
_ = request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonSerializer.Serialize(content))
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});

return request;
}

public static MockedRequest RespondJsonAsync<T>(this MockedRequest request, Task<T> content)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(JsonSerializer.Serialize(content));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
_ = request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonSerializer.Serialize(content))
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});

return request;
}

public static MockedRequest RespondText(this MockedRequest request, string content)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(content);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
_ = request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(content)
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});

return request;
}

public static MockedRequest RespondJson<T>(this MockedRequest request, Func<T> contentProvider)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(JsonSerializer.Serialize(contentProvider()));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
_ = request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonSerializer.Serialize(contentProvider()))
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});

return request;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Pages
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Client.Pages.DeviceModels;
using AzureIoTHub.Portal.Server.Tests.Unit.Helpers;
using AzureIoTHub.Portal.Shared.Models;
Expand Down Expand Up @@ -35,6 +36,7 @@ public class CreateDeviceModelPageTests : IDisposable
private MockHttpMessageHandler mockHttpClient;

private static string apiBaseUrl => $"/api/models";
private static string lorawanApiUrl => $"/api/lorawan/models";

[SetUp]
public void SetUp()
Expand Down Expand Up @@ -282,6 +284,41 @@ public void WhenLoraFeatureIsEnabledModelDetailsShouldDisplayLoRaWANTab()
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public void WhenLoraDeviceModelDetailsShouldCallLoRaAPIs()
{
// Arrange
_ = testContext.Services.AddSingleton(new PortalSettings { IsLoRaSupported = true });

var cut = RenderComponent<CreateDeviceModelPage>();
_ = cut.WaitForElement("#form");

cut.Find($"#{nameof(DeviceModel.Name)}").Change(Guid.NewGuid().ToString());
cut.Find($"#{nameof(DeviceModel.Description)}").Change(Guid.NewGuid().ToString());

cut.WaitForElement("#SupportLoRaFeatures")
.Change(true);

_ = this.mockHttpClient.When(HttpMethod.Post, $"{lorawanApiUrl}")
.RespondText(string.Empty);

_ = this.mockHttpClient.When(HttpMethod.Post, $"{ lorawanApiUrl }/*/commands")
.RespondText(string.Empty);

_ = this.mockHttpClient.When(HttpMethod.Post, $"{ lorawanApiUrl }/*/avatar")
.RespondText(string.Empty);

var saveButton = cut.WaitForElement("#SaveButton");

// Act
saveButton.Click();
cut.WaitForState(() => testContext.Services.GetRequiredService<FakeNavigationManager>().Uri.EndsWith("/device-models", StringComparison.OrdinalIgnoreCase));

// Assert
this.mockHttpClient.VerifyNoOutstandingExpectation();
}


public void Dispose()
{
GC.SuppressFinalize(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<MudText Typo="Typo.h5" Color="Color.Primary" Class="mb-4">Device Model</MudText>

<MudForm Model="@Model" @ref="form" id="form">
<MudTabs Elevation="1" Rounded="true" PanelClass="mt-6" @ref="tabs">
<MudTabs Elevation="1" Rounded="true" PanelClass="mt-6">
<MudTabPanel Text="General" Style=@(standardValidator.Validate(Model).IsValid ? "" : "color: red")>
<MudGrid>
<MudItem xs="12" sm="4" md="2">
Expand Down Expand Up @@ -143,13 +143,12 @@
</MudTabPanel>
@if (IsLoRa)
{
<MudTabPanel Text="LoRaWAN" Style=@(loraValidator.Validate(Model as LoRaDeviceModel).IsValid ? "" : "color: red")>
<MudTabPanel Class="LoRaWANTab" Text="LoRaWAN" Style=@(loraValidator.Validate(Model as LoRaDeviceModel).IsValid ? "" : "color: red")>
<MudGrid>
<CreateLoraDeviceModel LoRaDeviceModel="@(Model as LoRaDeviceModel)"
Commands="Commands"
OnSaveClick="Save"
CommandValidation="@CommandValidation" />

</MudGrid>
</MudTabPanel>
}
Expand Down Expand Up @@ -217,9 +216,6 @@
private MultipartFormDataContent content;
private string imageDataUrl;

// Used to switch active tab
private MudTabs tabs;

private void DeleteAvatar()
{
content = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
<MudItem xs="12">
<MudGrid>
<MudItem md="6" xs="12">
<MudSwitch @bind-Checked="@LoRaDeviceModel.UseOTAA">@(LoRaDeviceModel.UseOTAA ? "OTAA setting enable" : "APB setting enable")</MudSwitch>
</MudItem>
<MudItem md="6" xs="12">
<MudSwitch @bind-Checked="@LoRaDeviceModel.ABPRelaxMode" For="@(() => LoRaDeviceModel.ABPRelaxMode)" Label="Disable ABP relax mode" Color="Color.Primary"></MudSwitch>
<MudSwitch id="@nameof(LoRaDeviceModel.UseOTAA)" @bind-Checked="@LoRaDeviceModel.UseOTAA" Color="Color.Primary">@(LoRaDeviceModel.UseOTAA ? "OTAA setting enable" : "APB setting enable")</MudSwitch>
</MudItem>
@if (!LoRaDeviceModel.UseOTAA)
{
<MudItem md="6" xs="12">
<MudSwitch id="@nameof(LoRaDeviceModel.ABPRelaxMode)" @bind-Checked="@LoRaDeviceModel.ABPRelaxMode" For="@(() => LoRaDeviceModel.ABPRelaxMode)" Label="Disable ABP relax mode" Color="Color.Primary"></MudSwitch>
</MudItem>
}
<MudItem md="3" xs="6">
<MudSelect id="@nameof(LoRaDeviceModel.ClassType)"
@bind-Value="@LoRaDeviceModel.ClassType"
Expand All @@ -32,7 +35,7 @@
</MudSelect>
</MudItem>
<MudItem md="3" xs="6">
<MudSelect @bind-Value="@LoRaDeviceModel.Deduplication" For="@(() => LoRaDeviceModel.Deduplication)" AnchorOrigin="Origin.BottomCenter" Label="Message Deduplication" Variant="Variant.Outlined">
<MudSelect id="@nameof(LoRaDeviceModel.Deduplication)" @bind-Value="@LoRaDeviceModel.Deduplication" For="@(() => LoRaDeviceModel.Deduplication)" AnchorOrigin="Origin.BottomCenter" Label="Message Deduplication" Variant="Variant.Outlined">
@foreach (DeduplicationMode item in Enum.GetValues(typeof(DeduplicationMode)))
{
<MudSelectItem Value="@item">@item</MudSelectItem>
Expand All @@ -46,18 +49,18 @@
@if (LoRaDeviceModel.UseOTAA)
{
<MudItem xs="12">
<MudTextField @bind-Value="@LoRaDeviceModel.AppEUI" Label="OTAA AppEUI" For="@(() => LoRaDeviceModel.AppEUI)" Variant="Variant.Outlined" Required="true"></MudTextField>
<MudTextField id="@nameof(LoRaDeviceModel.AppEUI)" @bind-Value="@LoRaDeviceModel.AppEUI" Label="OTAA AppEUI" For="@(() => LoRaDeviceModel.AppEUI)" Variant="Variant.Outlined" Required="true"></MudTextField>
</MudItem>
}
<MudItem xs="12">
<MudTextField @bind-Value="@LoRaDeviceModel.SensorDecoder" Label="Sensor Decoder URL" For="@(() => LoRaDeviceModel.SensorDecoder)" Variant="Variant.Outlined"></MudTextField>
<MudTextField id="@nameof(LoRaDeviceModel.SensorDecoder)" @bind-Value="@LoRaDeviceModel.SensorDecoder" Label="Sensor Decoder URL" For="@(() => LoRaDeviceModel.SensorDecoder)" Variant="Variant.Outlined"></MudTextField>
</MudItem>
</MudGrid>
</MudItem>
<MudItem md="6" xs=12>
<MudGrid>
<MudItem md="6" xs="12">
<MudNumericField @bind-Value="@LoRaDeviceModel.KeepAliveTimeout" For="@(() => LoRaDeviceModel.KeepAliveTimeout)" Min=1 Label="Device Connection Timeout" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.KeepAliveTimeout)" @bind-Value="@LoRaDeviceModel.KeepAliveTimeout" For="@(() => LoRaDeviceModel.KeepAliveTimeout)" Min=1 Label="Device Connection Timeout" Variant="Variant.Outlined"></MudNumericField>
</MudItem>

</MudGrid>
Expand All @@ -71,21 +74,21 @@
<MudText Typo=Typo.h5 Class="mb-4">Receive Windows</MudText>
<MudGrid>
<MudItem xs="12">
<MudSwitch @bind-Checked="@LoRaDeviceModel.Downlink" For="@(() => LoRaDeviceModel.Downlink)" Label="Enable/disable downstream messages" Color="Color.Primary"></MudSwitch>
<MudSwitch id="@nameof(LoRaDeviceModel.Downlink)" @bind-Checked="@LoRaDeviceModel.Downlink" For="@(() => LoRaDeviceModel.Downlink)" Label="Enable/disable downstream messages" Color="Color.Primary"></MudSwitch>
</MudItem>
@if (LoRaDeviceModel.Downlink.HasValue && LoRaDeviceModel.Downlink.Value)
{
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.PreferredWindow" For="@(() => LoRaDeviceModel.PreferredWindow)" Min="1" Max="2" Label="Preferred receive window" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.PreferredWindow)" @bind-Value="@LoRaDeviceModel.PreferredWindow" For="@(() => LoRaDeviceModel.PreferredWindow)" Min="1" Max="2" Label="Preferred receive window" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.RXDelay" For="@(() => LoRaDeviceModel.RXDelay)" Min=1 Label="RX Delay" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.RXDelay)" @bind-Value="@LoRaDeviceModel.RXDelay" For="@(() => LoRaDeviceModel.RXDelay)" Min=1 Label="RX Delay" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.RX1DROffset" For="@(() => LoRaDeviceModel.RX1DROffset)" Label="RX1 Datarate Offset" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.RX1DROffset)" @bind-Value="@LoRaDeviceModel.RX1DROffset" For="@(() => LoRaDeviceModel.RX1DROffset)" Label="RX1 Datarate Offset" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.RX2DataRate" For="@(() => LoRaDeviceModel.RX2DataRate)" Label="RX2 Datarate" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.RX2DataRate)" @bind-Value="@LoRaDeviceModel.RX2DataRate" For="@(() => LoRaDeviceModel.RX2DataRate)" Label="RX2 Datarate" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
}
</MudGrid>
Expand All @@ -95,18 +98,18 @@
<MudText Typo=Typo.h5 Class="mb-4">Frame Counters</MudText>
<MudGrid>
<MudItem xs="12">
<MudSwitch @bind-Checked="@LoRaDeviceModel.Supports32BitFCnt" For="@(() => LoRaDeviceModel.Supports32BitFCnt)" Label="32bit counter support" Color="Color.Primary" Variant="Variant.Outlined"></MudSwitch>
<MudSwitch id="@nameof(LoRaDeviceModel.Supports32BitFCnt)" @bind-Checked="@LoRaDeviceModel.Supports32BitFCnt" For="@(() => LoRaDeviceModel.Supports32BitFCnt)" Label="32bit counter support" Color="Color.Primary" Variant="Variant.Outlined"></MudSwitch>
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.FCntUpStart" For="@(() => LoRaDeviceModel.FCntUpStart)" Min=0 Label="Frame counter up start value" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.FCntUpStart)" @bind-Value="@LoRaDeviceModel.FCntUpStart" For="@(() => LoRaDeviceModel.FCntUpStart)" Min=0 Label="Frame counter up start value" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
@if (LoRaDeviceModel.Downlink.HasValue && LoRaDeviceModel.Downlink.Value)
{
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.FCntDownStart" For="@(() => LoRaDeviceModel.FCntDownStart)" Min=0 Label="Frame counter down start value" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.FCntDownStart)" @bind-Value="@LoRaDeviceModel.FCntDownStart" For="@(() => LoRaDeviceModel.FCntDownStart)" Min=0 Label="Frame counter down start value" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField @bind-Value="@LoRaDeviceModel.FCntResetCounter" For="@(() => LoRaDeviceModel.FCntResetCounter)" Min=0 Label="Frame counter reset counter value" Variant="Variant.Outlined"></MudNumericField>
<MudNumericField id="@nameof(LoRaDeviceModel.FCntResetCounter)" @bind-Value="@LoRaDeviceModel.FCntResetCounter" For="@(() => LoRaDeviceModel.FCntResetCounter)" Min=0 Label="Frame counter reset counter value" Variant="Variant.Outlined"></MudNumericField>
</MudItem>
}
</MudGrid>
Expand Down Expand Up @@ -157,7 +160,7 @@
}
</MudCardContent>
<MudCardActions Class="pb-4 pl-4">
<MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto" OnClick="OnSaveClick">Save Changes</MudButton>
<MudButton id="SaveButton" Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto" OnClick="OnSaveClick">Save Changes</MudButton>
</MudCardActions>
</MudCard>
</MudItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public class LoRaDeviceModelValidator : AbstractValidator<LoRaDeviceModel>
{
public LoRaDeviceModelValidator()
{
_ = RuleFor(x => x.AppEUI)
.NotEmpty()
.Length(1, 100)
.When(x => x.UseOTAA);
//_ = RuleFor(x => x.AppEUI)
// .NotEmpty()
// .Length(1, 100)
// .When(x => x.UseOTAA);
}
}
}

0 comments on commit 7ec9a02

Please sign in to comment.