From 757925decd13936fd47065c09efb97e4e52da34d Mon Sep 17 00:00:00 2001 From: aestene Date: Tue, 17 Dec 2024 14:10:34 +0100 Subject: [PATCH] Establish new setup framework for testing Flotilla The previous framework uses collection based fixtures which have led to tests affecting eachother without meaning to. This change sets an easier framework for writing a new test class where all dependencies will not have to be made from scratch. Additionally, all tests will run with their own database to prevent leakages between tests. --- backend/api.test/Client/AreaTests.cs | 88 ++-- backend/api.test/Client/MissionTests.cs | 210 ++++----- backend/api.test/Client/RobotTests.cs | 72 ++- backend/api.test/Client/RoleAccessTests.cs | 95 ++-- backend/api.test/DbContextTestSetup.cs | 53 +-- .../EventHandlers/TestMissionEventHandler.cs | 423 ++++++------------ backend/api.test/Services/MissionService.cs | 101 +---- backend/api.test/Services/RobotService.cs | 143 ++---- backend/api.test/TestSetupHelpers.cs | 82 ++++ backend/api.test/TestWebApplicationFactory.cs | 23 +- .../CustomServiceConfigurations.cs | 10 +- backend/api/EventHandlers/MqttEventHandler.cs | 2 +- backend/api/Program.cs | 2 +- 13 files changed, 506 insertions(+), 798 deletions(-) create mode 100644 backend/api.test/TestSetupHelpers.cs diff --git a/backend/api.test/Client/AreaTests.cs b/backend/api.test/Client/AreaTests.cs index 448514df..ab50f5fb 100644 --- a/backend/api.test/Client/AreaTests.cs +++ b/backend/api.test/Client/AreaTests.cs @@ -2,51 +2,40 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; using Api.Controllers.Models; -using Api.Database.Context; using Api.Database.Models; using Api.Test.Database; -using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace Api.Test.Client { - [Collection("Database collection")] - public class AreaTests : IClassFixture> + public class AreaTests : IAsyncLifetime { - private readonly HttpClient _client; - private readonly DatabaseUtilities _databaseUtilities; - private readonly JsonSerializerOptions _serializerOptions = - new() - { - Converters = { new JsonStringEnumConverter() }, - PropertyNameCaseInsensitive = true, - }; + public required DatabaseUtilities DatabaseUtilities; + public required HttpClient Client; + public required JsonSerializerOptions SerializerOptions; - public AreaTests(TestWebApplicationFactory factory) + public async Task InitializeAsync() { - _client = factory.CreateClient( - new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - BaseAddress = new Uri("https://localhost:8000"), - } - ); - _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( - TestAuthHandler.AuthenticationScheme + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); - object? context = - factory.Services.GetService(typeof(FlotillaDbContext)) as FlotillaDbContext - ?? throw new ArgumentNullException(nameof(factory)); - _databaseUtilities = new DatabaseUtilities((FlotillaDbContext)context); + Client = TestSetupHelpers.ConfigureHttpClient(factory); + SerializerOptions = TestSetupHelpers.ConfigureJsonSerializerOptions(); + + DatabaseUtilities = new DatabaseUtilities( + TestSetupHelpers.ConfigureFlotillaDbContext(connectionString) + ); } + public Task DisposeAsync() => Task.CompletedTask; + [Fact] public async Task AreaTest() { @@ -127,19 +116,16 @@ public async Task AreaTest() // Act string installationUrl = "/installations"; - var installationResponse = await _client.PostAsync( - installationUrl, - installationContent - ); + var installationResponse = await Client.PostAsync(installationUrl, installationContent); string plantUrl = "/plants"; - var plantResponse = await _client.PostAsync(plantUrl, plantContent); + var plantResponse = await Client.PostAsync(plantUrl, plantContent); string inspectionAreaUrl = "/inspectionAreas"; - var inspectionAreaResponse = await _client.PostAsync( + var inspectionAreaResponse = await Client.PostAsync( inspectionAreaUrl, inspectionAreaContent ); string areaUrl = "/areas"; - var areaResponse = await _client.PostAsync(areaUrl, areaContent); + var areaResponse = await Client.PostAsync(areaUrl, areaContent); // Assert Assert.True(installationResponse.IsSuccessStatusCode); @@ -147,7 +133,7 @@ public async Task AreaTest() Assert.True(inspectionAreaResponse.IsSuccessStatusCode); Assert.True(areaResponse.IsSuccessStatusCode); var area = await areaResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(area); } @@ -156,15 +142,15 @@ public async Task AreaTest() public async Task MissionIsCreatedInInspectionArea() { // Arrange - Initialise area - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); string robotId = robot.Id; string testMissionName = "testMissionInInspectionAreaTest"; @@ -203,16 +189,16 @@ public async Task MissionIsCreatedInInspectionArea() // Act string missionUrl = "/missions/custom"; - var missionResponse = await _client.PostAsync(missionUrl, missionContent); + var missionResponse = await Client.PostAsync(missionUrl, missionContent); Assert.True(missionResponse.IsSuccessStatusCode); var mission = await missionResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(mission); Assert.NotNull(mission.MissionId); string inspectionAreaUrl = "/inspectionAreas"; - var inspectionareaMissionsResponse = await _client.GetAsync( + var inspectionareaMissionsResponse = await Client.GetAsync( inspectionAreaUrl + $"/{inspectionArea.Id}/mission-definitions" ); @@ -220,7 +206,7 @@ public async Task MissionIsCreatedInInspectionArea() Assert.True(inspectionareaMissionsResponse.IsSuccessStatusCode); var missions = await inspectionareaMissionsResponse.Content.ReadFromJsonAsync< IList - >(_serializerOptions); + >(SerializerOptions); Assert.NotNull(missions); Assert.Single( missions.Where(m => m.Id.Equals(mission.MissionId, StringComparison.Ordinal)) @@ -231,13 +217,13 @@ public async Task MissionIsCreatedInInspectionArea() public async Task EmergencyDockTest() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); string installationCode = installation.InstallationCode; // Act string goToDockingPositionUrl = $"/emergency-action/{installationCode}/abort-current-missions-and-send-all-robots-to-safe-zone"; - var missionResponse = await _client.PostAsync(goToDockingPositionUrl, null); + var missionResponse = await Client.PostAsync(goToDockingPositionUrl, null); // Assert Assert.True(missionResponse.IsSuccessStatusCode); @@ -251,9 +237,9 @@ public async Task EmergencyDockTest() public async Task UpdateDefaultLocalizationPoseOnInspectionArea() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); @@ -287,11 +273,11 @@ public async Task UpdateDefaultLocalizationPoseOnInspectionArea() ); // Act - var putResponse = await _client.PutAsync(url, content); + var putResponse = await Client.PutAsync(url, content); Assert.True(putResponse.IsSuccessStatusCode); var putInspectionArea = await putResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); // Assert diff --git a/backend/api.test/Client/MissionTests.cs b/backend/api.test/Client/MissionTests.cs index 5b710556..3ffef5c9 100644 --- a/backend/api.test/Client/MissionTests.cs +++ b/backend/api.test/Client/MissionTests.cs @@ -3,59 +3,48 @@ using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; using Api.Controllers.Models; -using Api.Database.Context; using Api.Database.Models; using Api.Test.Database; -using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace Api.Test.Client { - [Collection("Database collection")] - public class MissionTests : IClassFixture> + public class MissionTests : IAsyncLifetime { - private readonly HttpClient _client; - private readonly DatabaseUtilities _databaseUtilities; - private readonly JsonSerializerOptions _serializerOptions = - new() - { - Converters = { new JsonStringEnumConverter() }, - PropertyNameCaseInsensitive = true, - }; + public required DatabaseUtilities DatabaseUtilities; + public required HttpClient Client; + public required JsonSerializerOptions SerializerOptions; - public MissionTests(TestWebApplicationFactory factory) + public async Task InitializeAsync() { - _client = factory.CreateClient( - new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - BaseAddress = new Uri("https://localhost:8000"), - } - ); - _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( - TestAuthHandler.AuthenticationScheme + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + + Client = TestSetupHelpers.ConfigureHttpClient(factory); + SerializerOptions = TestSetupHelpers.ConfigureJsonSerializerOptions(); - object? context = - factory.Services.GetService(typeof(FlotillaDbContext)) as FlotillaDbContext - ?? throw new ArgumentNullException(nameof(factory)); - _databaseUtilities = new DatabaseUtilities((FlotillaDbContext)context); + DatabaseUtilities = new DatabaseUtilities( + TestSetupHelpers.ConfigureFlotillaDbContext(connectionString) + ); } + public Task DisposeAsync() => Task.CompletedTask; + [Fact] public async Task ScheduleOneMissionTest() { // Arrange - Area - var installation = await _databaseUtilities.ReadOrNewInstallation(); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Busy, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Busy, installation); string robotId = robot.Id; string missionsUrl = "/missions"; @@ -75,12 +64,12 @@ public async Task ScheduleOneMissionTest() "application/json" ); - var response = await _client.PostAsync(missionsUrl, content); + var response = await Client.PostAsync(missionsUrl, content); // Assert Assert.True(response.IsSuccessStatusCode); var missionRun = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(missionRun); Assert.NotNull(missionRun.Id); @@ -91,10 +80,10 @@ public async Task ScheduleOneMissionTest() public async Task Schedule3MissionsTest() { // Arrange - Area - var installation = await _databaseUtilities.ReadOrNewInstallation(); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Busy, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Busy, installation); string robotId = robot.Id; string missionSourceId = "97"; @@ -115,44 +104,44 @@ public async Task Schedule3MissionsTest() // Increasing pageSize to 50 to ensure the missions we are looking for is included string urlMissionRuns = "/missions/runs?pageSize=50"; - var response = await _client.GetAsync(urlMissionRuns); + var response = await Client.GetAsync(urlMissionRuns); var missionRuns = await response.Content.ReadFromJsonAsync>( - _serializerOptions + SerializerOptions ); Assert.True(response.IsSuccessStatusCode); Assert.NotNull(missionRuns); int missionRunsBefore = missionRuns.Count; string missionsUrl = "/missions"; - response = await _client.PostAsync(missionsUrl, content); + response = await Client.PostAsync(missionsUrl, content); // Assert Assert.True(response.IsSuccessStatusCode); - response = await _client.PostAsync(missionsUrl, content); + response = await Client.PostAsync(missionsUrl, content); var missionRun1 = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.True(response.IsSuccessStatusCode); Assert.NotNull(missionRun1); - response = await _client.PostAsync(missionsUrl, content); + response = await Client.PostAsync(missionsUrl, content); var missionRun2 = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.True(response.IsSuccessStatusCode); Assert.NotNull(missionRun2); - response = await _client.PostAsync(missionsUrl, content); + response = await Client.PostAsync(missionsUrl, content); var missionRun3 = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.True(response.IsSuccessStatusCode); Assert.NotNull(missionRun3); - response = await _client.GetAsync(urlMissionRuns); + response = await Client.GetAsync(urlMissionRuns); missionRuns = await response.Content.ReadFromJsonAsync>( - _serializerOptions + SerializerOptions ); // Assert @@ -167,13 +156,13 @@ public async Task Schedule3MissionsTest() public async Task AddNonDuplicateAreasToDb() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var _ = await _databaseUtilities.ReadOrNewArea( + var _ = await DatabaseUtilities.ReadOrNewArea( installation.InstallationCode, plant.PlantCode, inspectionArea.Name @@ -209,7 +198,7 @@ public async Task AddNonDuplicateAreasToDb() "application/json" ); string areaUrl = "/areas"; - var response = await _client.PostAsync(areaUrl, areaContent); + var response = await Client.PostAsync(areaUrl, areaContent); Assert.True( response.IsSuccessStatusCode, $"Failed to post to {areaUrl}. Status code: {response.StatusCode}" @@ -217,7 +206,7 @@ public async Task AddNonDuplicateAreasToDb() Assert.True(response != null, $"Failed to post to {areaUrl}. Null returned"); var responseObject = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.True(responseObject != null, $"No object returned from post to {areaUrl}"); } @@ -226,13 +215,13 @@ public async Task AddNonDuplicateAreasToDb() public async Task AddDuplicateAreasToDb_Fails() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var area = await _databaseUtilities.ReadOrNewArea( + var area = await DatabaseUtilities.ReadOrNewArea( installation.InstallationCode, plant.PlantCode, inspectionArea.Name @@ -268,7 +257,7 @@ public async Task AddDuplicateAreasToDb_Fails() "application/json" ); string areaUrl = "/areas"; - var response = await _client.PostAsync(areaUrl, areaContent); + var response = await Client.PostAsync(areaUrl, areaContent); Assert.Equal(HttpStatusCode.Conflict, response.StatusCode); } @@ -277,7 +266,7 @@ public async Task GetMissionById_ShouldReturnNotFound() { string missionId = "RandomString"; string url = "/missions/runs/" + missionId; - var response = await _client.GetAsync(url); + var response = await Client.GetAsync(url); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } @@ -286,7 +275,7 @@ public async Task DeleteMission_ShouldReturnNotFound() { string missionId = "RandomString"; string url = "/missions/runs/" + missionId; - var response = await _client.DeleteAsync(url); + var response = await Client.DeleteAsync(url); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } @@ -294,9 +283,9 @@ public async Task DeleteMission_ShouldReturnNotFound() public async Task ScheduleDuplicateCustomMissionDefinitions() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); @@ -304,7 +293,7 @@ public async Task ScheduleDuplicateCustomMissionDefinitions() string testMissionName = "testMissionScheduleDuplicateCustomMissionDefinitions"; // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Busy, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Busy, installation); string robotId = robot.Id; // Arrange - Create custom mission definition @@ -348,17 +337,17 @@ public async Task ScheduleDuplicateCustomMissionDefinitions() // Act string customMissionsUrl = "/missions/custom"; - var response1 = await _client.PostAsync(customMissionsUrl, content); - var response2 = await _client.PostAsync(customMissionsUrl, content); + var response1 = await Client.PostAsync(customMissionsUrl, content); + var response2 = await Client.PostAsync(customMissionsUrl, content); // Assert Assert.True(response1.IsSuccessStatusCode); Assert.True(response2.IsSuccessStatusCode); var missionRun1 = await response1.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); var missionRun2 = await response2.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(missionRun1); Assert.NotNull(missionRun2); @@ -367,10 +356,10 @@ public async Task ScheduleDuplicateCustomMissionDefinitions() Assert.Equal(missionId1, missionId2); // Increasing pageSize to 50 to ensure the missions we are looking for is included string missionDefinitionsUrl = "/missions/definitions?pageSize=50"; - var missionDefinitionsResponse = await _client.GetAsync(missionDefinitionsUrl); + var missionDefinitionsResponse = await Client.GetAsync(missionDefinitionsUrl); var missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync< List - >(_serializerOptions); + >(SerializerOptions); Assert.NotNull(missionDefinitions); Assert.Single(missionDefinitions.Where(m => m.Id == missionId1)); } @@ -379,15 +368,15 @@ public async Task ScheduleDuplicateCustomMissionDefinitions() public async Task GetNextRun() { // Arrange - Initialise area - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); string robotId = robot.Id; // Arrange - Schedule custom mission - create mission definition @@ -421,10 +410,10 @@ public async Task GetNextRun() ); string customMissionsUrl = "/missions/custom"; - var response = await _client.PostAsync(customMissionsUrl, content); + var response = await Client.PostAsync(customMissionsUrl, content); Assert.True(response.IsSuccessStatusCode); var missionRun = await response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(missionRun); Assert.NotNull(missionRun.MissionId); @@ -463,36 +452,27 @@ public async Task GetNextRun() "application/json" ); string scheduleMissionsUrl = $"/missions/schedule/{missionRun.MissionId}"; - var missionRun1Response = await _client.PostAsync( - scheduleMissionsUrl, - scheduleContent1 - ); - var missionRun2Response = await _client.PostAsync( - scheduleMissionsUrl, - scheduleContent2 - ); - var missionRun3Response = await _client.PostAsync( - scheduleMissionsUrl, - scheduleContent3 - ); + var missionRun1Response = await Client.PostAsync(scheduleMissionsUrl, scheduleContent1); + var missionRun2Response = await Client.PostAsync(scheduleMissionsUrl, scheduleContent2); + var missionRun3Response = await Client.PostAsync(scheduleMissionsUrl, scheduleContent3); var missionRun1 = await missionRun1Response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); var missionRun2 = await missionRun2Response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); var missionRun3 = await missionRun3Response.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); // Act string nextMissionUrl = $"missions/definitions/{missionRun.MissionId}/next-run"; - var nextMissionResponse = await _client.GetAsync(nextMissionUrl); + var nextMissionResponse = await Client.GetAsync(nextMissionUrl); // Assert Assert.True(nextMissionResponse.IsSuccessStatusCode); var nextMissionRun = await nextMissionResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(nextMissionRun); Assert.NotNull(missionRun1); @@ -508,24 +488,24 @@ public async Task GetNextRun() public async Task ScheduleDuplicatMissionDefinitions() { // Arrange - Initialise area - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var area = await _databaseUtilities.ReadOrNewArea( + var area = await DatabaseUtilities.ReadOrNewArea( installation.InstallationCode, plant.PlantCode, inspectionArea.Name ); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); string robotId = robot.Id; string missionSourceId = "986"; - var source = await _databaseUtilities.NewSource(missionSourceId); + var source = await DatabaseUtilities.NewSource(missionSourceId); var query = new ScheduledMissionQuery { @@ -542,17 +522,17 @@ public async Task ScheduleDuplicatMissionDefinitions() // Act string missionsUrl = "/missions"; - var response1 = await _client.PostAsync(missionsUrl, content); - var response2 = await _client.PostAsync(missionsUrl, content); + var response1 = await Client.PostAsync(missionsUrl, content); + var response2 = await Client.PostAsync(missionsUrl, content); // Assert Assert.True(response1.IsSuccessStatusCode); Assert.True(response2.IsSuccessStatusCode); var missionRun1 = await response1.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); var missionRun2 = await response2.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(missionRun1); Assert.NotNull(missionRun2); @@ -560,10 +540,10 @@ public async Task ScheduleDuplicatMissionDefinitions() string? missionId2 = missionRun2.MissionId; Assert.Equal(missionId1, missionId2); string missionDefinitionsUrl = "/missions/definitions?pageSize=50"; - var missionDefinitionsResponse = await _client.GetAsync(missionDefinitionsUrl); + var missionDefinitionsResponse = await Client.GetAsync(missionDefinitionsUrl); var missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync< List - >(_serializerOptions); + >(SerializerOptions); Assert.NotNull(missionDefinitions); Assert.NotNull(missionDefinitions.Find(m => m.Id == missionId1)); } @@ -572,9 +552,9 @@ public async Task ScheduleDuplicatMissionDefinitions() public async Task MissionDoesNotStartIfRobotIsNotInSameInstallationAsMission() { // Arrange - Initialise area - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); @@ -585,10 +565,10 @@ public async Task MissionDoesNotStartIfRobotIsNotInSameInstallationAsMission() // Arrange - Get different installation string otherInstallationCode = "installationMissionDoesNotStartIfRobotIsNotInSameInstallationAsMission_Other"; - var otherInstallation = await _databaseUtilities.NewInstallation(otherInstallationCode); + var otherInstallation = await DatabaseUtilities.NewInstallation(otherInstallationCode); // Arrange - Robot - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, otherInstallation); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, otherInstallation); string robotId = robot.Id; // Arrange - Create custom mission definition @@ -632,7 +612,7 @@ public async Task MissionDoesNotStartIfRobotIsNotInSameInstallationAsMission() // Act string customMissionsUrl = "/missions/custom"; - var response = await _client.PostAsync(customMissionsUrl, content); + var response = await Client.PostAsync(customMissionsUrl, content); Assert.Equal(HttpStatusCode.Conflict, response.StatusCode); } @@ -640,12 +620,12 @@ public async Task MissionDoesNotStartIfRobotIsNotInSameInstallationAsMission() public async Task MissionFailsIfRobotIsNotInSameInspectionAreaAsMission() { // Arrange - Initialise area - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); string inspectionAreaName1 = "inspectionAreaMissionFailsIfRobotIsNotInSameInspectionAreaAsMission1"; - var inspectionArea1 = await _databaseUtilities.NewInspectionArea( + var inspectionArea1 = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode, inspectionAreaName1 @@ -653,7 +633,7 @@ public async Task MissionFailsIfRobotIsNotInSameInspectionAreaAsMission() string inspectionAreaName2 = "inspectionAreaMissionFailsIfRobotIsNotInSameInspectionAreaAsMission2"; - var inspectionArea2 = await _databaseUtilities.NewInspectionArea( + var inspectionArea2 = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode, inspectionAreaName2 @@ -662,7 +642,7 @@ public async Task MissionFailsIfRobotIsNotInSameInspectionAreaAsMission() string testMissionName = "testMissionFailsIfRobotIsNotInSameInspectionAreaAsMission"; // Arrange - Robot - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea1 @@ -710,7 +690,7 @@ public async Task MissionFailsIfRobotIsNotInSameInspectionAreaAsMission() // Act string customMissionsUrl = "/missions/custom"; - var missionResponse = await _client.PostAsync(customMissionsUrl, content); + var missionResponse = await Client.PostAsync(customMissionsUrl, content); Assert.Equal(HttpStatusCode.Conflict, missionResponse.StatusCode); } } diff --git a/backend/api.test/Client/RobotTests.cs b/backend/api.test/Client/RobotTests.cs index 3945cf91..1a002935 100644 --- a/backend/api.test/Client/RobotTests.cs +++ b/backend/api.test/Client/RobotTests.cs @@ -2,58 +2,48 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; using Api.Controllers.Models; -using Api.Database.Context; using Api.Database.Models; using Api.Test.Database; -using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Xunit; namespace Api.Test.Client { - [Collection("Database collection")] - public class RobotTests : IClassFixture> + public class RobotTests : IAsyncLifetime { - private readonly HttpClient _client; - private readonly DatabaseUtilities _databaseUtilities; - private readonly JsonSerializerOptions _serializerOptions = - new() - { - Converters = { new JsonStringEnumConverter() }, - PropertyNameCaseInsensitive = true, - }; + public required DatabaseUtilities DatabaseUtilities; + public required HttpClient Client; + public required JsonSerializerOptions SerializerOptions; - public RobotTests(TestWebApplicationFactory factory) + public async Task InitializeAsync() { - _client = factory.CreateClient( - new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - BaseAddress = new Uri("https://localhost:8000"), - } + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); - _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( - TestAuthHandler.AuthenticationScheme + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + + Client = TestSetupHelpers.ConfigureHttpClient(factory); + SerializerOptions = TestSetupHelpers.ConfigureJsonSerializerOptions(); + + DatabaseUtilities = new DatabaseUtilities( + TestSetupHelpers.ConfigureFlotillaDbContext(connectionString) ); - object? context = - factory.Services.GetService(typeof(FlotillaDbContext)) as FlotillaDbContext - ?? throw new ArgumentNullException(nameof(factory)); - _databaseUtilities = new DatabaseUtilities((FlotillaDbContext)context); } + public Task DisposeAsync() => Task.CompletedTask; + [Fact] public async Task RobotsTest() { string url = "/robots"; - var response = await _client.GetAsync(url); + var response = await Client.GetAsync(url); var robots = await response.Content.ReadFromJsonAsync>( - _serializerOptions + SerializerOptions ); Assert.True(response.IsSuccessStatusCode); Assert.NotNull(robots); @@ -64,28 +54,28 @@ public async Task GetRobotById_ShouldReturnNotFound() { string robotId = "RandomString"; string url = "/robots/" + robotId; - var response = await _client.GetAsync(url); + var response = await Client.GetAsync(url); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetRobotById_ShouldReturnRobot() { - var installation = await _databaseUtilities.ReadOrNewInstallation(); - _ = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + _ = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); string url = "/robots"; - var response = await _client.GetAsync(url); + var response = await Client.GetAsync(url); var robots = await response.Content.ReadFromJsonAsync>( - _serializerOptions + SerializerOptions ); Assert.NotNull(robots); string robotId = robots[0].Id; - var robotResponse = await _client.GetAsync("/robots/" + robotId); + var robotResponse = await Client.GetAsync("/robots/" + robotId); var robot = await robotResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.Equal(HttpStatusCode.OK, robotResponse.StatusCode); Assert.NotNull(robot); @@ -100,13 +90,13 @@ public async Task GetRobotById_ShouldReturnRobot() public async Task RobotIsNotCreatedWithAreaNotInInstallation() { // Arrange - Area - var installation = await _databaseUtilities.ReadOrNewInstallation(); + var installation = await DatabaseUtilities.ReadOrNewInstallation(); - var wrongInstallation = await _databaseUtilities.NewInstallation("wrongInstallation"); - var wrongPlant = await _databaseUtilities.ReadOrNewPlant( + var wrongInstallation = await DatabaseUtilities.NewInstallation("wrongInstallation"); + var wrongPlant = await DatabaseUtilities.ReadOrNewPlant( wrongInstallation.InstallationCode ); - var wrongInspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var wrongInspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( wrongInstallation.InstallationCode, wrongPlant.PlantCode ); @@ -134,7 +124,7 @@ public async Task RobotIsNotCreatedWithAreaNotInInstallation() try { - var response = await _client.PostAsync(robotUrl, content); + var response = await Client.PostAsync(robotUrl, content); } catch (DbUpdateException ex) { diff --git a/backend/api.test/Client/RoleAccessTests.cs b/backend/api.test/Client/RoleAccessTests.cs index 5f46d489..44fbbd1c 100644 --- a/backend/api.test/Client/RoleAccessTests.cs +++ b/backend/api.test/Client/RoleAccessTests.cs @@ -1,50 +1,40 @@ using System; using System.Net.Http; -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; using Api.Controllers.Models; using Api.Database.Models; using Api.Test.Mocks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Api.Test +namespace Api.Test.Client { - [Collection("Database collection")] - public class RoleAccessTests : IClassFixture> + public class RoleAccessTests : IAsyncLifetime { - private readonly HttpClient _client; - private readonly MockHttpContextAccessor _httpContextAccessor; - private readonly JsonSerializerOptions _serializerOptions = - new() - { - Converters = { new JsonStringEnumConverter() }, - PropertyNameCaseInsensitive = true, - }; + public required HttpClient Client; + public required JsonSerializerOptions SerializerOptions; + public required MockHttpContextAccessor HttpContextAccessor; - public RoleAccessTests(TestWebApplicationFactory factory) + public async Task InitializeAsync() { - _httpContextAccessor = (MockHttpContextAccessor) - factory.Services.GetService()!; - _httpContextAccessor.SetHttpContextRoles(["Role.Admin"]); - //var x = new HttpContextAccessor(); - _client = factory.CreateClient( - new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - BaseAddress = new Uri("https://localhost:8000"), - } - ); - _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( - TestAuthHandler.AuthenticationScheme + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + var serviceProvider = TestSetupHelpers.ConfigureServiceProvider(factory); + + Client = TestSetupHelpers.ConfigureHttpClient(factory); + SerializerOptions = TestSetupHelpers.ConfigureJsonSerializerOptions(); + HttpContextAccessor = (MockHttpContextAccessor) + serviceProvider.GetService()!; } + public Task DisposeAsync() => Task.CompletedTask; + [Fact] public async Task AuthorisedGetPlantTest_NotFound() { @@ -90,29 +80,26 @@ public async Task AuthorisedGetPlantTest_NotFound() // Act string installationUrl = "/installations"; - var installationResponse = await _client.PostAsync( - installationUrl, - installationContent - ); + var installationResponse = await Client.PostAsync(installationUrl, installationContent); string accessRoleUrl = "/access-roles"; - var accessRoleResponse = await _client.PostAsync(accessRoleUrl, accessRoleContent); + var accessRoleResponse = await Client.PostAsync(accessRoleUrl, accessRoleContent); string plantUrl = "/plants"; - var plantResponse = await _client.PostAsync(plantUrl, plantContent); + var plantResponse = await Client.PostAsync(plantUrl, plantContent); // Only restrict ourselves to non-admin role after doing POSTs - _httpContextAccessor.SetHttpContextRoles(["User.TestInstallationAreaTest_Wrong"]); + HttpContextAccessor.SetHttpContextRoles(["User.TestInstallationAreaTest_Wrong"]); // Assert Assert.True(accessRoleResponse.IsSuccessStatusCode); Assert.True(installationResponse.IsSuccessStatusCode); Assert.True(plantResponse.IsSuccessStatusCode); - var plant = await plantResponse.Content.ReadFromJsonAsync(_serializerOptions); + var plant = await plantResponse.Content.ReadFromJsonAsync(SerializerOptions); Assert.NotNull(plant); // Act string getPlantUrl = $"/plants/{plant.Id}"; - var samePlantResponse = await _client.GetAsync(getPlantUrl); + var samePlantResponse = await Client.GetAsync(getPlantUrl); // Assert Assert.False(samePlantResponse.IsSuccessStatusCode); @@ -217,24 +204,21 @@ public async Task ExplicitlyAuthorisedPostInstallationPlantInspectionAreaAndArea // Act string installationUrl = "/installations"; - var installationResponse = await _client.PostAsync( - installationUrl, - installationContent - ); + var installationResponse = await Client.PostAsync(installationUrl, installationContent); string accessRoleUrl = "/access-roles"; - var accessRoleResponse = await _client.PostAsync(accessRoleUrl, accessRoleContent); + var accessRoleResponse = await Client.PostAsync(accessRoleUrl, accessRoleContent); string plantUrl = "/plants"; - var plantResponse = await _client.PostAsync(plantUrl, plantContent); + var plantResponse = await Client.PostAsync(plantUrl, plantContent); string inspectionAreaUrl = "/inspectionAreas"; - var inspectionAreaResponse = await _client.PostAsync( + var inspectionAreaResponse = await Client.PostAsync( inspectionAreaUrl, inspectionAreaContent ); string areaUrl = "/areas"; - var areaResponse = await _client.PostAsync(areaUrl, areaContent); + var areaResponse = await Client.PostAsync(areaUrl, areaContent); // Only restrict ourselves to non-admin role after doing POSTs - _httpContextAccessor.SetHttpContextRoles( + HttpContextAccessor.SetHttpContextRoles( [ "User.ExplicitlyAuthorisedPostInstallationPlantInspectionAreaAndAreaTestInstallation", ] @@ -247,18 +231,18 @@ public async Task ExplicitlyAuthorisedPostInstallationPlantInspectionAreaAndArea Assert.True(inspectionAreaResponse.IsSuccessStatusCode); Assert.True(areaResponse.IsSuccessStatusCode); var area = await areaResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(area); // Act string getAreaUrl = $"/areas/{area.Id}"; - var sameAreaResponse = await _client.GetAsync(getAreaUrl); + var sameAreaResponse = await Client.GetAsync(getAreaUrl); // Assert Assert.True(sameAreaResponse.IsSuccessStatusCode); var sameArea = await sameAreaResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(sameArea); Assert.Equal(sameArea.Id, area.Id); @@ -346,19 +330,16 @@ public async Task AdminAuthorisedPostInstallationPlantInspectionAreaAndAreaTest( // Act string installationUrl = "/installations"; - var installationResponse = await _client.PostAsync( - installationUrl, - installationContent - ); + var installationResponse = await Client.PostAsync(installationUrl, installationContent); string plantUrl = "/plants"; - var plantResponse = await _client.PostAsync(plantUrl, plantContent); + var plantResponse = await Client.PostAsync(plantUrl, plantContent); string inspectionAreaUrl = "/inspectionAreas"; - var inspectionAreaResponse = await _client.PostAsync( + var inspectionAreaResponse = await Client.PostAsync( inspectionAreaUrl, inspectionAreaContent ); string areaUrl = "/areas"; - var areaResponse = await _client.PostAsync(areaUrl, areaContent); + var areaResponse = await Client.PostAsync(areaUrl, areaContent); // Assert Assert.True(installationResponse.IsSuccessStatusCode); @@ -366,7 +347,7 @@ public async Task AdminAuthorisedPostInstallationPlantInspectionAreaAndAreaTest( Assert.True(inspectionAreaResponse.IsSuccessStatusCode); Assert.True(areaResponse.IsSuccessStatusCode); var area = await areaResponse.Content.ReadFromJsonAsync( - _serializerOptions + SerializerOptions ); Assert.NotNull(area); } diff --git a/backend/api.test/DbContextTestSetup.cs b/backend/api.test/DbContextTestSetup.cs index 4dcf5a97..031b64e5 100644 --- a/backend/api.test/DbContextTestSetup.cs +++ b/backend/api.test/DbContextTestSetup.cs @@ -1,65 +1,18 @@ using System; +using System.Data.Common; +using System.Net.Http; using System.Security.Claims; using System.Text.Encodings.Web; +using System.Text.Json; using System.Threading.Tasks; using Api.Database.Context; using Microsoft.AspNetCore.Authentication; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Xunit; namespace Api.Test { - // Class for building and disposing dbcontext - public class DatabaseFixture : IDisposable - { - public FlotillaDbContext NewContext => CreateContext(); - private SqliteConnection? _connection; - - private DbContextOptions CreateOptions() - { - string connectionString = new SqliteConnectionStringBuilder - { - DataSource = ":memory:", - Cache = SqliteCacheMode.Shared, - }.ToString(); - _connection = new SqliteConnection(connectionString); - _connection.Open(); - var builder = new DbContextOptionsBuilder(); - builder.EnableSensitiveDataLogging(); - builder.UseSqlite(_connection); - return builder.Options; - } - - public FlotillaDbContext CreateContext() - { - var options = CreateOptions(); - var context = new FlotillaDbContext(options); - context.Database.EnsureCreated(); - return context; - } - - public void Dispose() - { - if (_connection != null) - { - _connection.Dispose(); - _connection = null; - } - GC.SuppressFinalize(this); - } - } - - [CollectionDefinition("Database collection")] - public class DatabaseCollection : ICollectionFixture - { - // This class has no code, and is never created. Its purpose is simply - // to be the place to apply [CollectionDefinition] and all the - // ICollectionFixture<> interfaces. - } - // Class for mocking authentication options public class TestAuthHandlerOptions : AuthenticationSchemeOptions { diff --git a/backend/api.test/EventHandlers/TestMissionEventHandler.cs b/backend/api.test/EventHandlers/TestMissionEventHandler.cs index 0a7365ec..20f92982 100644 --- a/backend/api.test/EventHandlers/TestMissionEventHandler.cs +++ b/backend/api.test/EventHandlers/TestMissionEventHandler.cs @@ -1,251 +1,104 @@ using System; +using System.Data.Common; using System.Linq; +using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Api.Controllers.Models; using Api.Database.Context; using Api.Database.Models; -using Api.EventHandlers; using Api.Mqtt; using Api.Mqtt.Events; using Api.Mqtt.MessageModels; -using Api.Options; using Api.Services; -using Api.Services.ActionServices; using Api.Services.Events; using Api.Test.Database; -using Api.Test.Mocks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Moq; using Xunit; namespace Api.Test.EventHandlers { - [Collection("Database collection")] - public class TestMissionEventHandler : IDisposable + public class TestMissionEventHandler : IAsyncLifetime, IAsyncDisposable { - private readonly MissionEventHandler _missionEventHandler; - private readonly MissionRunService _missionRunService; - private readonly MqttEventHandler _mqttEventHandler; - private readonly MqttService _mqttService; - private readonly DatabaseUtilities _databaseUtilities; - private readonly RobotService _robotService; - private readonly LocalizationService _localizationService; - private readonly EmergencyActionService _emergencyActionService; - private readonly MissionSchedulingService _missionSchedulingService; - - public TestMissionEventHandler(DatabaseFixture fixture) + private FlotillaDbContext Context => CreateContext(); + public required TestWebApplicationFactory Factory; + public required IServiceProvider ServiceProvider; + + public required string ConnectionString; + + public required DatabaseUtilities DatabaseUtilities; + + public required IMissionRunService MissionRunService; + public required IRobotService RobotService; + public required IMissionSchedulingService MissionSchedulingService; + + public required MqttService MqttService; + + public async Task InitializeAsync() { - var missionEventHandlerLogger = new Mock>().Object; + string databaseName = Guid.NewGuid().ToString(); + (ConnectionString, _) = await TestSetupHelpers.ConfigureDatabase(databaseName); + + DatabaseUtilities = new DatabaseUtilities(Context); + + Factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + ServiceProvider = TestSetupHelpers.ConfigureServiceProvider(Factory); + + MissionRunService = ServiceProvider.GetRequiredService(); + RobotService = ServiceProvider.GetRequiredService(); + MissionSchedulingService = + ServiceProvider.GetRequiredService(); + var mqttServiceLogger = new Mock>().Object; - var mqttEventHandlerLogger = new Mock>().Object; - var missionLogger = new Mock>().Object; - var missionSchedulingServiceLogger = new Mock< - ILogger - >().Object; - var robotServiceLogger = new Mock>().Object; - var localizationServiceLogger = new Mock>().Object; - var mapServiceLogger = new Mock>().Object; - var mapBlobOptions = new Mock>().Object; - var returnToHomeServiceLogger = new Mock>().Object; - var missionDefinitionServiceLogger = new Mock< - ILogger - >().Object; - var lastMissionRunServiceLogger = new Mock>().Object; - var sourceServiceLogger = new Mock>().Object; - var errorHandlingServiceLogger = new Mock>().Object; - var missionTaskServiceLogger = new Mock>().Object; - - var configuration = WebApplication.CreateBuilder().Configuration; - - var context = fixture.NewContext; - - var signalRService = new MockSignalRService(); - var accessRoleService = new AccessRoleService(context, new HttpContextAccessor()); - var userInfoService = new UserInfoService( - context, - new HttpContextAccessor(), - new Mock>().Object - ); - - _mqttService = new MqttService(mqttServiceLogger, configuration); - - var missionTaskService = new MissionTaskService(context, missionTaskServiceLogger); - - var missionLoader = new MockMissionLoader(); - var stidServiceMock = new MockStidService(context); - var sourceService = new SourceService(context, sourceServiceLogger); - var robotModelService = new RobotModelService(context); - var taskDurationServiceMock = new MockTaskDurationService(); - var isarServiceMock = new MockIsarService(); - var installationService = new InstallationService(context, accessRoleService); - var defaultLocalizationPoseService = new DefaultLocalizationPoseService(context); - var plantService = new PlantService(context, installationService, accessRoleService); - var inspectionAreaService = new InspectionAreaService( - context, - defaultLocalizationPoseService, - installationService, - plantService, - accessRoleService, - signalRService - ); - var areaService = new AreaService( - context, - installationService, - plantService, - inspectionAreaService, - defaultLocalizationPoseService, - accessRoleService - ); - var mapServiceMock = new MockMapService(); - _robotService = new RobotService( - context, - robotServiceLogger, - robotModelService, - signalRService, - accessRoleService, - installationService, - inspectionAreaService - ); - _missionRunService = new MissionRunService( - context, - signalRService, - missionLogger, - accessRoleService, - missionTaskService, - inspectionAreaService, - _robotService, - userInfoService - ); - var missionDefinitionService = new MissionDefinitionService( - context, - missionLoader, - signalRService, - accessRoleService, - missionDefinitionServiceLogger, - _missionRunService, - sourceService - ); - _localizationService = new LocalizationService( - localizationServiceLogger, - _robotService, - installationService, - inspectionAreaService - ); - var errorHandlingService = new ErrorHandlingService( - errorHandlingServiceLogger, - _robotService, - _missionRunService - ); - var returnToHomeService = new ReturnToHomeService( - returnToHomeServiceLogger, - _robotService, - _missionRunService - ); - _missionSchedulingService = new MissionSchedulingService( - missionSchedulingServiceLogger, - _missionRunService, - _robotService, - isarServiceMock, - _localizationService, - returnToHomeService, - signalRService, - errorHandlingService - ); - var lastMissionRunService = new LastMissionRunService(missionDefinitionService); - - _databaseUtilities = new DatabaseUtilities(context); - _emergencyActionService = new EmergencyActionService(); - - var mockServiceProvider = new Mock(); - - // Mock services and controllers that are passed through the mocked service injector - mockServiceProvider - .Setup(p => p.GetService(typeof(IMissionRunService))) - .Returns(_missionRunService); - mockServiceProvider - .Setup(p => p.GetService(typeof(IRobotService))) - .Returns(_robotService); - mockServiceProvider - .Setup(p => p.GetService(typeof(IMissionSchedulingService))) - .Returns(_missionSchedulingService); - mockServiceProvider - .Setup(p => p.GetService(typeof(FlotillaDbContext))) - .Returns(context); - mockServiceProvider - .Setup(p => p.GetService(typeof(ILocalizationService))) - .Returns(_localizationService); - mockServiceProvider - .Setup(p => p.GetService(typeof(IReturnToHomeService))) - .Returns(returnToHomeService); - mockServiceProvider - .Setup(p => p.GetService(typeof(IMapService))) - .Returns(mapServiceMock); - mockServiceProvider - .Setup(p => p.GetService(typeof(ITaskDurationService))) - .Returns(taskDurationServiceMock); - mockServiceProvider - .Setup(p => p.GetService(typeof(ILastMissionRunService))) - .Returns(lastMissionRunService); - mockServiceProvider.Setup(p => p.GetService(typeof(IAreaService))).Returns(areaService); - mockServiceProvider - .Setup(p => p.GetService(typeof(ISignalRService))) - .Returns(signalRService); - mockServiceProvider - .Setup(p => p.GetService(typeof(IEmergencyActionService))) - .Returns(_emergencyActionService); - - // Mock service injector - var mockScope = new Mock(); - mockScope.Setup(scope => scope.ServiceProvider).Returns(mockServiceProvider.Object); - var mockFactory = new Mock(); - mockFactory.Setup(f => f.CreateScope()).Returns(mockScope.Object); - - // Instantiating the event handlers are required for the event subscribers to be activated - _missionEventHandler = new MissionEventHandler( - missionEventHandlerLogger, - mockFactory.Object - ); - _mqttEventHandler = new MqttEventHandler(mqttEventHandlerLogger, mockFactory.Object); + MqttService = new MqttService(mqttServiceLogger, Factory.Configuration); + } + + public async Task DisposeAsync() + { + await Context.Database.EnsureDeletedAsync(); + await Context.Database.EnsureCreatedAsync(); } - public void Dispose() + async ValueTask IAsyncDisposable.DisposeAsync() { - _missionEventHandler.Dispose(); - GC.SuppressFinalize(this); + await Task.CompletedTask; + } + + private FlotillaDbContext CreateContext() + { + return TestSetupHelpers.ConfigureFlotillaDbContext(ConnectionString); } [Fact] public async Task ScheduledMissionStartedWhenSystemIsAvailable() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea ); - var missionRun = await _databaseUtilities.NewMissionRun( + var missionRun = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea ); // Act - await _missionRunService.Create(missionRun); + await MissionRunService.Create(missionRun); Thread.Sleep(100); // Assert - var postTestMissionRun = await _missionRunService.ReadById( + var postTestMissionRun = await MissionRunService.ReadById( missionRun.Id, readOnly: true ); @@ -256,39 +109,39 @@ public async Task ScheduledMissionStartedWhenSystemIsAvailable() public async Task SecondScheduledMissionQueuedIfRobotIsBusy() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea ); - var missionRunOne = await _databaseUtilities.NewMissionRun( + var missionRunOne = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea ); - var missionRunTwo = await _databaseUtilities.NewMissionRun( + var missionRunTwo = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea ); // Act - await _missionRunService.Create(missionRunOne); + await MissionRunService.Create(missionRunOne); Thread.Sleep(100); - await _missionRunService.Create(missionRunTwo); + await MissionRunService.Create(missionRunTwo); // Assert - var postTestMissionRunOne = await _missionRunService.ReadById( + var postTestMissionRunOne = await MissionRunService.ReadById( missionRunOne.Id, readOnly: true ); - var postTestMissionRunTwo = await _missionRunService.ReadById( + var postTestMissionRunTwo = await MissionRunService.ReadById( missionRunTwo.Id, readOnly: true ); @@ -300,24 +153,24 @@ public async Task SecondScheduledMissionQueuedIfRobotIsBusy() public async Task NewMissionIsStartedWhenRobotBecomesAvailable() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Busy, installation, inspectionArea ); - var missionRun = await _databaseUtilities.NewMissionRun( + var missionRun = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea ); - await _missionRunService.Create(missionRun); + await MissionRunService.Create(missionRun); Thread.Sleep(100); var mqttEventArgs = new MqttReceivedArgs( @@ -331,11 +184,11 @@ public async Task NewMissionIsStartedWhenRobotBecomesAvailable() ); // Act - _mqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); + MqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); Thread.Sleep(500); // Assert - var postTestMissionRun = await _missionRunService.ReadById( + var postTestMissionRun = await MissionRunService.ReadById( missionRun.Id, readOnly: true ); @@ -346,13 +199,13 @@ public async Task NewMissionIsStartedWhenRobotBecomesAvailable() public async Task ReturnToHomeMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvailable() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Busy, installation, inspectionArea @@ -369,11 +222,11 @@ public async Task ReturnToHomeMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvai ); // Act - _mqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); + MqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); // Assert Thread.Sleep(1000); - var ongoingMission = await _missionRunService.ReadAll( + var ongoingMission = await MissionRunService.ReadAll( new MissionRunQueryStringParameters { Statuses = [MissionStatus.Ongoing], @@ -389,19 +242,19 @@ public async Task ReturnToHomeMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvai public async Task ReturnToHomeMissionIsNotStartedIfReturnToHomeIsNotSupported() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Busy, installation, inspectionArea ); robot.RobotCapabilities!.Remove(RobotCapabilitiesEnum.return_to_home); - await _robotService.Update(robot); + await RobotService.Update(robot); var mqttEventArgs = new MqttReceivedArgs( new IsarStatusMessage @@ -414,11 +267,11 @@ public async Task ReturnToHomeMissionIsNotStartedIfReturnToHomeIsNotSupported() ); // Act - _mqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); + MqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs); // Assert Thread.Sleep(1000); - var ongoingMission = await _missionRunService.ReadAll( + var ongoingMission = await MissionRunService.ReadAll( new MissionRunQueryStringParameters { Statuses = [MissionStatus.Ongoing], @@ -434,39 +287,39 @@ public async Task ReturnToHomeMissionIsNotStartedIfReturnToHomeIsNotSupported() public async Task MissionRunIsStartedForOtherAvailableRobotIfOneRobotHasAnOngoingMissionRun() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robotOne = await _databaseUtilities.NewRobot( + var robotOne = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea ); - var robotTwo = await _databaseUtilities.NewRobot( + var robotTwo = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea ); - var missionRunOne = await _databaseUtilities.NewMissionRun( + var missionRunOne = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robotOne, inspectionArea ); - var missionRunTwo = await _databaseUtilities.NewMissionRun( + var missionRunTwo = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robotTwo, inspectionArea ); // Act (Ensure first mission is started) - await _missionRunService.Create(missionRunOne); + await MissionRunService.Create(missionRunOne); Thread.Sleep(100); // Assert - var postStartMissionRunOne = await _missionRunService.ReadById( + var postStartMissionRunOne = await MissionRunService.ReadById( missionRunOne.Id, readOnly: true ); @@ -474,11 +327,11 @@ public async Task MissionRunIsStartedForOtherAvailableRobotIfOneRobotHasAnOngoin Assert.Equal(MissionStatus.Ongoing, postStartMissionRunOne.Status); // Act (Ensure second mission is started for second robot) - await _missionRunService.Create(missionRunTwo); + await MissionRunService.Create(missionRunTwo); Thread.Sleep(100); // Assert - var postStartMissionRunTwo = await _missionRunService.ReadById( + var postStartMissionRunTwo = await MissionRunService.ReadById( missionRunTwo.Id, readOnly: true ); @@ -490,24 +343,20 @@ public async Task MissionRunIsStartedForOtherAvailableRobotIfOneRobotHasAnOngoin public async Task QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameTimeAsOnIsarMissionCompleted() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( - RobotStatus.Available, - installation, - null - ); - var missionRun1 = await _databaseUtilities.NewMissionRun( + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation, null); + var missionRun1 = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, true ); - var missionRun2 = await _databaseUtilities.NewMissionRun( + var missionRun2 = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, @@ -515,13 +364,13 @@ public async Task QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameT ); var missionRunCreatedEventArgs = new MissionRunCreatedEventArgs(missionRun1); - _missionRunService.RaiseEvent( - nameof(MissionRunService.MissionRunCreated), + MissionRunService.RaiseEvent( + nameof(Api.Services.MissionRunService.MissionRunCreated), missionRunCreatedEventArgs ); - Thread.Sleep(100); + Thread.Sleep(1000); - var missionRun1PostCreation = await _missionRunService.ReadById( + var missionRun1PostCreation = await MissionRunService.ReadById( missionRun1.Id, readOnly: true ); @@ -541,22 +390,22 @@ public async Task QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameT var robotAvailableEventArgs = new RobotAvailableEventArgs(robot); - _mqttService.RaiseEvent( + MqttService.RaiseEvent( nameof(MqttService.MqttIsarMissionReceived), mqttIsarMissionEventArgs ); - _missionSchedulingService.RaiseEvent( - nameof(MissionSchedulingService.RobotAvailable), + MissionSchedulingService.RaiseEvent( + nameof(Api.Services.MissionSchedulingService.RobotAvailable), robotAvailableEventArgs ); // Assert - var postTestMissionRun1 = await _missionRunService.ReadById( + var postTestMissionRun1 = await MissionRunService.ReadById( missionRun1.Id, readOnly: true ); Assert.Equal(MissionStatus.Successful, postTestMissionRun1!.Status); - var postTestMissionRun2 = await _missionRunService.ReadById( + var postTestMissionRun2 = await MissionRunService.ReadById( missionRun2.Id, readOnly: true ); @@ -569,18 +418,18 @@ public async Task QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameT public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMissionCompleted() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Available, installation, inspectionArea ); - var missionRun1 = await _databaseUtilities.NewMissionRun( + var missionRun1 = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, @@ -589,7 +438,7 @@ public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMis MissionStatus.Ongoing, Guid.NewGuid().ToString() ); - var missionRun2 = await _databaseUtilities.NewMissionRun( + var missionRun2 = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, @@ -598,8 +447,8 @@ public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMis Thread.Sleep(100); var missionRunCreatedEventArgs = new MissionRunCreatedEventArgs(missionRun1); - _missionRunService.RaiseEvent( - nameof(MissionRunService.MissionRunCreated), + MissionRunService.RaiseEvent( + nameof(Api.Services.MissionRunService.MissionRunCreated), missionRunCreatedEventArgs ); Thread.Sleep(100); @@ -626,18 +475,18 @@ public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMis } ); - _mqttService.RaiseEvent( + MqttService.RaiseEvent( nameof(MqttService.MqttIsarMissionReceived), mqttIsarMissionEventArgs ); - _mqttService.RaiseEvent( + MqttService.RaiseEvent( nameof(MqttService.MqttIsarStatusReceived), mqttIsarStatusEventArgs ); Thread.Sleep(2500); // Accommodate for sleep in OnIsarStatus // Assert - var postTestMissionRun1 = await _missionRunService.ReadById( + var postTestMissionRun1 = await MissionRunService.ReadById( missionRun1.Id, readOnly: true ); @@ -645,7 +494,7 @@ public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMis Api.Database.Models.TaskStatus.Successful, postTestMissionRun1!.Tasks[0].Status ); - var postTestMissionRun2 = await _missionRunService.ReadById( + var postTestMissionRun2 = await MissionRunService.ReadById( missionRun2.Id, readOnly: true ); @@ -656,18 +505,18 @@ public async Task QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMis public async Task ReturnHomeMissionAbortedIfNewMissionScheduled() { // Arrange - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.NewInstallation(); + var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.NewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot( + var robot = await DatabaseUtilities.NewRobot( RobotStatus.Busy, installation, inspectionArea ); - var returnToHomeMission = await _databaseUtilities.NewMissionRun( + var returnToHomeMission = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, @@ -676,7 +525,7 @@ public async Task ReturnHomeMissionAbortedIfNewMissionScheduled() MissionStatus.Ongoing, Guid.NewGuid().ToString() ); - var missionRun = await _databaseUtilities.NewMissionRun( + var missionRun = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea, @@ -690,11 +539,14 @@ public async Task ReturnHomeMissionAbortedIfNewMissionScheduled() // Act var eventArgs = new MissionRunCreatedEventArgs(missionRun); - _missionRunService.RaiseEvent(nameof(MissionRunService.MissionRunCreated), eventArgs); + MissionRunService.RaiseEvent( + nameof(Api.Services.MissionRunService.MissionRunCreated), + eventArgs + ); Thread.Sleep(500); // Assert - var updatedReturnHomeMission = await _missionRunService.ReadById( + var updatedReturnHomeMission = await MissionRunService.ReadById( returnToHomeMission.Id, readOnly: true ); @@ -722,20 +574,17 @@ public async Task ReturnHomeMissionAbortedIfNewMissionScheduled() } ); - _mqttService.RaiseEvent( + MqttService.RaiseEvent( nameof(MqttService.MqttIsarMissionReceived), mqttIsarMissionEventArgs ); - _mqttService.RaiseEvent( + MqttService.RaiseEvent( nameof(MqttService.MqttIsarStatusReceived), mqttIsarStatusEventArgs ); Thread.Sleep(500); - var updatedMissionRun = await _missionRunService.ReadById( - missionRun.Id, - readOnly: true - ); + var updatedMissionRun = await MissionRunService.ReadById(missionRun.Id, readOnly: true); Assert.True(updatedMissionRun?.Status.Equals(MissionStatus.Ongoing)); } } diff --git a/backend/api.test/Services/MissionService.cs b/backend/api.test/Services/MissionService.cs index 9c623511..133d74ad 100644 --- a/backend/api.test/Services/MissionService.cs +++ b/backend/api.test/Services/MissionService.cs @@ -1,93 +1,40 @@ using System; using System.Threading.Tasks; using Api.Controllers.Models; -using Api.Database.Context; using Api.Database.Models; using Api.Services; using Api.Test.Database; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Api.Test.Services { - [Collection("Database collection")] - public class MissionServiceTest : IDisposable + public class MissionServiceTest : IAsyncLifetime { - private readonly FlotillaDbContext _context; - private readonly DatabaseUtilities _databaseUtilities; - private readonly ILogger _logger; - private readonly MissionRunService _missionRunService; - private readonly ISignalRService _signalRService; - private readonly IAccessRoleService _accessRoleService; - private readonly UserInfoService _userInfoService; - private readonly IMissionTaskService _missionTaskService; - private readonly IInspectionAreaService _inspectionAreaService; - private readonly IInstallationService _installationService; - private readonly IPlantService _plantService; - private readonly IRobotModelService _robotModelService; - private readonly IRobotService _robotService; + public required DatabaseUtilities DatabaseUtilities; + public required IMissionRunService MissionRunService; - public MissionServiceTest(DatabaseFixture fixture) + public async Task InitializeAsync() { - _context = fixture.NewContext; - var defaultLocalizationPoseService = new DefaultLocalizationPoseService(_context); - _logger = new Mock>().Object; - _signalRService = new MockSignalRService(); - _accessRoleService = new AccessRoleService(_context, new HttpContextAccessor()); - _userInfoService = new UserInfoService( - _context, - new HttpContextAccessor(), - new Mock>().Object + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); - _missionTaskService = new MissionTaskService( - _context, - new Mock>().Object - ); - _installationService = new InstallationService(_context, _accessRoleService); - _plantService = new PlantService(_context, _installationService, _accessRoleService); - _inspectionAreaService = new InspectionAreaService( - _context, - defaultLocalizationPoseService, - _installationService, - _plantService, - _accessRoleService, - new MockSignalRService() - ); - _robotModelService = new RobotModelService(_context); - _robotService = new RobotService( - _context, - new Mock>().Object, - _robotModelService, - new MockSignalRService(), - _accessRoleService, - _installationService, - _inspectionAreaService - ); - _missionRunService = new MissionRunService( - _context, - _signalRService, - _logger, - _accessRoleService, - _missionTaskService, - _inspectionAreaService, - _robotService, - _userInfoService + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + var serviceProvider = TestSetupHelpers.ConfigureServiceProvider(factory); + + DatabaseUtilities = new DatabaseUtilities( + TestSetupHelpers.ConfigureFlotillaDbContext(connectionString) ); - _databaseUtilities = new DatabaseUtilities(_context); + MissionRunService = serviceProvider.GetRequiredService(); } - public void Dispose() - { - _context.Dispose(); - GC.SuppressFinalize(this); - } + public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task ReadIdDoesNotExist() { - var missionRun = await _missionRunService.ReadById( + var missionRun = await MissionRunService.ReadById( "some_id_that_does_not_exist", readOnly: true ); @@ -97,28 +44,28 @@ public async Task ReadIdDoesNotExist() [Fact] public async Task Create() { - var reportsBefore = await _missionRunService.ReadAll( + var reportsBefore = await MissionRunService.ReadAll( new MissionRunQueryStringParameters(), readOnly: true ); int nReportsBefore = reportsBefore.Count; - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var plant = await _databaseUtilities.ReadOrNewPlant(installation.InstallationCode); - var inspectionArea = await _databaseUtilities.ReadOrNewInspectionArea( + var installation = await DatabaseUtilities.ReadOrNewInstallation(); + var plant = await DatabaseUtilities.ReadOrNewPlant(installation.InstallationCode); + var inspectionArea = await DatabaseUtilities.ReadOrNewInspectionArea( installation.InstallationCode, plant.PlantCode ); - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); - var missionRun = await _databaseUtilities.NewMissionRun( + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); + var missionRun = await DatabaseUtilities.NewMissionRun( installation.InstallationCode, robot, inspectionArea ); - await _missionRunService.Create(missionRun); + await MissionRunService.Create(missionRun); - var reportsAfter = await _missionRunService.ReadAll( + var reportsAfter = await MissionRunService.ReadAll( new MissionRunQueryStringParameters() ); int nReportsAfter = reportsAfter.Count; diff --git a/backend/api.test/Services/RobotService.cs b/backend/api.test/Services/RobotService.cs index 32025805..b58ddca9 100644 --- a/backend/api.test/Services/RobotService.cs +++ b/backend/api.test/Services/RobotService.cs @@ -2,82 +2,45 @@ using System.Linq; using System.Threading.Tasks; using Api.Controllers.Models; -using Api.Database.Context; using Api.Database.Models; using Api.Services; using Api.Test.Database; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Api.Test.Services { - [Collection("Database collection")] - public class RobotServiceTest : IDisposable + public class RobotServiceTest : IAsyncLifetime { - private readonly FlotillaDbContext _context; - private readonly ILogger _logger; - private readonly RobotModelService _robotModelService; - private readonly ISignalRService _signalRService; - private readonly IAccessRoleService _accessRoleService; - private readonly IInstallationService _installationService; - private readonly IPlantService _plantService; - private readonly IDefaultLocalizationPoseService _defaultLocalizationPoseService; - private readonly IInspectionAreaService _inspectionAreaService; - private readonly IAreaService _areaService; - private readonly DatabaseUtilities _databaseUtilities; + public required DatabaseUtilities DatabaseUtilities; + public required IRobotService RobotService; + public required IInstallationService InstallationService; - public RobotServiceTest(DatabaseFixture fixture) + public async Task InitializeAsync() { - _context = fixture.NewContext; - _databaseUtilities = new DatabaseUtilities(_context); - _logger = new Mock>().Object; - _robotModelService = new RobotModelService(_context); - _signalRService = new MockSignalRService(); - _accessRoleService = new AccessRoleService(_context, new HttpContextAccessor()); - _installationService = new InstallationService(_context, _accessRoleService); - _plantService = new PlantService(_context, _installationService, _accessRoleService); - _defaultLocalizationPoseService = new DefaultLocalizationPoseService(_context); - _inspectionAreaService = new InspectionAreaService( - _context, - _defaultLocalizationPoseService, - _installationService, - _plantService, - _accessRoleService, - _signalRService + string databaseName = Guid.NewGuid().ToString(); + (string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase( + databaseName ); - _areaService = new AreaService( - _context, - _installationService, - _plantService, - _inspectionAreaService, - _defaultLocalizationPoseService, - _accessRoleService + var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName); + var serviceProvider = TestSetupHelpers.ConfigureServiceProvider(factory); + + DatabaseUtilities = new DatabaseUtilities( + TestSetupHelpers.ConfigureFlotillaDbContext(connectionString) ); - } - public void Dispose() - { - _context.Dispose(); - GC.SuppressFinalize(this); + RobotService = serviceProvider.GetRequiredService(); + InstallationService = serviceProvider.GetRequiredService(); } + public Task DisposeAsync() => Task.CompletedTask; + [Fact] public async Task ReadAll() { - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var _ = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); - var robotService = new RobotService( - _context, - _logger, - _robotModelService, - _signalRService, - _accessRoleService, - _installationService, - _inspectionAreaService - ); - var robots = await robotService.ReadAll(); + var installation = await DatabaseUtilities.NewInstallation(); + _ = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); + var robots = await RobotService.ReadAll(); Assert.True(robots.Any()); } @@ -85,18 +48,9 @@ public async Task ReadAll() [Fact] public async Task Read() { - var robotService = new RobotService( - _context, - _logger, - _robotModelService, - _signalRService, - _accessRoleService, - _installationService, - _inspectionAreaService - ); - var installation = await _databaseUtilities.ReadOrNewInstallation(); - var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation); - var robotById = await robotService.ReadById(robot.Id, readOnly: true); + var installation = await DatabaseUtilities.NewInstallation(); + var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); + var robotById = await RobotService.ReadById(robot.Id, readOnly: false); Assert.NotNull(robotById); Assert.Equal(robot.Id, robotById.Id); } @@ -104,62 +58,23 @@ public async Task Read() [Fact] public async Task ReadIdDoesNotExist() { - var robotService = new RobotService( - _context, - _logger, - _robotModelService, - _signalRService, - _accessRoleService, - _installationService, - _inspectionAreaService - ); - var robot = await robotService.ReadById("some_id_that_does_not_exist", readOnly: true); + var robot = await RobotService.ReadById("some_id_that_does_not_exist", readOnly: true); Assert.Null(robot); } [Fact] public async Task Create() { - var robotService = new RobotService( - _context, - _logger, - _robotModelService, - _signalRService, - _accessRoleService, - _installationService, - _inspectionAreaService - ); - var installationService = new InstallationService(_context, _accessRoleService); - - var installation = await installationService.Create( + var installation = await InstallationService.Create( new CreateInstallationQuery { Name = "Johan Sverdrup", InstallationCode = "JSV" } ); - var robotsBefore = await robotService.ReadAll(readOnly: true); + var robotsBefore = await RobotService.ReadAll(readOnly: true); int nRobotsBefore = robotsBefore.Count(); - var documentationQuery = new CreateDocumentationQuery - { - Name = "Some document", - Url = "someURL", - }; - var robotQuery = new CreateRobotQuery - { - Name = "", - IsarId = "", - SerialNumber = "", - Documentation = [documentationQuery], - CurrentInstallationCode = installation.InstallationCode, - RobotType = RobotType.Robot, - Host = "", - Port = 1, - Status = RobotStatus.Available, - }; - var robotModel = _context.RobotModels.First(); - var robot = new Robot(robotQuery, installation, robotModel); + _ = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation); - await robotService.Create(robot); - var robotsAfter = await robotService.ReadAll(readOnly: true); + var robotsAfter = await RobotService.ReadAll(readOnly: true); int nRobotsAfter = robotsAfter.Count(); Assert.Equal(nRobotsBefore + 1, nRobotsAfter); diff --git a/backend/api.test/TestSetupHelpers.cs b/backend/api.test/TestSetupHelpers.cs new file mode 100644 index 00000000..8c8af5a3 --- /dev/null +++ b/backend/api.test/TestSetupHelpers.cs @@ -0,0 +1,82 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Api.Database.Context; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; + +namespace Api.Test; + +public static class TestSetupHelpers +{ + public static async Task<(string, DbConnection)> ConfigureDatabase(string databaseName) + { + string connectionString = new SqliteConnectionStringBuilder + { + DataSource = $"file:{databaseName}?mode=memory", + Cache = SqliteCacheMode.Shared, + }.ToString(); + + var context = ConfigureFlotillaDbContext(connectionString); + await context.Database.EnsureCreatedAsync(); + + var connection = context.Database.GetDbConnection(); + await connection.OpenAsync(); + + return (connectionString, connection); + } + + public static JsonSerializerOptions ConfigureJsonSerializerOptions() + { + return new JsonSerializerOptions + { + Converters = { new JsonStringEnumConverter() }, + PropertyNameCaseInsensitive = true, + }; + } + + public static IServiceProvider ConfigureServiceProvider( + TestWebApplicationFactory factory + ) + { + return factory.Services; + } + + public static FlotillaDbContext ConfigureFlotillaDbContext(string connectionString) + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.EnableSensitiveDataLogging(); + optionsBuilder.UseSqlite(connectionString); + + var context = new FlotillaDbContext(optionsBuilder.Options); + return context; + } + + public static TestWebApplicationFactory ConfigureWebApplicationFactory( + string databaseName + ) + { + return new TestWebApplicationFactory(databaseName); + } + + public static HttpClient ConfigureHttpClient(TestWebApplicationFactory factory) + { + var client = factory.CreateClient( + new WebApplicationFactoryClientOptions + { + AllowAutoRedirect = false, + BaseAddress = new Uri("http://localhost:8000"), + } + ); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + TestAuthHandler.AuthenticationScheme + ); + + return client; + } +} diff --git a/backend/api.test/TestWebApplicationFactory.cs b/backend/api.test/TestWebApplicationFactory.cs index 53587c71..1a69e57f 100644 --- a/backend/api.test/TestWebApplicationFactory.cs +++ b/backend/api.test/TestWebApplicationFactory.cs @@ -1,4 +1,5 @@ using System.IO; +using Api.Database.Context; using Api.Services; using Api.Services.MissionLoaders; using Api.Test.Mocks; @@ -7,19 +8,24 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Api.Test { - public class TestWebApplicationFactory : WebApplicationFactory + public class TestWebApplicationFactory(string databaseName) + : WebApplicationFactory where TProgram : class { + public IConfiguration? Configuration; + protected override void ConfigureWebHost(IWebHostBuilder builder) { string projectDir = Directory.GetCurrentDirectory(); string configPath = Path.Combine(projectDir, "appsettings.Test.json"); - var configuration = new ConfigurationBuilder().AddJsonFile(configPath).Build(); + Configuration = new ConfigurationBuilder().AddJsonFile(configPath).Build(); builder.UseEnvironment("Test"); builder.ConfigureAppConfiguration( (context, config) => @@ -29,6 +35,19 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) ); builder.ConfigureTestServices(services => { + string sqlLiteConnectionString = new SqliteConnectionStringBuilder + { + DataSource = $"file:{databaseName}?mode=memory", + Cache = SqliteCacheMode.Shared, + }.ToString(); + + services.AddDbContext(options => + options.UseSqlite( + sqlLiteConnectionString, + o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery) + ) + ); + services.AddScoped(); services.AddScoped(); services.AddSingleton(); diff --git a/backend/api/Configurations/CustomServiceConfigurations.cs b/backend/api/Configurations/CustomServiceConfigurations.cs index 2e9c7155..e4a3e929 100644 --- a/backend/api/Configurations/CustomServiceConfigurations.cs +++ b/backend/api/Configurations/CustomServiceConfigurations.cs @@ -11,14 +11,20 @@ public static class CustomServiceConfigurations { public static IServiceCollection ConfigureDatabase( this IServiceCollection services, - IConfiguration configuration + IConfiguration configuration, + string environmentName ) { bool useInMemoryDatabase = configuration .GetSection("Database") .GetValue("UseInMemoryDatabase"); - if (useInMemoryDatabase) + if (environmentName.Equals("Test", StringComparison.Ordinal)) + { + Console.WriteLine("Using TEST"); + // The application is running in a test state and database will be configured there + } + else if (useInMemoryDatabase) { DbContextOptionsBuilder dbBuilder = new DbContextOptionsBuilder(); diff --git a/backend/api/EventHandlers/MqttEventHandler.cs b/backend/api/EventHandlers/MqttEventHandler.cs index 78de91df..880e20b8 100644 --- a/backend/api/EventHandlers/MqttEventHandler.cs +++ b/backend/api/EventHandlers/MqttEventHandler.cs @@ -390,7 +390,7 @@ private async void OnIsarMissionUpdate(object? sender, MqttReceivedArgs mqttArgs if (flotillaMissionRun is null) { string errorMessage = - $"Mission with isar mission Id {isarMission.IsarId} was not found"; + $"Mission with isar mission Id {isarMission.MissionId} was not found"; _logger.LogError("{Message}", errorMessage); return; } diff --git a/backend/api/Program.cs b/backend/api/Program.cs index cc6230b4..ef0182c7 100644 --- a/backend/api/Program.cs +++ b/backend/api/Program.cs @@ -45,7 +45,7 @@ builder.ConfigureLogger(); -builder.Services.ConfigureDatabase(builder.Configuration); +builder.Services.ConfigureDatabase(builder.Configuration, builder.Environment.EnvironmentName); builder.Services.ConfigureMissionLoader(builder.Configuration);