Create Device Registry interface + Add IoT Hub Registry Manager Impl (#1607)

* Create Device Registry interface + Add IoT Hub Registry Manager Impl

* Suppress some CA200 warnings

* Simplify statements in IoTHubRegistryManager

* Move IDisposable from IDeviceRegistryManager to the implementation

* Make IoTHubRegistryManager constructor internal + use concret class in unit tests

Co-authored-by: Daniele Antonio Maggio <1955514+danigian@users.noreply.github.com>
This commit is contained in:
Kevin BEAUGRAND 2022-05-10 18:35:56 +02:00 коммит произвёл GitHub
Родитель 133d9d12ed
Коммит 966dfe21e3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 576 добавлений и 74 удалений

Просмотреть файл

@ -17,7 +17,6 @@ namespace LoraKeysManagerFacade
using LoRaWan;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Azure;
@ -31,11 +30,11 @@ namespace LoraKeysManagerFacade
internal const string CupsPropertyName = "cups";
internal const string CupsCredentialsUrlPropertyName = "cupsCredentialUrl";
internal const string LnsCredentialsUrlPropertyName = "tcCredentialUrl";
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly IAzureClientFactory<BlobServiceClient> azureClientFactory;
private readonly ILogger<ConcentratorCredentialsFunction> logger;
public ConcentratorCredentialsFunction(RegistryManager registryManager,
public ConcentratorCredentialsFunction(IDeviceRegistryManager registryManager,
IAzureClientFactory<BlobServiceClient> azureClientFactory,
ILogger<ConcentratorCredentialsFunction> logger)
{

Просмотреть файл

@ -16,7 +16,6 @@ namespace LoraKeysManagerFacade
using LoRaWan;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Azure;
@ -29,11 +28,11 @@ namespace LoraKeysManagerFacade
internal const string CupsPropertyName = "cups";
internal const string CupsFwUrlPropertyName = "fwUrl";
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly IAzureClientFactory<BlobServiceClient> azureClientFactory;
private readonly ILogger<ConcentratorFirmwareFunction> logger;
public ConcentratorFirmwareFunction(RegistryManager registryManager,
public ConcentratorFirmwareFunction(IDeviceRegistryManager registryManager,
IAzureClientFactory<BlobServiceClient> azureClientFactory,
ILogger<ConcentratorFirmwareFunction> logger)
{

Просмотреть файл

@ -10,6 +10,7 @@ namespace LoraKeysManagerFacade
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using LoRaTools;
using LoRaWan;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Devices;
@ -28,10 +29,10 @@ namespace LoraKeysManagerFacade
private const string HostAddressPropertyName = "hostAddress";
private const string NetworkId = "quickstartnetwork";
private static readonly Uri DefaultHostAddress = new Uri("ws://mylns:5000");
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly IHttpClientFactory httpClientFactory;
public CreateEdgeDevice(RegistryManager registryManager, IHttpClientFactory httpClientFactory)
public CreateEdgeDevice(IDeviceRegistryManager registryManager, IHttpClientFactory httpClientFactory)
{
this.registryManager = registryManager;
this.httpClientFactory = httpClientFactory;

Просмотреть файл

@ -11,7 +11,6 @@ namespace LoraKeysManagerFacade
using LoRaWan;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
@ -19,11 +18,11 @@ namespace LoraKeysManagerFacade
public class DeviceGetter
{
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly ILoRaDeviceCacheStore cacheStore;
private readonly ILogger<DeviceGetter> logger;
public DeviceGetter(RegistryManager registryManager, ILoRaDeviceCacheStore cacheStore, ILogger<DeviceGetter> logger)
public DeviceGetter(IDeviceRegistryManager registryManager, ILoRaDeviceCacheStore cacheStore, ILogger<DeviceGetter> logger)
{
this.registryManager = registryManager;
this.cacheStore = cacheStore;

Просмотреть файл

@ -7,6 +7,7 @@ namespace LoraKeysManagerFacade
{
using System;
using LoraKeysManagerFacade.FunctionBundler;
using LoRaTools;
using LoRaTools.ADR;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
@ -42,10 +43,6 @@ namespace LoraKeysManagerFacade
var redisCache = redis.GetDatabase();
var deviceCacheStore = new LoRaDeviceCacheRedisStore(redisCache);
#pragma warning disable CA2000 // Dispose objects before losing scope
// Object is handled by DI container.
_ = builder.Services.AddSingleton(RegistryManager.CreateFromConnectionString(iotHubConnectionString));
#pragma warning restore CA2000 // Dispose objects before losing scope
builder.Services.AddAzureClients(builder =>
{
_ = builder.AddBlobServiceClient(configHandler.StorageConnectionString)
@ -53,6 +50,7 @@ namespace LoraKeysManagerFacade
});
_ = builder.Services
.AddHttpClient()
.AddSingleton<IDeviceRegistryManager>(IoTHubRegistryManager.CreateWithProvider(() => RegistryManager.CreateFromConnectionString(iotHubConnectionString)))
.AddSingleton<IServiceClient>(new ServiceClientAdapter(ServiceClient.CreateFromConnectionString(iotHubConnectionString)))
.AddSingleton<ILoRaDeviceCacheStore>(deviceCacheStore)
.AddSingleton<ILoRaADRManager>(sp => new LoRaADRServerManager(new LoRaADRRedisStore(redisCache, sp.GetRequiredService<ILogger<LoRaADRRedisStore>>()),

Просмотреть файл

@ -8,9 +8,9 @@ namespace LoraKeysManagerFacade
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using LoRaTools;
using LoRaTools.Utils;
using LoRaWan;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Common.Exceptions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@ -57,7 +57,7 @@ namespace LoraKeysManagerFacade
private static string GenerateKey(DevAddr devAddr) => CacheKeyPrefix + devAddr;
public LoRaDevAddrCache(ILoRaDeviceCacheStore cacheStore, RegistryManager registryManager, ILogger logger, string gatewayId)
public LoRaDevAddrCache(ILoRaDeviceCacheStore cacheStore, IDeviceRegistryManager registryManager, ILogger logger, string gatewayId)
{
this.cacheStore = cacheStore;
this.logger = logger ?? NullLogger.Instance;
@ -110,7 +110,7 @@ namespace LoraKeysManagerFacade
this.logger.LogInformation($"Successfully saved dev address info on dictionary key: {cacheKeyToUse}, hashkey: {info.DevEUI}, object: {serializedObjectValue}");
}
internal async Task PerformNeededSyncs(RegistryManager registryManager)
internal async Task PerformNeededSyncs(IDeviceRegistryManager registryManager)
{
// If a full update is expected
if (await this.cacheStore.LockTakeAsync(FullUpdateLockKey, this.lockOwner, FullUpdateKeyTimeSpan, block: false))
@ -191,7 +191,7 @@ namespace LoraKeysManagerFacade
/// <summary>
/// Perform a full relaoad on the dev address cache. This occur typically once every 24 h.
/// </summary>
private async Task PerformFullReload(RegistryManager registryManager)
private async Task PerformFullReload(IDeviceRegistryManager registryManager)
{
var query = $"SELECT * FROM devices WHERE is_defined(properties.desired.AppKey) OR is_defined(properties.desired.AppSKey) OR is_defined(properties.desired.NwkSKey)";
var devAddrCacheInfos = await GetDeviceTwinsFromIotHub(registryManager, query);
@ -201,7 +201,7 @@ namespace LoraKeysManagerFacade
/// <summary>
/// Method performing a deltaReload. Typically occur every 5 minutes.
/// </summary>
private async Task PerformDeltaReload(RegistryManager registryManager)
private async Task PerformDeltaReload(IDeviceRegistryManager registryManager)
{
// if the value is null (first call), we take five minutes before this call
var lastUpdate = this.cacheStore.StringGet(LastDeltaUpdateKeyValue) ?? DateTime.UtcNow.AddMinutes(-5).ToString(LoraKeysManagerFacadeConstants.RoundTripDateTimeStringFormat, CultureInfo.InvariantCulture);
@ -210,7 +210,7 @@ namespace LoraKeysManagerFacade
BulkSaveDevAddrCache(devAddrCacheInfos, false);
}
private async Task<List<DevAddrCacheInfo>> GetDeviceTwinsFromIotHub(RegistryManager registryManager, string inputQuery)
private async Task<List<DevAddrCacheInfo>> GetDeviceTwinsFromIotHub(IDeviceRegistryManager registryManager, string inputQuery)
{
var query = registryManager.CreateQuery(inputQuery);
var lastQueryTs = DateTime.UtcNow.AddSeconds(-10); // account for some clock drift

Просмотреть файл

@ -9,17 +9,16 @@ namespace LoraKeysManagerFacade
using LoRaWan;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
public class SearchDeviceByDevEUI
{
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly ILogger<SearchDeviceByDevEUI> logger;
public SearchDeviceByDevEUI(RegistryManager registryManager, ILogger<SearchDeviceByDevEUI> logger)
public SearchDeviceByDevEUI(IDeviceRegistryManager registryManager, ILogger<SearchDeviceByDevEUI> logger)
{
this.registryManager = registryManager;
this.logger = logger;

Просмотреть файл

@ -31,11 +31,11 @@ namespace LoraKeysManagerFacade
public class SendCloudToDeviceMessage
{
private readonly ILoRaDeviceCacheStore cacheStore;
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly IServiceClient serviceClient;
private readonly ILogger log;
public SendCloudToDeviceMessage(ILoRaDeviceCacheStore cacheStore, RegistryManager registryManager, IServiceClient serviceClient, ILogger<SendCloudToDeviceMessage> log)
public SendCloudToDeviceMessage(ILoRaDeviceCacheStore cacheStore, IDeviceRegistryManager registryManager, IServiceClient serviceClient, ILogger<SendCloudToDeviceMessage> log)
{
this.cacheStore = cacheStore;
this.registryManager = registryManager;

Просмотреть файл

@ -5,16 +5,16 @@ namespace LoraKeysManagerFacade
{
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Devices;
using LoRaTools;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
public class SyncDevAddrCache
{
private readonly LoRaDevAddrCache loRaDevAddrCache;
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
public SyncDevAddrCache(LoRaDevAddrCache loRaDevAddrCache, RegistryManager registryManager)
public SyncDevAddrCache(LoRaDevAddrCache loRaDevAddrCache, IDeviceRegistryManager registryManager)
{
this.loRaDevAddrCache = loRaDevAddrCache;
this.registryManager = registryManager;

Просмотреть файл

@ -36,7 +36,7 @@ namespace LoRaWan.NetworkServerDiscovery
private readonly ILogger<TagBasedLnsDiscovery> logger;
private readonly IMemoryCache memoryCache;
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
private readonly Dictionary<StationEui, Uri> lastLnsUriByStationId = new();
private readonly object lastLnsUriByStationIdLock = new();
private readonly SemaphoreSlim lnsByNetworkCacheSemaphore = new SemaphoreSlim(1);
@ -45,27 +45,27 @@ namespace LoRaWan.NetworkServerDiscovery
: this(memoryCache, InitializeRegistryManager(configuration, logger), logger)
{ }
private static RegistryManager InitializeRegistryManager(IConfiguration configuration, ILogger logger)
private static IDeviceRegistryManager InitializeRegistryManager(IConfiguration configuration, ILogger logger)
{
var iotHubConnectionString = configuration.GetConnectionString(IotHubConnectionStringName);
if (!string.IsNullOrEmpty(iotHubConnectionString))
{
logger.LogInformation("Using connection string based auth for IoT Hub.");
return RegistryManager.CreateFromConnectionString(iotHubConnectionString);
return IoTHubRegistryManager.CreateWithProvider(() => RegistryManager.CreateFromConnectionString(iotHubConnectionString));
}
else
{
var hostName = configuration.GetValue<string>(HostName);
if (string.IsNullOrEmpty(hostName))
throw new InvalidOperationException($"Specify either 'ConnectionStrings__{IotHubConnectionStringName}' or '{HostName}'.");
var hostName = configuration.GetValue<string>(HostName);
logger.LogInformation("Using managed identity based auth for IoT Hub.");
return RegistryManager.Create(hostName, new ManagedIdentityCredential());
}
if (string.IsNullOrEmpty(hostName))
throw new InvalidOperationException($"Specify either 'ConnectionStrings__{IotHubConnectionStringName}' or '{HostName}'.");
logger.LogInformation("Using managed identity based auth for IoT Hub.");
return IoTHubRegistryManager.CreateWithProvider(() =>
RegistryManager.Create(hostName, new ManagedIdentityCredential()));
}
internal TagBasedLnsDiscovery(IMemoryCache memoryCache, RegistryManager registryManager, ILogger<TagBasedLnsDiscovery> logger)
internal TagBasedLnsDiscovery(IMemoryCache memoryCache, IDeviceRegistryManager registryManager, ILogger<TagBasedLnsDiscovery> logger)
{
this.memoryCache = memoryCache;
this.registryManager = registryManager;

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace LoRaTools
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
public interface IDeviceRegistryManager
{
Task<Twin> GetTwinAsync(string deviceId, CancellationToken cancellationToken);
Task<Device> GetDeviceAsync(string deviceId);
Task<Twin> UpdateTwinAsync(string deviceId, string moduleId, Twin deviceTwin, string eTag, CancellationToken cancellationToken);
Task<Twin> UpdateTwinAsync(string deviceId, string moduleId, Twin deviceTwin, string eTag);
Task<Twin> UpdateTwinAsync(string deviceId, Twin twin, string eTag, CancellationToken cancellationToken);
Task<BulkRegistryOperationResult> AddDeviceWithTwinAsync(Device device, Twin twin);
IQuery CreateQuery(string query);
IQuery CreateQuery(string query, int? pageSize);
Task<Device> AddDeviceAsync(Device edgeGatewayDevice);
Task<Module> AddModuleAsync(Module moduleToAdd);
Task ApplyConfigurationContentOnDeviceAsync(string deviceName, ConfigurationContent deviceConfigurationContent);
Task<Twin> GetTwinAsync(string deviceName);
Task<Configuration> AddConfigurationAsync(Configuration configuration);
Task<Twin> UpdateTwinAsync(string deviceName, Twin twin, string eTag);
Task RemoveDeviceAsync(string deviceId);
}
}

Просмотреть файл

@ -0,0 +1,67 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace LoRaTools
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
public sealed class IoTHubRegistryManager : IDeviceRegistryManager, IDisposable
{
private readonly RegistryManager instance;
public static IDeviceRegistryManager CreateWithProvider(Func<RegistryManager> registryManagerProvider)
{
return registryManagerProvider == null
? throw new ArgumentNullException(nameof(registryManagerProvider))
: (IDeviceRegistryManager)new IoTHubRegistryManager(registryManagerProvider);
}
internal IoTHubRegistryManager(Func<RegistryManager> registryManagerProvider)
{
this.instance = registryManagerProvider() ?? throw new InvalidOperationException("RegistryManager provider provided a null RegistryManager.");
}
public Task<Configuration> AddConfigurationAsync(Configuration configuration) => this.instance.AddConfigurationAsync(configuration);
public Task<Device> AddDeviceAsync(Device edgeGatewayDevice) => this.instance.AddDeviceAsync(edgeGatewayDevice);
public Task<BulkRegistryOperationResult> AddDeviceWithTwinAsync(Device device, Twin twin) => this.instance.AddDeviceWithTwinAsync(device, twin);
public Task<Module> AddModuleAsync(Module moduleToAdd) => this.instance.AddModuleAsync(moduleToAdd);
public Task ApplyConfigurationContentOnDeviceAsync(string deviceName, ConfigurationContent deviceConfigurationContent)
=> this.instance.ApplyConfigurationContentOnDeviceAsync(deviceName, deviceConfigurationContent);
public IQuery CreateQuery(string query) => this.instance.CreateQuery(query);
public IQuery CreateQuery(string query, int? pageSize) => this.instance.CreateQuery(query, pageSize);
public void Dispose() => this.instance?.Dispose();
public Task<Device> GetDeviceAsync(string deviceId) => this.instance.GetDeviceAsync(deviceId);
public Task<Twin> GetTwinAsync(string deviceId, CancellationToken cancellationToken)
=> this.instance.GetTwinAsync(deviceId, cancellationToken);
public Task<Twin> GetTwinAsync(string deviceName) => this.instance.GetTwinAsync(deviceName);
public Task<Twin> UpdateTwinAsync(string deviceId, string moduleId, Twin deviceTwin, string eTag)
=> this.instance.UpdateTwinAsync(deviceId, moduleId, deviceTwin, eTag);
public Task<Twin> UpdateTwinAsync(string deviceId, Twin twin, string eTag, CancellationToken cancellationToken)
=> this.instance.UpdateTwinAsync(deviceId, twin, eTag, cancellationToken);
public Task<Twin> UpdateTwinAsync(string deviceName, Twin twin, string eTag)
=> this.instance.UpdateTwinAsync(deviceName, twin, eTag);
public Task<Twin> UpdateTwinAsync(string deviceId, string moduleId, Twin deviceTwin, string eTag, CancellationToken cancellationToken)
=> this.instance.UpdateTwinAsync(deviceId, moduleId, deviceTwin, eTag, cancellationToken);
public Task RemoveDeviceAsync(string deviceId)
=> this.instance.RemoveDeviceAsync(deviceId);
}
}

Просмотреть файл

@ -5,6 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
<PackageReference Include="Microsoft.Azure.Devices" Version="$(MicrosoftAzureDevicesVersion)" />
<PackageReference Include="Microsoft.Azure.Devices.Client" Version="$(MicrosoftAzureDevicesClientVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsVersion)" />
<PackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpVersion)" />

Просмотреть файл

@ -9,6 +9,7 @@ namespace LoRaWan.Tests.Common
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LoRaTools;
using LoRaTools.CommonAPI;
using LoRaTools.Utils;
using LoRaWan.NetworkServer.BasicsStation;
@ -30,7 +31,7 @@ namespace LoRaWan.Tests.Common
private const int C2dExpiryTime = 5;
public const string MESSAGE_IDENTIFIER_PROPERTY_NAME = "messageIdentifier";
private RegistryManager registryManager;
private IDeviceRegistryManager registryManager;
private TcpLogListener tcpLogListener;
public TestConfiguration Configuration { get; }
@ -113,9 +114,9 @@ namespace LoRaWan.Tests.Common
}
}
private RegistryManager GetRegistryManager()
private IDeviceRegistryManager GetRegistryManager()
{
return this.registryManager ??= RegistryManager.CreateFromConnectionString(Configuration.IoTHubConnectionString);
return this.registryManager ??= IoTHubRegistryManager.CreateWithProvider(() => RegistryManager.CreateFromConnectionString(Configuration.IoTHubConnectionString));
}
public async Task<Twin> GetTwinAsync(string deviceId)
@ -444,8 +445,6 @@ namespace LoRaWan.Tests.Common
AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException;
IoTHubMessages = null;
this.registryManager?.Dispose();
this.registryManager = null;
this.tcpLogListener?.Dispose();
this.tcpLogListener = null;

Просмотреть файл

@ -57,13 +57,14 @@ namespace LoRaWan.Tests.E2E
StationInfo.GroupJoin(LnsInfo, station => station.NetworkId, lns => lns.NetworkId, (s, ls) => (s.StationEui, LnsInfo: ls))
.ToImmutableDictionary(x => x.StationEui, x => x.LnsInfo.ToImmutableArray());
private readonly RegistryManager registryManager;
private readonly IDeviceRegistryManager registryManager;
public LnsDiscoveryFixture() =>
this.registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.GetConfiguration().IoTHubConnectionString);
this.registryManager = IoTHubRegistryManager.CreateWithProvider(() => RegistryManager.CreateFromConnectionString(TestConfiguration.GetConfiguration().IoTHubConnectionString));
public void Dispose() =>
this.registryManager.Dispose();
public void Dispose()
{
}
public Task DisposeAsync() =>
Task.WhenAll(from deviceId in LnsInfo.Select(l => l.DeviceId.ToString())

Просмотреть файл

@ -10,6 +10,7 @@ namespace LoRaWan.Tests.Integration
using System.Text;
using System.Threading.Tasks;
using LoraKeysManagerFacade;
using LoRaTools;
using LoRaWan.Tests.Common;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
@ -39,11 +40,11 @@ namespace LoRaWan.Tests.Integration
this.testOutputHelper = testOutputHelper;
}
private static Mock<RegistryManager> InitRegistryManager(List<DevAddrCacheInfo> deviceIds, int numberOfDeviceDeltaUpdates = 2)
private static Mock<IDeviceRegistryManager> InitRegistryManager(List<DevAddrCacheInfo> deviceIds, int numberOfDeviceDeltaUpdates = 2)
{
var currentDevAddrContext = new List<DevAddrCacheInfo>();
var currentDevices = deviceIds;
var mockRegistryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var mockRegistryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var hasMoreShouldReturn = true;
var primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
@ -907,7 +908,7 @@ namespace LoRaWan.Tests.Integration
private static DevAddr CreateDevAddr() => new DevAddr((uint)RandomNumberGenerator.GetInt32(int.MaxValue));
private DeviceGetter SetupDeviceGetter(RegistryManager registryManager) =>
private DeviceGetter SetupDeviceGetter(IDeviceRegistryManager registryManager) =>
new DeviceGetter(registryManager, this.cache, new TestOutputLogger<DeviceGetter>(this.testOutputHelper));
}
}

Просмотреть файл

@ -33,13 +33,13 @@ namespace LoRaWan.Tests.Integration
internal sealed class LnsDiscoveryApplication : WebApplicationFactory<Program>
{
public Mock<RegistryManager>? RegistryManagerMock { get; private set; }
public Mock<IDeviceRegistryManager>? RegistryManagerMock { get; private set; }
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
RegistryManagerMock = new Mock<RegistryManager>();
RegistryManagerMock = new Mock<IDeviceRegistryManager>();
services.RemoveAll<ILnsDiscovery>();
services.AddSingleton<ILnsDiscovery>(sp => new TagBasedLnsDiscovery(sp.GetRequiredService<IMemoryCache>(), RegistryManagerMock.Object, sp.GetRequiredService<ILogger<TagBasedLnsDiscovery>>()));
});

Просмотреть файл

@ -0,0 +1,404 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace LoRaWan.Tests.Unit
{
using System.Threading;
using System.Threading.Tasks;
using global::LoRaTools;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Moq;
using Moq.Protected;
using Xunit;
public class IoTHubRegistryManagerTests
{
private readonly MockRepository mockRepository;
private readonly Mock<RegistryManager> mockRegistryManager;
public IoTHubRegistryManagerTests()
{
this.mockRepository = new MockRepository(MockBehavior.Strict);
this.mockRegistryManager = this.mockRepository.Create<RegistryManager>();
}
private IoTHubRegistryManager CreateManager()
{
this.mockRegistryManager.Protected().Setup("Dispose", ItExpr.Is<bool>(_ => true));
return new IoTHubRegistryManager(() => this.mockRegistryManager.Object);
}
[Fact]
public async Task AddConfigurationAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var configuration = new Configuration("testConfiguration");
this.mockRegistryManager.Setup(c => c.AddConfigurationAsync(It.Is<Configuration>(x => x == configuration)))
.ReturnsAsync(configuration);
// Act
var result = await manager.AddConfigurationAsync(configuration);
// Assert
Assert.Equal(configuration, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task AddDeviceAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var edgeGatewayDevice = new Device("deviceid");
this.mockRegistryManager.Setup(c => c.AddDeviceAsync(It.Is<Device>(x => x == edgeGatewayDevice)))
.ReturnsAsync(edgeGatewayDevice);
// Act
var result = await manager.AddDeviceAsync(edgeGatewayDevice);
// Assert
Assert.Equal(edgeGatewayDevice, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task AddDeviceWithTwinAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var device = new Device("deviceid");
var twin = new Twin("deviceid");
this.mockRegistryManager.Setup(c => c.AddDeviceWithTwinAsync(
It.Is<Device>(x => x == device),
It.Is<Twin>(x => x == twin)))
.ReturnsAsync(new BulkRegistryOperationResult
{
IsSuccessful = true
});
// Act
var result = await manager.AddDeviceWithTwinAsync(device, twin);
// Assert
Assert.NotNull(result);
Assert.True(result.IsSuccessful);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task AddModuleAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var moduleToAdd = new Module();
this.mockRegistryManager.Setup(c => c.AddModuleAsync(
It.Is<Module>(x => x == moduleToAdd)))
.ReturnsAsync(moduleToAdd);
// Act
var result = await manager.AddModuleAsync(moduleToAdd);
// Assert
Assert.Equal(moduleToAdd, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task ApplyConfigurationContentOnDeviceAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceName = "deviceid";
var deviceConfigurationContent = new ConfigurationContent();
this.mockRegistryManager.Setup(c => c.ApplyConfigurationContentOnDeviceAsync(
It.Is<string>(x => x == deviceName),
It.Is<ConfigurationContent>(x => x == deviceConfigurationContent)))
.Returns(Task.CompletedTask);
// Act
await manager.ApplyConfigurationContentOnDeviceAsync(deviceName, deviceConfigurationContent);
}
// Assert
this.mockRepository.VerifyAll();
}
[Fact]
public void CreateQuery()
{
// Arrange
using (var manager = this.CreateManager())
{
var query = "new query";
var mockQuery = this.mockRepository.Create<IQuery>();
this.mockRegistryManager.Setup(c => c.CreateQuery(
It.Is<string>(x => x == query)))
.Returns(mockQuery.Object);
// Act
var result = manager.CreateQuery(query);
// Assert
Assert.Equal(mockQuery.Object, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public void CreateQuery_WithPageSize()
{
// Arrange
using (var manager = this.CreateManager())
{
var pageSize = 10;
var query = "new query";
var mockQuery = this.mockRepository.Create<IQuery>();
this.mockRegistryManager.Setup(c => c.CreateQuery(
It.Is<string>(x => x == query),
It.Is<int>(x => x == pageSize)))
.Returns(mockQuery.Object);
// Act
var result = manager.CreateQuery(query, pageSize);
// Assert
Assert.Equal(mockQuery.Object, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task GetDeviceAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var device = new Device(deviceId);
this.mockRegistryManager.Setup(c => c.GetDeviceAsync(
It.Is<string>(x => x == deviceId)))
.ReturnsAsync(device);
// Act
var result = await manager.GetDeviceAsync(deviceId);
// Assert
Assert.Equal(device, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task GetTwinAsync_With_CancellationToken()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var cancellationToken = CancellationToken.None;
var twin = new Twin(deviceId);
this.mockRegistryManager.Setup(c => c.GetTwinAsync(
It.Is<string>(x => x == deviceId),
It.Is<CancellationToken>(x => x == cancellationToken)))
.ReturnsAsync(twin);
// Act
var result = await manager.GetTwinAsync(deviceId, cancellationToken);
// Assert
Assert.Equal(twin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task GetTwinAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var twin = new Twin(deviceId);
this.mockRegistryManager.Setup(c => c.GetTwinAsync(
It.Is<string>(x => x == deviceId)))
.ReturnsAsync(twin);
// Act
var result = await manager.GetTwinAsync(deviceId);
// Assert
Assert.Equal(twin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task UpdateTwinAsync_With_Module()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var moduleId = "moduleid";
var deviceTwin = new Twin();
var eTag = "eTag";
this.mockRegistryManager.Setup(c => c.UpdateTwinAsync(
It.Is<string>(x => x == deviceId),
It.Is<string>(x => x == moduleId),
It.Is<Twin>(x => x == deviceTwin),
It.Is<string>(x => x == eTag)))
.ReturnsAsync(deviceTwin);
// Act
var result = await manager.UpdateTwinAsync(deviceId, moduleId, deviceTwin, eTag);
// Assert
Assert.Equal(deviceTwin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task UpdateTwinAsync_With_CancellationToken()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var deviceTwin = new Twin();
var eTag = "eTag";
var cancellationToken = CancellationToken.None;
this.mockRegistryManager.Setup(c => c.UpdateTwinAsync(
It.Is<string>(x => x == deviceId),
It.Is<Twin>(x => x == deviceTwin),
It.Is<string>(x => x == eTag),
It.Is<CancellationToken>(x => x == cancellationToken)))
.ReturnsAsync(deviceTwin);
// Act
var result = await manager.UpdateTwinAsync(deviceId, deviceTwin, eTag, cancellationToken);
// Assert
Assert.Equal(deviceTwin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task UpdateTwinAsync2()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var deviceTwin = new Twin();
var eTag = "eTag";
this.mockRegistryManager.Setup(c => c.UpdateTwinAsync(
It.Is<string>(x => x == deviceId),
It.Is<Twin>(x => x == deviceTwin),
It.Is<string>(x => x == eTag)))
.ReturnsAsync(deviceTwin);
// Act
var result = await manager.UpdateTwinAsync(deviceId, deviceTwin, eTag);
// Assert
Assert.Equal(deviceTwin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task UpdateTwinAsync_With_Module_And_CancellationToken()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
var moduleId = "moduleid";
var deviceTwin = new Twin();
var eTag = "eTag";
var cancellationToken = CancellationToken.None;
this.mockRegistryManager.Setup(c => c.UpdateTwinAsync(
It.Is<string>(x => x == deviceId),
It.Is<string>(x => x == moduleId),
It.Is<Twin>(x => x == deviceTwin),
It.Is<string>(x => x == eTag),
It.Is<CancellationToken>(x => x == cancellationToken)))
.ReturnsAsync(deviceTwin);
// Act
var result = await manager.UpdateTwinAsync(
deviceId,
moduleId,
deviceTwin,
eTag,
cancellationToken);
// Assert
Assert.Equal(deviceTwin, result);
}
this.mockRepository.VerifyAll();
}
[Fact]
public async Task RemoveDeviceAsync()
{
// Arrange
using (var manager = this.CreateManager())
{
var deviceId = "deviceid";
this.mockRegistryManager.Setup(c => c.RemoveDeviceAsync(
It.Is<string>(x => x == deviceId)))
.Returns(Task.CompletedTask);
// Act
await manager.RemoveDeviceAsync(
deviceId);
}
// Assert
this.mockRepository.VerifyAll();
}
}
}

Просмотреть файл

@ -12,11 +12,11 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using global::LoraKeysManagerFacade;
using global::LoRaTools;
using global::LoRaTools.CommonAPI;
using LoRaWan.Tests.Common;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Logging.Abstractions;
@ -26,7 +26,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
public class ConcentratorCredentialTests
{
private readonly Mock<RegistryManager> registryManager;
private readonly Mock<IDeviceRegistryManager> registryManager;
private readonly Mock<IAzureClientFactory<BlobServiceClient>> azureClientFactory;
private readonly ConcentratorCredentialsFunction concentratorCredential;
private readonly StationEui stationEui = StationEui.Parse("001122FFFEAABBCC");
@ -35,7 +35,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
public ConcentratorCredentialTests()
{
this.registryManager = new Mock<RegistryManager>();
this.registryManager = new Mock<IDeviceRegistryManager>();
this.azureClientFactory = new Mock<IAzureClientFactory<BlobServiceClient>>();
this.concentratorCredential = new ConcentratorCredentialsFunction(registryManager.Object, azureClientFactory.Object, NullLogger<ConcentratorCredentialsFunction>.Instance);
}

Просмотреть файл

@ -13,10 +13,10 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using global::LoraKeysManagerFacade;
using global::LoRaTools;
using LoRaWan.Tests.Common;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Logging.Abstractions;
@ -30,7 +30,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
private readonly StationEui testStationEui = StationEui.Parse("11-11-11-11-11-11-11-11");
private readonly Mock<RegistryManager> registryManager;
private readonly Mock<IDeviceRegistryManager> registryManager;
private readonly Mock<BlobClient> blobClient;
private readonly ConcentratorFirmwareFunction concentratorFirmware;
@ -52,7 +52,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
azureClientFactory.Setup(m => m.CreateClient(FacadeStartup.WebJobsStorageClientName))
.Returns(blobServiceClient.Object);
this.registryManager = new Mock<RegistryManager>();
this.registryManager = new Mock<IDeviceRegistryManager>();
this.concentratorFirmware = new ConcentratorFirmwareFunction(this.registryManager.Object, azureClientFactory.Object, NullLogger<ConcentratorFirmwareFunction>.Instance);
}

Просмотреть файл

@ -6,6 +6,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
using System;
using System.Text;
using global::LoraKeysManagerFacade;
using global::LoRaTools;
using LoRaWan.Tests.Common;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
@ -30,9 +31,9 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
Assert.Equal(devEui, items[0].DevEUI);
}
private static RegistryManager InitRegistryManager(DevEui devEui)
private static IDeviceRegistryManager InitRegistryManager(DevEui devEui)
{
var mockRegistryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var mockRegistryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
mockRegistryManager
.Setup(x => x.GetDeviceAsync(It.Is(devEui.ToString(), StringComparer.Ordinal)))

Просмотреть файл

@ -7,6 +7,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
using System.Text;
using System.Threading.Tasks;
using global::LoraKeysManagerFacade;
using global::LoRaTools;
using global::LoRaTools.CommonAPI;
using LoRaWan.Tests.Common;
using Microsoft.AspNetCore.Http;
@ -28,7 +29,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
{
var ctx = new DefaultHttpContext();
ctx.Request.QueryString = new QueryString($"?{ApiVersion.QueryStringParamName}={ApiVersion.Version_2019_02_12_Preview.Version}");
var registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var searchDeviceByDevEUI = SetupSubject(registryManager.Object);
var result = await searchDeviceByDevEUI.GetDeviceByDevEUI(ctx.Request);
Assert.IsType<BadRequestObjectResult>(result);
@ -39,7 +40,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
{
var ctx = new DefaultHttpContext();
ctx.Request.QueryString = new QueryString($"?devEUI=193123");
var registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var searchDeviceByDevEUI = SetupSubject(registryManager.Object);
var result = await searchDeviceByDevEUI.GetDeviceByDevEUI(ctx.Request);
Assert.IsType<BadRequestObjectResult>(result);
@ -52,7 +53,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
{
var ctx = new DefaultHttpContext();
ctx.Request.QueryString = new QueryString($"?devEUI=193123&{ApiVersion.QueryStringParamName}={version}");
var registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var searchDeviceByDevEUI = SetupSubject(registryManager.Object);
var result = await searchDeviceByDevEUI.GetDeviceByDevEUI(ctx.Request);
Assert.IsType<BadRequestObjectResult>(result);
@ -67,7 +68,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
var ctx = new DefaultHttpContext();
ctx.Request.QueryString = new QueryString($"?devEUI={devEUI.ToString(format, null)}&{ApiVersion.QueryStringParamName}={ApiVersion.LatestVersion}");
var registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
registryManager.Setup(x => x.GetDeviceAsync(devEUI.ToString()))
.ReturnsAsync((Device)null);
@ -115,12 +116,12 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
registryManager.VerifyAll();
}
private static (Mock<RegistryManager>, HttpRequest) SetupIotHubQuery(string devEui, string primaryKey)
private static (Mock<IDeviceRegistryManager>, HttpRequest) SetupIotHubQuery(string devEui, string primaryKey)
{
var ctx = new DefaultHttpContext();
ctx.Request.QueryString = new QueryString($"?devEUI={devEui}&{ApiVersion.QueryStringParamName}={ApiVersion.LatestVersion}");
var registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
var registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
var deviceInfo = new Device(devEui)
{
Authentication = new AuthenticationMechanism() { SymmetricKey = new SymmetricKey() { PrimaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(primaryKey)) } },
@ -132,7 +133,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
return (registryManager, ctx.Request);
}
private static SearchDeviceByDevEUI SetupSubject(RegistryManager registryManager) =>
private static SearchDeviceByDevEUI SetupSubject(IDeviceRegistryManager registryManager) =>
new SearchDeviceByDevEUI(registryManager, NullLogger<SearchDeviceByDevEUI>.Instance);
}
}

Просмотреть файл

@ -10,6 +10,7 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
using System.Text;
using System.Threading.Tasks;
using global::LoraKeysManagerFacade;
using global::LoRaTools;
using global::LoRaTools.CommonAPI;
using LoRaWan.Tests.Common;
using Microsoft.AspNetCore.Http;
@ -28,14 +29,14 @@ namespace LoRaWan.Tests.Unit.LoraKeysManagerFacade
private readonly LoRaInMemoryDeviceStore cacheStore;
private readonly Mock<IServiceClient> serviceClient;
private readonly Mock<RegistryManager> registryManager;
private readonly Mock<IDeviceRegistryManager> registryManager;
private readonly SendCloudToDeviceMessage sendCloudToDeviceMessage;
public SendCloudToDeviceMessageTest()
{
this.cacheStore = new LoRaInMemoryDeviceStore();
this.serviceClient = new Mock<IServiceClient>(MockBehavior.Strict);
this.registryManager = new Mock<RegistryManager>(MockBehavior.Strict);
this.registryManager = new Mock<IDeviceRegistryManager>(MockBehavior.Strict);
this.sendCloudToDeviceMessage = new SendCloudToDeviceMessage(this.cacheStore, this.registryManager.Object, this.serviceClient.Object, new NullLogger<SendCloudToDeviceMessage>());
}

Просмотреть файл

@ -11,6 +11,7 @@ namespace LoRaWan.Tests.Unit.NetworkServerDiscovery
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using global::LoRaTools;
using LoRaWan.NetworkServerDiscovery;
using LoRaWan.Tests.Common;
using Microsoft.Azure.Devices;
@ -25,13 +26,13 @@ namespace LoRaWan.Tests.Unit.NetworkServerDiscovery
private static readonly StationEui StationEui = new StationEui(1);
private static readonly string[] LnsUris = new[] { "ws://foo:5000/bar/baz", "wss://baz:5001/baz", "ws://baz" };
private readonly Mock<RegistryManager> registryManagerMock;
private readonly Mock<IDeviceRegistryManager> registryManagerMock;
private readonly MemoryCache memoryCache;
private readonly TagBasedLnsDiscovery subject;
public TagBasedLnsDiscoveryTests()
{
this.registryManagerMock = new Mock<RegistryManager>();
this.registryManagerMock = new Mock<IDeviceRegistryManager>();
this.registryManagerMock.Setup(rm => rm.CreateQuery(It.IsAny<string>())).Returns(Mock.Of<IQuery>());
this.memoryCache = new MemoryCache(new MemoryCacheOptions());
this.subject = new TagBasedLnsDiscovery(memoryCache, this.registryManagerMock.Object, NullLogger<TagBasedLnsDiscovery>.Instance);