diff --git a/src/AzureIoTHub.Portal.Server.Tests.Unit/Extensions/DeviceModelCommandExtensionsTests.cs b/src/AzureIoTHub.Portal.Server.Tests.Unit/Extensions/DeviceModelCommandExtensionsTests.cs deleted file mode 100644 index a5588665a..000000000 --- a/src/AzureIoTHub.Portal.Server.Tests.Unit/Extensions/DeviceModelCommandExtensionsTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -// 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.Extensions -{ - using System; - using System.Linq; - using System.Text; - using AzureIoTHub.Portal.Models.v10.LoRaWAN; - using FluentAssertions; - using NUnit.Framework; - using Server.Extensions; - - [TestFixture] - public class DeviceModelCommandExtensionsTests - { - - [Test] - public void ToDynamicMustNotIncludeConfirmedPropertyWhenConfirmedIsFalse() - { - // Arrange - var command = new DeviceModelCommand - { - Name = Guid.NewGuid().ToString(), - Frame = Guid.NewGuid().ToString(), - Port = 123, - Confirmed = false, - IsBuiltin = false - }; - - // Act - var result = command.ToDynamic(); - - // Assert - _ = ((Type)result.GetType()).GetProperties() - .Where(p => p.Name.Equals("confirmed", StringComparison.Ordinal)) - .Any() - .Should() - .BeFalse(); - - _ = ((string)result.rawPayload).Should().Be(Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame))); - _ = ((int)result.fport).Should().Be(command.Port); - } - - [Test] - public void ToDynamicMustIncludeConfirmedPropertyWhenConfirmedIsTrue() - { - // Arrange - var command = new DeviceModelCommand - { - Name = Guid.NewGuid().ToString(), - Frame = Guid.NewGuid().ToString(), - Port = 123, - Confirmed = true, - IsBuiltin = false - }; - - // Act - var result = command.ToDynamic(); - - // Assert - _ = ((bool)result.confirmed).Should().BeTrue(); - _ = ((string)result.rawPayload).Should().Be(Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame))); - _ = ((int)result.fport).Should().Be(command.Port); - } - } -} diff --git a/src/AzureIoTHub.Portal.Server.Tests.Unit/Managers/LoraDeviceMethodManagerTests.cs b/src/AzureIoTHub.Portal.Server.Tests.Unit/Managers/LoraDeviceMethodManagerTests.cs index ef00ca5aa..1a24acc0f 100644 --- a/src/AzureIoTHub.Portal.Server.Tests.Unit/Managers/LoraDeviceMethodManagerTests.cs +++ b/src/AzureIoTHub.Portal.Server.Tests.Unit/Managers/LoraDeviceMethodManagerTests.cs @@ -3,48 +3,41 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Managers { - using AzureIoTHub.Portal.Models.v10.LoRaWAN; + using Models.v10.LoRaWAN; using AzureIoTHub.Portal.Server.Managers; using FluentAssertions; - using Moq; - using Moq.Protected; using NUnit.Framework; using System; + using System.Net; using System.Net.Http; + using System.Net.Http.Json; using System.Text; - using System.Threading; using System.Threading.Tasks; + using AutoFixture; + using Microsoft.Extensions.DependencyInjection; + using RichardSzalay.MockHttp; [TestFixture] - public class LoraDeviceMethodManagerTests : IDisposable + public class LoraDeviceMethodManagerTests : BackendUnitTest { -#pragma warning disable CA2213 // Disposable fields should be disposed - private HttpClient mockHttpClient; -#pragma warning restore CA2213 // Disposable fields should be disposed - private MockRepository mockRepository; + private ILoraDeviceMethodManager loraDeviceMethodManager; - private Mock httpMessageHandlerMock; - - [SetUp] - public void SetUp() + public override void Setup() { - this.mockRepository = new MockRepository(MockBehavior.Strict); + base.Setup(); - this.httpMessageHandlerMock = this.mockRepository.Create(); - this.mockHttpClient = new HttpClient(this.httpMessageHandlerMock.Object) - { - BaseAddress = new Uri("http://fake.local") - }; + _ = ServiceCollection.AddSingleton(); + + Services = ServiceCollection.BuildServiceProvider(); + + this.loraDeviceMethodManager = Services.GetRequiredService(); } [Test] public async Task ExecuteLoRaDeviceMessageThrowsArgumentNullExceptionWhenDeviceIdIsNull() { - // Arrange - var loraDeviceMethodManager = new LoraDeviceMethodManager(this.mockHttpClient); - // Act - var act = () => loraDeviceMethodManager.ExecuteLoRaDeviceMessage(null, null); + var act = () => this.loraDeviceMethodManager.ExecuteLoRaDeviceMessage(null, null); // Assert _ = await act.Should().ThrowAsync(); @@ -54,11 +47,10 @@ public async Task ExecuteLoRaDeviceMessageThrowsArgumentNullExceptionWhenDeviceI public async Task ExecuteLoRaDeviceMessageThrowsArgumentNullExceptionWhenCommandIsNull() { // Arrange - var loraDeviceMethodManager = new LoraDeviceMethodManager(this.mockHttpClient); - var deviceId = Guid.NewGuid().ToString(); + var deviceId = Fixture.Create(); // Act - var act = () => loraDeviceMethodManager.ExecuteLoRaDeviceMessage(deviceId, null); + var act = () => this.loraDeviceMethodManager.ExecuteLoRaDeviceMessage(deviceId, null); // Assert _ = await act.Should().ThrowAsync(); @@ -68,39 +60,38 @@ public async Task ExecuteLoRaDeviceMessageThrowsArgumentNullExceptionWhenCommand public async Task ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAreProvided() { // Arrange - var loraDeviceMethodManager = new LoraDeviceMethodManager(this.mockHttpClient); - var deviceId = Guid.NewGuid().ToString(); + var deviceId = Fixture.Create(); var command = new DeviceModelCommand { - Frame = Guid.NewGuid().ToString() + Frame = Fixture.Create(), + Confirmed = Fixture.Create(), + Port = Fixture.Create() }; - using var deviceResponseMock = new HttpResponseMessage(); - - deviceResponseMock.Content = new StringContent("{}", Encoding.UTF8, "application/json"); - - this.httpMessageHandlerMock - .Protected() - .Setup>("SendAsync", ItExpr.Is(req => req.RequestUri.LocalPath.Equals($"/api/cloudtodevicemessage/{deviceId}", StringComparison.OrdinalIgnoreCase) && req.Method == HttpMethod.Post), ItExpr.IsAny()) - .ReturnsAsync((HttpRequestMessage _, CancellationToken _) => deviceResponseMock) - .Verifiable(); + var expectedRawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame)); + + _ = MockHttpClient.When(HttpMethod.Post, $"/api/cloudtodevicemessage/{deviceId}") + .With(m => + { + _ = m.Content.Should().BeAssignableTo(); + var body = (JsonContent) m.Content; + var loRaCloudToDeviceMessage = (LoRaCloudToDeviceMessage)body?.Value; + _ = loRaCloudToDeviceMessage.Should().NotBeNull(); + _ = loRaCloudToDeviceMessage?.Fport.Should().Be(command.Port); + _ = loRaCloudToDeviceMessage?.Confirmed.Should().Be(command.Confirmed); + _ = loRaCloudToDeviceMessage?.RawPayload.Should().Be(expectedRawPayload); + return true; + }) + .Respond(HttpStatusCode.Created); // Act - var result = await loraDeviceMethodManager.ExecuteLoRaDeviceMessage(deviceId, command); + var result = await this.loraDeviceMethodManager.ExecuteLoRaDeviceMessage(deviceId, command); // Assert _ = result.Should().NotBeNull(); _ = result.IsSuccessStatusCode.Should().BeTrue(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { + MockHttpClient.VerifyNoOutstandingRequest(); + MockHttpClient.VerifyNoOutstandingExpectation(); } } } diff --git a/src/AzureIoTHub.Portal/Server/Extensions/DeviceModelCommandExtensions.cs b/src/AzureIoTHub.Portal/Server/Extensions/DeviceModelCommandExtensions.cs deleted file mode 100644 index 81c74d0b1..000000000 --- a/src/AzureIoTHub.Portal/Server/Extensions/DeviceModelCommandExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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.Extensions -{ - using System; - using System.Text; - using AzureIoTHub.Portal.Models.v10.LoRaWAN; - - public static class DeviceModelCommandExtensions - { - public static dynamic ToDynamic(this DeviceModelCommand command) - { - return command.Confirmed - ? (new - { - rawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame)), - fport = command.Port, - confirmed = command.Confirmed - }) - : (new - { - rawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame)), - fport = command.Port - }); - } - } -} diff --git a/src/AzureIoTHub.Portal/Server/Managers/LoraDeviceMethodManager.cs b/src/AzureIoTHub.Portal/Server/Managers/LoraDeviceMethodManager.cs index ba99a97c8..3593756a2 100644 --- a/src/AzureIoTHub.Portal/Server/Managers/LoraDeviceMethodManager.cs +++ b/src/AzureIoTHub.Portal/Server/Managers/LoraDeviceMethodManager.cs @@ -7,10 +7,10 @@ namespace AzureIoTHub.Portal.Server.Managers using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; + using System.Text; using System.Threading; using System.Threading.Tasks; using AzureIoTHub.Portal.Models.v10.LoRaWAN; - using AzureIoTHub.Portal.Server.Extensions; public class LoraDeviceMethodManager : ILoraDeviceMethodManager { @@ -26,7 +26,12 @@ public async Task ExecuteLoRaDeviceMessage(string deviceId, ArgumentNullException.ThrowIfNull(deviceId, nameof(deviceId)); ArgumentNullException.ThrowIfNull(command, nameof(command)); - var body = command.ToDynamic(); + var body = new LoRaCloudToDeviceMessage + { + RawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame)), + Fport = command.Port, + Confirmed = command.Confirmed + }; using var commandContent = JsonContent.Create(body); diff --git a/src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaCloudToDeviceMessage.cs b/src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaCloudToDeviceMessage.cs new file mode 100644 index 000000000..94a4f01f0 --- /dev/null +++ b/src/AzureIoTHub.Portal/Shared/Models/v1.0/LoRaWAN/LoRaCloudToDeviceMessage.cs @@ -0,0 +1,19 @@ +// 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.Models.v10.LoRaWAN +{ + using System.Text.Json.Serialization; + + public class LoRaCloudToDeviceMessage + { + [JsonPropertyName("rawPayload")] + public string RawPayload { get; set; } + + [JsonPropertyName("fport")] + public int Fport { get; set; } + + [JsonPropertyName("confirmed")] + public bool Confirmed { get; set; } + } +}