diff --git a/src/AzureIoTHub.Portal.Application/Mappers/AWS/AWSDeviceThingProfile.cs b/src/AzureIoTHub.Portal.Application/Mappers/AWS/AWSDeviceThingProfile.cs index 63605c5b2..98ba36af8 100644 --- a/src/AzureIoTHub.Portal.Application/Mappers/AWS/AWSDeviceThingProfile.cs +++ b/src/AzureIoTHub.Portal.Application/Mappers/AWS/AWSDeviceThingProfile.cs @@ -44,6 +44,16 @@ public AWSDeviceThingProfile() Name = att.Key, Value = att.Value }))); + + _ = CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.ThingId)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.ThingName)) + .ForMember(dest => dest.Version, opts => opts.MapFrom(src => src.Version)) + .ForMember(dest => dest.Tags, opts => opts.MapFrom(src => src.Attributes.Select(att => new DeviceTagValue + { + Name = att.Key, + Value = att.Value + }))); } private static MemoryStream EmptyPayload() diff --git a/src/AzureIoTHub.Portal.Domain/Repositories/IDeviceModelRepository.cs b/src/AzureIoTHub.Portal.Domain/Repositories/IDeviceModelRepository.cs index 3fc23d94b..13f1681ab 100644 --- a/src/AzureIoTHub.Portal.Domain/Repositories/IDeviceModelRepository.cs +++ b/src/AzureIoTHub.Portal.Domain/Repositories/IDeviceModelRepository.cs @@ -7,6 +7,6 @@ namespace AzureIoTHub.Portal.Domain.Repositories public interface IDeviceModelRepository : IRepository { - public DeviceModel? GetByName(string modelName); + Task GetByNameAsync(string modelName); } } diff --git a/src/AzureIoTHub.Portal.Domain/Repositories/IEdgeDeviceModelRepository.cs b/src/AzureIoTHub.Portal.Domain/Repositories/IEdgeDeviceModelRepository.cs index 5bf6d3fed..5f6516547 100644 --- a/src/AzureIoTHub.Portal.Domain/Repositories/IEdgeDeviceModelRepository.cs +++ b/src/AzureIoTHub.Portal.Domain/Repositories/IEdgeDeviceModelRepository.cs @@ -7,5 +7,6 @@ namespace AzureIoTHub.Portal.Domain.Repositories public interface IEdgeDeviceModelRepository : IRepository { + Task GetByNameAsync(string edgeModelDevice); } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJob.cs b/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJob.cs new file mode 100644 index 000000000..545c124b1 --- /dev/null +++ b/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJob.cs @@ -0,0 +1,168 @@ +// 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.Infrastructure.Jobs.AWS +{ + using System.Linq; + using System.Net; + using System.Threading.Tasks; + using Amazon.GreengrassV2; + using Amazon.GreengrassV2.Model; + using Amazon.IoT; + using Amazon.IoT.Model; + using AutoMapper; + using AzureIoTHub.Portal.Domain; + using AzureIoTHub.Portal.Domain.Entities; + using AzureIoTHub.Portal.Domain.Repositories; + using Microsoft.Extensions.Logging; + using Quartz; + using Quartz.Util; + + [DisallowConcurrentExecution] + public class SyncGreenGrassDevicesJob : IJob + { + + private readonly ILogger logger; + private readonly IMapper mapper; + private readonly IUnitOfWork unitOfWork; + private readonly IEdgeDeviceRepository edgeDeviceRepository; + private readonly IEdgeDeviceModelRepository edgeDeviceModelRepository; + private readonly IDeviceTagValueRepository deviceTagValueRepository; + private readonly IAmazonIoT amazonIoTClient; + private readonly IAmazonGreengrassV2 amazonGreenGrass; + + public SyncGreenGrassDevicesJob( + ILogger logger, + IMapper mapper, + IUnitOfWork unitOfWork, + IEdgeDeviceRepository edgeDeviceRepository, + IEdgeDeviceModelRepository edgeDeviceModelRepository, + IDeviceTagValueRepository deviceTagValueRepository, + IAmazonIoT amazonIoTClient, + IAmazonGreengrassV2 amazonGreenGrass) + { + this.mapper = mapper; + this.unitOfWork = unitOfWork; + this.edgeDeviceRepository = edgeDeviceRepository; + this.edgeDeviceModelRepository = edgeDeviceModelRepository; + this.deviceTagValueRepository = deviceTagValueRepository; + this.amazonIoTClient = amazonIoTClient; + this.amazonGreenGrass = amazonGreenGrass; + this.logger = logger; + } + + + public async Task Execute(IJobExecutionContext context) + { + try + { + this.logger.LogInformation("Start of sync GreenGrass Devices job"); + + await SyncGreenGrassDevicesAsEdgeDevices(); + + this.logger.LogInformation("End of sync GreenGrass Devices job"); + } + catch (Exception e) + { + this.logger.LogError(e, "Sync GreenGrass Devices job has failed"); + } + } + + private async Task SyncGreenGrassDevicesAsEdgeDevices() + { + var things = await GetAllGreenGrassDevices(); + + foreach (var thing in things) + { + //Thing error + if (thing.HttpStatusCode != HttpStatusCode.OK) + { + this.logger.LogWarning($"Cannot import device '{thing.ThingName}' due to an error in the Amazon IoT API : {thing.HttpStatusCode}"); + continue; + } + + //ThingType not specified + if (thing.ThingTypeName.IsNullOrWhiteSpace()) + { + this.logger.LogInformation($"Cannot import Greengrass device '{thing.ThingName}' since it doesn't have related thing type."); + continue; + } + + //EdgeDeviceModel not find in DB + var edgeDeviceModel = await this.edgeDeviceModelRepository.GetByNameAsync(thing.ThingTypeName); + if (edgeDeviceModel == null) + { + this.logger.LogWarning($"Cannot import Greengrass device '{thing.ThingName}'. The EdgeDeviceModel '{thing.ThingTypeName}' doesn't exist"); + continue; + } + + //Create or update the Edge Device + await CreateOrUpdateGreenGrassDevice(thing, edgeDeviceModel); + } + + foreach (var item in (await this.edgeDeviceRepository.GetAllAsync( + edgeDevice => !things.Select(x => x.ThingId).Contains(edgeDevice.Id), + default, + d => d.Tags, + d => d.Labels + ))) + { + this.edgeDeviceRepository.Delete(item.Id); + } + + await this.unitOfWork.SaveAsync(); + } + + private async Task> GetAllGreenGrassDevices() + { + var devices = new List(); + + var nextToken = string.Empty; + + var response = await amazonGreenGrass.ListCoreDevicesAsync( + new ListCoreDevicesRequest + { + NextToken = nextToken + }); + + foreach (var requestDescribeThing in response.CoreDevices.Select(device => new DescribeThingRequest { ThingName = device.CoreDeviceThingName })) + { + try + { + devices.Add(await this.amazonIoTClient.DescribeThingAsync(requestDescribeThing)); + } + catch (AmazonIoTException e) + { + this.logger.LogWarning($"Cannot import Greengrass device '{requestDescribeThing.ThingName}' due to an error in the Amazon IoT API.", e); + continue; + } + } + + return devices; + } + + private async Task CreateOrUpdateGreenGrassDevice(DescribeThingResponse greengrassDevice, EdgeDeviceModel edgeModelDevice) + { + var edgeDevice = this.mapper.Map(greengrassDevice); + var edgeDeviceEntity = await this.edgeDeviceRepository.GetByIdAsync(edgeDevice.Id, d => d.Tags); + edgeDevice.DeviceModelId = edgeModelDevice.Id; + + if (edgeDeviceEntity == null) + { + await this.edgeDeviceRepository.InsertAsync(edgeDevice); + } + else + { + if (edgeDeviceEntity.Version >= edgeDevice.Version) return; + + foreach (var deviceTagEntity in edgeDeviceEntity.Tags) + { + this.deviceTagValueRepository.Delete(deviceTagEntity.Id); + } + + _ = this.mapper.Map(edgeDevice, edgeDeviceEntity); + this.edgeDeviceRepository.Update(edgeDeviceEntity); + } + } + } +} diff --git a/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncThingsJob.cs b/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncThingsJob.cs index eb0bc47a1..4ba7d346a 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncThingsJob.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncThingsJob.cs @@ -88,8 +88,8 @@ private async Task SyncThingsAsDevices() continue; } - //ThingType not find in DB - var deviceModel = this.deviceModelRepository.GetByName(thing.ThingTypeName); + //DeviceModel not find in DB + var deviceModel = await this.deviceModelRepository.GetByNameAsync(thing.ThingTypeName); if (deviceModel == null) { this.logger.LogWarning($"Cannot import device '{thing.ThingName}'. The ThingType '{thing.ThingTypeName}' doesn't exist"); diff --git a/src/AzureIoTHub.Portal.Infrastructure/Repositories/DeviceModelRepository.cs b/src/AzureIoTHub.Portal.Infrastructure/Repositories/DeviceModelRepository.cs index dfb1748a3..ba9422e91 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Repositories/DeviceModelRepository.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Repositories/DeviceModelRepository.cs @@ -5,6 +5,7 @@ namespace AzureIoTHub.Portal.Infrastructure.Repositories { using AzureIoTHub.Portal.Domain.Repositories; using Domain.Entities; + using Microsoft.EntityFrameworkCore; public class DeviceModelRepository : GenericRepository, IDeviceModelRepository { @@ -12,9 +13,10 @@ public DeviceModelRepository(PortalDbContext context) : base(context) { } - public DeviceModel? GetByName(string modelName) + public async Task GetByNameAsync(string modelName) { - return GetAll().FirstOrDefault(model => model.Name.Equals(modelName, StringComparison.Ordinal)); + return await this.context.Set() + .FirstOrDefaultAsync(deviceModel => deviceModel.Name.Equals(modelName, StringComparison.Ordinal)); } } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/Repositories/EdgeDeviceModelRepository.cs b/src/AzureIoTHub.Portal.Infrastructure/Repositories/EdgeDeviceModelRepository.cs index 940be2f00..95e6b834f 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Repositories/EdgeDeviceModelRepository.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Repositories/EdgeDeviceModelRepository.cs @@ -6,11 +6,18 @@ namespace AzureIoTHub.Portal.Infrastructure.Repositories { using Domain.Entities; using Domain.Repositories; + using Microsoft.EntityFrameworkCore; public class EdgeDeviceModelRepository : GenericRepository, IEdgeDeviceModelRepository { public EdgeDeviceModelRepository(PortalDbContext context) : base(context) { } + + public async Task GetByNameAsync(string edgeModelDevice) + { + return await this.context.Set() + .FirstOrDefaultAsync(edgeDeviceModel => edgeDeviceModel.Name.Equals(edgeModelDevice, StringComparison.Ordinal)); + } } } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJobTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJobTests.cs new file mode 100644 index 000000000..b9816d834 --- /dev/null +++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncGreenGrassDevicesJobTests.cs @@ -0,0 +1,495 @@ +// 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.Infrastructure.Jobs.AWS +{ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + using System.Net; + using System.Threading; + using System.Threading.Tasks; + using Amazon.GreengrassV2; + using Amazon.GreengrassV2.Model; + using Amazon.IoT; + using Amazon.IoT.Model; + using AutoFixture; + using AzureIoTHub.Portal.Domain; + using AzureIoTHub.Portal.Domain.Entities; + using AzureIoTHub.Portal.Domain.Repositories; + using AzureIoTHub.Portal.Infrastructure.Jobs.AWS; + using AzureIoTHub.Portal.Tests.Unit.UnitTests.Bases; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using NUnit.Framework; + using Quartz; + + public class SyncGreenGrassDevicesJobTests : BackendUnitTest + { + private IJob syncGreenGrassDevicesJob; + + private Mock amazonIoTClient; + private Mock amazonGreenGrass; + private Mock mockUnitOfWork; + private Mock mockDeviceRepository; + private Mock mockDeviceModelRepository; + private Mock mockDeviceTagValueRepository; + + public override void Setup() + { + base.Setup(); + + this.mockUnitOfWork = MockRepository.Create(); + this.mockDeviceRepository = MockRepository.Create(); + this.mockDeviceModelRepository = MockRepository.Create(); + this.mockDeviceTagValueRepository = MockRepository.Create(); + this.amazonIoTClient = MockRepository.Create(); + this.amazonGreenGrass = MockRepository.Create(); + + _ = ServiceCollection.AddSingleton(this.mockUnitOfWork.Object); + _ = ServiceCollection.AddSingleton(this.mockDeviceRepository.Object); + _ = ServiceCollection.AddSingleton(this.mockDeviceModelRepository.Object); + _ = ServiceCollection.AddSingleton(this.mockDeviceTagValueRepository.Object); + _ = ServiceCollection.AddSingleton(this.amazonIoTClient.Object); + _ = ServiceCollection.AddSingleton(this.amazonGreenGrass.Object); + _ = ServiceCollection.AddSingleton(); + + + Services = ServiceCollection.BuildServiceProvider(); + + this.syncGreenGrassDevicesJob = Services.GetRequiredService(); + } + + [Test] + public async Task ExecuteNewEdgeDeviceEdgeDeviceCreated() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var newDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 1 + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = newDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + ThingId = newDevice.Id, + ThingName = newDevice.Name, + ThingTypeName = newDevice.DeviceModel.Name, + Version = newDevice.Version, + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockDeviceModelRepository + .Setup(x => x.GetByNameAsync(newDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); + + _ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(newDevice.Id, d => d.Tags)) + .ReturnsAsync((EdgeDevice)null); + + _ = this.mockDeviceRepository.Setup(repository => repository.InsertAsync(It.IsAny())) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List + { + new EdgeDevice + { + Id = Guid.NewGuid().ToString() + } + }); + + this.mockDeviceRepository.Setup(x => x.Delete(It.IsAny())).Verifiable(); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task ExecuteExistingDeviceWithHigherVersionDeviceUpdated() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 1, + Tags = new List() + { + new() + { + Id = Fixture.Create(), + Name = Fixture.Create(), + Value = Fixture.Create() + } + } + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + ThingId = existingDevice.Id, + ThingName = existingDevice.Name, + ThingTypeName = existingDevice.DeviceModel.Name, + Version = 2, + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockDeviceModelRepository + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); + + _ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(existingDevice.Id, d => d.Tags)) + .ReturnsAsync(existingDevice); + + this.mockDeviceTagValueRepository.Setup(repository => repository.Delete(It.IsAny())).Verifiable(); + + this.mockDeviceRepository.Setup(repository => repository.Update(It.IsAny())).Verifiable(); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List + { + new EdgeDevice + { + Id = Guid.NewGuid().ToString() + } + }); + + this.mockDeviceRepository.Setup(x => x.Delete(It.IsAny())).Verifiable(); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task ExecuteExistingEdgeDeviceWithOlderVersionEdgeDeviceNotUpdated() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 2, + Tags = new List() + { + new() + { + Id = Fixture.Create(), + Name = Fixture.Create(), + Value = Fixture.Create() + } + } + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + ThingId = existingDevice.Id, + ThingName = existingDevice.Name, + ThingTypeName = existingDevice.DeviceModel.Name, + Version = 1, + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockDeviceModelRepository + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); + + _ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(existingDevice.Id, d => d.Tags)) + .ReturnsAsync(existingDevice); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List + { + new EdgeDevice + { + Id = Guid.NewGuid().ToString() + } + }); + + this.mockDeviceRepository.Setup(x => x.Delete(It.IsAny())).Verifiable(); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task ExecuteNewEdgeDeviceWithDescribeThingErrorSkipped() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 2 + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + HttpStatusCode = HttpStatusCode.RequestTimeout + }); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List()); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task ExecuteNewDeviceWithoutThingTypeSkipped() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 2 + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + ThingId = existingDevice.Id, + ThingName = existingDevice.Name, + Version = 1, + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List()); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task ExecuteNewDeviceWithUnknownThingTypeSkipped() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 2 + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DescribeThingResponse() + { + ThingId = existingDevice.Id, + ThingName = existingDevice.Name, + ThingTypeName = existingDevice.DeviceModel.Name, + Version = 1, + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockDeviceModelRepository + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync((EdgeDeviceModel)null); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List()); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + + public async Task ExecuteNewEdgeDeviceWithAmazonIotExceptionSkipped() + { + // Arrange + var mockJobExecutionContext = MockRepository.Create(); + + var expectedDeviceModel = Fixture.Create(); + var existingDevice = new EdgeDevice + { + Id = Fixture.Create(), + Name = Fixture.Create(), + DeviceModel = expectedDeviceModel, + DeviceModelId = expectedDeviceModel.Id, + Version = 2 + }; + + var greenGrassDevices = new ListCoreDevicesResponse + { + CoreDevices = new List() + { + new CoreDevice + { + CoreDeviceThingName = existingDevice.Name + } + } + }; + + _ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(greenGrassDevices); + + _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) + .Throws(new AmazonIoTException("")); + + _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) + .ReturnsAsync(new List()); + + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + // Act + await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object); + + // Assert + MockRepository.VerifyAll(); + } + } +} diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncThingsJobTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncThingsJobTests.cs index cc98c976e..f7ed62260 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncThingsJobTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Jobs/AWS/SyncThingsJobTests.cs @@ -14,7 +14,6 @@ namespace AzureIoTHub.Portal.Tests.Unit.Infrastructure.Jobs.AWS using Amazon.IotData; using Amazon.IotData.Model; using AutoFixture; - using AzureIoTHub.Portal.Application.Managers; using AzureIoTHub.Portal.Domain; using AzureIoTHub.Portal.Domain.Entities; using AzureIoTHub.Portal.Domain.Repositories; @@ -31,7 +30,6 @@ public class SyncThingsJobTests : BackendUnitTest private Mock amazonIoTClient; private Mock amazonIoTDataClient; - private Mock mockAWSImageManager; private Mock mockUnitOfWork; private Mock mockDeviceRepository; private Mock mockDeviceModelRepository; @@ -41,7 +39,6 @@ public override void Setup() { base.Setup(); - this.mockAWSImageManager = MockRepository.Create(); this.mockUnitOfWork = MockRepository.Create(); this.mockDeviceRepository = MockRepository.Create(); this.mockDeviceModelRepository = MockRepository.Create(); @@ -49,7 +46,6 @@ public override void Setup() this.amazonIoTClient = MockRepository.Create(); this.amazonIoTDataClient = MockRepository.Create(); - _ = ServiceCollection.AddSingleton(this.mockAWSImageManager.Object); _ = ServiceCollection.AddSingleton(this.mockUnitOfWork.Object); _ = ServiceCollection.AddSingleton(this.mockDeviceRepository.Object); _ = ServiceCollection.AddSingleton(this.mockDeviceModelRepository.Object); @@ -105,8 +101,8 @@ public async Task ExecuteNewDeviceDeviceCreated() }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(newDevice.DeviceModel.Name)) - .Returns(expectedDeviceModel); + .Setup(x => x.GetByNameAsync(newDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); _ = this.amazonIoTDataClient.Setup(client => client.GetThingShadowAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new GetThingShadowResponse() { HttpStatusCode = HttpStatusCode.OK }); @@ -188,8 +184,8 @@ public async Task ExecuteExistingDeviceWithHigherVersionDeviceUpdated() }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(existingDevice.DeviceModel.Name)) - .Returns(expectedDeviceModel); + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); _ = this.amazonIoTDataClient.Setup(client => client.GetThingShadowAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new GetThingShadowResponse() { HttpStatusCode = HttpStatusCode.OK }); @@ -255,8 +251,8 @@ public async Task ExecuteExistingDeviceWithOlderVersionDeviceNotUpdated() }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(existingDevice.DeviceModel.Name)) - .Returns(expectedDeviceModel); + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); _ = this.amazonIoTDataClient.Setup(client => client.GetThingShadowAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new GetThingShadowResponse() { HttpStatusCode = HttpStatusCode.OK }); @@ -310,10 +306,6 @@ public async Task ExecuteNewDeviceWithDescribeThingErrorSkipped() _ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new DescribeThingResponse() { - ThingId = existingDevice.Id, - ThingName = existingDevice.Name, - ThingTypeName = existingDevice.DeviceModel.Name, - Version = 1, HttpStatusCode = HttpStatusCode.RequestTimeout }); @@ -423,8 +415,8 @@ public async Task ExecuteNewDeviceWithUnknownThingTypeSkipped() }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(existingDevice.DeviceModel.Name)) - .Returns((DeviceModel)null); + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync((DeviceModel)null); _ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny>>(), It.IsAny(), d => d.Tags, d => d.Labels)) .ReturnsAsync(new List()); @@ -481,8 +473,8 @@ public async Task ExecuteNewDeviceWithoutThingShadowSkipped(HttpStatusCode thing }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(existingDevice.DeviceModel.Name)) - .Returns(expectedDeviceModel); + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); _ = this.amazonIoTDataClient.Setup(client => client.GetThingShadowAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new GetThingShadowResponse() { HttpStatusCode = thingShadowCode }); @@ -585,8 +577,8 @@ public async Task ExecuteNewDeviceWithAmazonIotDataExceptionSkipped() }); _ = this.mockDeviceModelRepository - .Setup(x => x.GetByName(existingDevice.DeviceModel.Name)) - .Returns(expectedDeviceModel); + .Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name)) + .ReturnsAsync(expectedDeviceModel); _ = this.amazonIoTDataClient.Setup(client => client.GetThingShadowAsync(It.IsAny(), It.IsAny())) .Throws(new AmazonIotDataException(""));