From d59f7d689dabf71569c02b1560d464f0ea4532e3 Mon Sep 17 00:00:00 2001 From: Christian de Jonge Date: Mon, 6 Nov 2023 09:49:41 +0100 Subject: [PATCH] Remove Populate Area function in tests and split endpointtest file --- backend/api.test/Client/AreaTests.cs | 331 +++++++ backend/api.test/Client/MissionTests.cs | 432 +++++++++ backend/api.test/Client/RobotTests.cs | 77 ++ backend/api.test/EndpointTest.cs | 889 ------------------ backend/api.test/Mocks/EchoServiceMock.cs | 20 +- backend/api.test/TestWebApplicationFactory.cs | 66 ++ .../MissionSchedulingController.cs | 11 +- .../api/Controllers/Models/AreaResponse.cs | 10 +- .../api/Controllers/Models/DeckResponse.cs | 8 +- .../Models/MissionDefinitionResponse.cs | 9 +- 10 files changed, 937 insertions(+), 916 deletions(-) create mode 100644 backend/api.test/Client/AreaTests.cs create mode 100644 backend/api.test/Client/MissionTests.cs create mode 100644 backend/api.test/Client/RobotTests.cs delete mode 100644 backend/api.test/EndpointTest.cs create mode 100644 backend/api.test/TestWebApplicationFactory.cs diff --git a/backend/api.test/Client/AreaTests.cs b/backend/api.test/Client/AreaTests.cs new file mode 100644 index 000000000..f97cb2dc1 --- /dev/null +++ b/backend/api.test/Client/AreaTests.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +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.Models; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; +namespace Api.Test +{ + [Collection("Database collection")] + public class AreaTests : IClassFixture> + { + private readonly HttpClient _client; + private readonly JsonSerializerOptions _serializerOptions = + new() + { + Converters = + { + new JsonStringEnumConverter() + }, + PropertyNameCaseInsensitive = true + }; + + public AreaTests(TestWebApplicationFactory factory) + { + _client = factory.CreateClient(new WebApplicationFactoryClientOptions + { + AllowAutoRedirect = false, + BaseAddress = new Uri("https://localhost:8000") + }); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + TestAuthHandler.AuthenticationScheme + ); + } + + [Fact] + public async Task AreaTest() + { + // Arrange + var testPose = new Pose + { + Position = new Position + { + X = 1, + Y = 2, + Z = 2 + }, + Orientation = new Orientation + { + X = 0, + Y = 0, + Z = 0, + W = 1 + } + }; + + string testInstallation = "TestInstallationAreaTest"; + var installationQuery = new CreateInstallationQuery + { + InstallationCode = testInstallation, + Name = testInstallation + }; + + string testPlant = "TestPlantAreaTest"; + var plantQuery = new CreatePlantQuery + { + InstallationCode = testInstallation, + PlantCode = testPlant, + Name = testPlant + }; + + string testDeck = "testDeckAreaTest"; + var deckQuery = new CreateDeckQuery + { + InstallationCode = testInstallation, + PlantCode = testPlant, + Name = testDeck + }; + + string testArea = "testAreaAreaTest"; + var areaQuery = new CreateAreaQuery + { + InstallationCode = testInstallation, + PlantCode = testPlant, + DeckName = testDeck, + AreaName = testArea, + DefaultLocalizationPose = testPose + }; + + var installationContent = new StringContent( + JsonSerializer.Serialize(installationQuery), + null, + "application/json" + ); + + var plantContent = new StringContent( + JsonSerializer.Serialize(plantQuery), + null, + "application/json" + ); + + var deckContent = new StringContent( + JsonSerializer.Serialize(deckQuery), + null, + "application/json" + ); + + var areaContent = new StringContent( + JsonSerializer.Serialize(areaQuery), + null, + "application/json" + ); + + // Act + string installationUrl = "/installations"; + var installationResponse = await _client.PostAsync(installationUrl, installationContent); + string plantUrl = "/plants"; + var plantResponse = await _client.PostAsync(plantUrl, plantContent); + string deckUrl = "/decks"; + var deckResponse = await _client.PostAsync(deckUrl, deckContent); + string areaUrl = "/areas"; + var areaResponse = await _client.PostAsync(areaUrl, areaContent); + + // Assert + Assert.True(installationResponse.IsSuccessStatusCode); + Assert.True(plantResponse.IsSuccessStatusCode); + Assert.True(deckResponse.IsSuccessStatusCode); + Assert.True(areaResponse.IsSuccessStatusCode); + var area = await areaResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.True(area != null); + } + + [Fact] + public async Task GetMissionsInAreaTest() + { + // Arrange + // Robot + string robotUrl = "/robots"; + var robotResponse = await _client.GetAsync(robotUrl); + Assert.True(robotResponse.IsSuccessStatusCode); + var robots = await robotResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + + // Installation + string installationUrl = "/installations"; + var installationResponse = await _client.GetAsync(installationUrl); + Assert.True(installationResponse.IsSuccessStatusCode); + var installations = await installationResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(installations != null); + var installation = installations[0]; + + // Area + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areas = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areas != null); + var area = areas[0]; + string areaId = area.Id; + + string testMissionName = "testMissionInAreaTest"; + + var inspections = new List + { + new() { AnalysisType = AnalysisType.CarSeal, InspectionType = InspectionType.Image} + }; + var tasks = new List + { + new() { Inspections = inspections, InspectionTarget = new Position(), TagId = "test", RobotPose = new Pose(), TaskOrder = 0} + }; + var missionQuery = new CustomMissionQuery + { + RobotId = robotId, + DesiredStartTime = DateTime.UtcNow, + InstallationCode = installation.InstallationCode, + AreaName = area.AreaName, + Name = testMissionName, + Tasks = tasks + }; + + var missionContent = new StringContent( + JsonSerializer.Serialize(missionQuery), + null, + "application/json" + ); + + // Act + string missionUrl = "/missions/custom"; + var missionResponse = await _client.PostAsync(missionUrl, missionContent); + + Assert.True(missionResponse.IsSuccessStatusCode); + var mission = await missionResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.NotNull(mission); + Assert.NotNull(mission.MissionId); + + var areaMissionsResponse = await _client.GetAsync(areaUrl + $"/{areaId}/mission-definitions"); + + // Assert + Assert.True(areaMissionsResponse.IsSuccessStatusCode); + var missions = await areaMissionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.NotNull(missions); + Assert.Single(missions.Where(m => m.Id.Equals(mission.MissionId, StringComparison.Ordinal))); + } + + [Fact] + public async Task SafePositionTest() + { + // Arrange + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + string addSafePositionUrl = $"/areas/{installationCode}/{areaName}/safe-position"; + var testPosition = new Position + { + X = 1, + Y = 2, + Z = 2 + }; + var query = new Pose + { + Position = testPosition, + Orientation = new Orientation + { + X = 0, + Y = 0, + Z = 0, + W = 1 + } + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + areaResponse = await _client.PostAsync(addSafePositionUrl, content); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaContent = await areaResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.True(areaContent != null); + + // Act + string goToSafePositionUrl = $"/emergency-action/{installationCode}/abort-current-missions-and-send-all-robots-to-safe-zone"; + var missionResponse = await _client.PostAsync(goToSafePositionUrl, null); + + // Assert + Assert.True(missionResponse.IsSuccessStatusCode); + + } + + [Fact] + public async Task GetMapMetadataNotFound() + { + // Arrange + string areaUrl = "/areas"; + var response = await _client.GetAsync(areaUrl); + Assert.True(response.IsSuccessStatusCode); + var areas = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areas != null); + var areaResponse = areas[0]; + string areaId = areaResponse.Id; + string invalidAreaId = "InvalidId"; + + // Act + string url = $"/areas/{areaId}/map-metadata"; + response = await _client.GetAsync(url); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + // Assert + string invalidUrl = $"/areas/{invalidAreaId}/map-metadata"; + var responseInvalid = await _client.GetAsync(invalidUrl); + Assert.Equal(HttpStatusCode.NotFound, responseInvalid.StatusCode); + } + + [Fact] + public async Task UpdateDefaultLocalizationPoseOnDeck() + { + string deckUrl = "/decks"; + var deckResponse = await _client.GetAsync(deckUrl); + Assert.True(deckResponse.IsSuccessStatusCode); + var decks = await deckResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(decks != null); + var deck = decks[0]; + string deckId = deck.Id; + + string url = $"/decks/{deckId}/update-default-localization-pose"; + var query = new Pose + { + Position = new Position + { + X = 1, + Y = 2, + Z = 3 + }, + Orientation = new Orientation + { + X = 0, + Y = 0, + Z = 0, + W = 1 + } + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + var putResponse = await _client.PutAsync(url, content); + Assert.True(putResponse.IsSuccessStatusCode); + var putDeck = await putResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.True(putDeck != null); + Assert.True(putDeck.DefaultLocalizationPose != null); + Assert.True(putDeck.DefaultLocalizationPose.Pose.Position.Z.Equals(query.Position.Z)); + Assert.True(putDeck.DefaultLocalizationPose.Pose.Orientation.W.Equals(query.Orientation.W)); + } + } +} diff --git a/backend/api.test/Client/MissionTests.cs b/backend/api.test/Client/MissionTests.cs new file mode 100644 index 000000000..c263e502e --- /dev/null +++ b/backend/api.test/Client/MissionTests.cs @@ -0,0 +1,432 @@ +using System; +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.Models; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; +namespace Api.Test +{ + [Collection("Database collection")] + public class MissionTests : IClassFixture> + { + private readonly HttpClient _client; + private readonly JsonSerializerOptions _serializerOptions = + new() + { + Converters = + { + new JsonStringEnumConverter() + }, + PropertyNameCaseInsensitive = true + }; + + public MissionTests(TestWebApplicationFactory factory) + { + _client = factory.CreateClient(new WebApplicationFactoryClientOptions + { + AllowAutoRedirect = false, + BaseAddress = new Uri("https://localhost:8000") + }); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + TestAuthHandler.AuthenticationScheme + ); + } + + + [Fact] + public async Task MissionsTest() + { + // Arrange - Robots + string robotUrl = "/robots"; + string missionsUrl = "/missions"; + var robotResponse = await _client.GetAsync(robotUrl); + Assert.True(robotResponse.IsSuccessStatusCode); + var robots = await robotResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + + // Arrange - Areas + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + int echoMissionId = 97; + + // Act + var query = new ScheduledMissionQuery + { + RobotId = robotId, + InstallationCode = installationCode, + AreaName = areaName, + EchoMissionId = echoMissionId, + DesiredStartTime = DateTime.UtcNow + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + // 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 missionRuns = await response.Content.ReadFromJsonAsync>( + _serializerOptions + ); + Assert.True(response.IsSuccessStatusCode); + Assert.True(missionRuns != null); + int missionRunsBefore = missionRuns.Count; + + await _client.PostAsync(missionsUrl, content); + await _client.PostAsync(missionsUrl, content); + await _client.PostAsync(missionsUrl, content); + + response = await _client.GetAsync(urlMissionRuns); + missionRuns = await response.Content.ReadFromJsonAsync>( + _serializerOptions + ); + + // Assert + Assert.True(response.IsSuccessStatusCode); + Assert.True(missionRuns != null); + Assert.True(missionRuns.Count == (missionRunsBefore + 3)); + } + + [Fact] + public async Task GetMissionById_ShouldReturnNotFound() + { + string missionId = "RandomString"; + string url = "/missions/runs/" + missionId; + var response = await _client.GetAsync(url); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task DeleteMission_ShouldReturnNotFound() + { + string missionId = "RandomString"; + string url = "/missions/runs/" + missionId; + var response = await _client.DeleteAsync(url); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task StartMissionTest() + { + // Arrange - Robots + string robotUrl = "/robots"; + string missionsUrl = "/missions"; + var response = await _client.GetAsync(robotUrl); + Assert.True(response.IsSuccessStatusCode); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + + // Arrange - Areas + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + int echoMissionId = 95; + + // Act + var query = new ScheduledMissionQuery + { + RobotId = robotId, + InstallationCode = installationCode, + AreaName = areaName, + EchoMissionId = echoMissionId, + DesiredStartTime = DateTime.UtcNow + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + response = await _client.PostAsync(missionsUrl, content); + + // Assert + Assert.True(response.IsSuccessStatusCode); + var missionRun = await response.Content.ReadFromJsonAsync(_serializerOptions); + Assert.True(missionRun != null); + Assert.True(missionRun.Id != null); + Assert.True(missionRun.Status == MissionStatus.Pending); + } + + [Fact] + public async Task ScheduleDuplicateCustomMissionDefinitions() + { + // Arrange - Initialise areas + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + string testMissionName = "testMissionScheduleDuplicateCustomMissionDefinitions"; + + // Arrange - Create custom mission definition + string robotUrl = "/robots"; + var response = await _client.GetAsync(robotUrl); + Assert.True(response.IsSuccessStatusCode); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + + var query = new CustomMissionQuery + { + RobotId = robotId, + InstallationCode = installationCode, + AreaName = areaName, + DesiredStartTime = DateTime.SpecifyKind(new DateTime(3050, 1, 1), DateTimeKind.Utc), + InspectionFrequency = new TimeSpan(14, 0, 0, 0), + Name = testMissionName, + Tasks = new List + { + new() + { + RobotPose = new Pose(), + Inspections = new List(), + InspectionTarget = new Position(), + TaskOrder = 0 + }, + new() + { + RobotPose = new Pose(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f), + Inspections = new List(), + InspectionTarget = new Position(), + TaskOrder = 1 + } + } + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + // Act + string customMissionsUrl = "/missions/custom"; + 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); + var missionRun2 = await response2.Content.ReadFromJsonAsync(_serializerOptions); + Assert.NotNull(missionRun1); + Assert.NotNull(missionRun2); + string? missionId1 = missionRun1.MissionId; + string? missionId2 = missionRun2.MissionId; + 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 missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.NotNull(missionDefinitions); + Assert.NotNull(missionDefinitions.Find(m => m.Id == missionId1)); + } + + [Fact] + public async Task GetNextRun() + { + // Arrange - Initialise areas + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + // Arrange - Create custom mission definition + string robotUrl = "/robots"; + var response = await _client.GetAsync(robotUrl); + Assert.True(response.IsSuccessStatusCode); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + + string testMissionName = "testMissionNextRun"; + var query = new CustomMissionQuery + { + RobotId = robotId, + InstallationCode = installationCode, + AreaName = areaName, + DesiredStartTime = DateTime.SpecifyKind(new DateTime(3050, 1, 1), DateTimeKind.Utc), + InspectionFrequency = new TimeSpan(14, 0, 0, 0), + Name = testMissionName, + Tasks = new List + { + new() + { + RobotPose = new Pose(), + Inspections = new List(), + InspectionTarget = new Position(), + TaskOrder = 0 + } + } + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + string customMissionsUrl = "/missions/custom"; + response = await _client.PostAsync(customMissionsUrl, content); + Assert.True(response.IsSuccessStatusCode); + var missionRun = await response.Content.ReadFromJsonAsync(_serializerOptions); + Assert.True(missionRun != null); + Assert.True(missionRun.MissionId != null); + Assert.True(missionRun.Id != null); + Assert.True(missionRun.Status == MissionStatus.Pending); + + // Arrange - Schedule missions from mission definition + var scheduleQuery1 = new ScheduleMissionQuery + { + RobotId = robotId, + DesiredStartTime = DateTime.SpecifyKind(new DateTime(2050, 1, 1), DateTimeKind.Utc), + MissionDefinitionId = missionRun.MissionId + }; + var scheduleContent1 = new StringContent( + JsonSerializer.Serialize(scheduleQuery1), + null, + "application/json" + ); + var scheduleQuery2 = new ScheduleMissionQuery + { + RobotId = robotId, + DesiredStartTime = DateTime.UtcNow, + MissionDefinitionId = missionRun.MissionId + }; + var scheduleContent2 = new StringContent( + JsonSerializer.Serialize(scheduleQuery2), + null, + "application/json" + ); + var scheduleQuery3 = new ScheduleMissionQuery + { + RobotId = robotId, + DesiredStartTime = DateTime.SpecifyKind(new DateTime(2100, 1, 1), DateTimeKind.Utc), + MissionDefinitionId = missionRun.MissionId + }; + var scheduleContent3 = new StringContent( + JsonSerializer.Serialize(scheduleQuery3), + null, + "application/json" + ); + string scheduleMissionsUrl = "/missions/schedule"; + 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); + var missionRun2 = await missionRun2Response.Content.ReadFromJsonAsync(_serializerOptions); + var missionRun3 = await missionRun3Response.Content.ReadFromJsonAsync(_serializerOptions); + + // Act + string nextMissionUrl = $"missions/definitions/{missionRun.MissionId}/next-run"; + var nextMissionResponse = await _client.GetAsync(nextMissionUrl); + + // Assert + Assert.True(nextMissionResponse.IsSuccessStatusCode); + var nextMissionRun = await nextMissionResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.NotNull(nextMissionRun); + Assert.NotNull(missionRun1); + Assert.NotNull(missionRun2); + Assert.NotNull(missionRun3); + Assert.Equal(missionRun1.MissionId, missionRun.MissionId); + Assert.Equal(missionRun2.MissionId, missionRun.MissionId); + Assert.Equal(missionRun3.MissionId, missionRun.MissionId); + Assert.True(nextMissionRun.Id == missionRun2.Id); + } + + [Fact] + public async Task ScheduleDuplicateEchoMissionDefinitions() + { + // Arrange - Initialise areas + string areaUrl = "/areas"; + var areaResponse = await _client.GetAsync(areaUrl); + Assert.True(areaResponse.IsSuccessStatusCode); + var areaResponses = await areaResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(areaResponses != null); + var area = areaResponses[0]; + string areaName = area.AreaName; + string installationCode = area.InstallationCode; + + + // Arrange - Create echo mission definition + string robotUrl = "/robots"; + var response = await _client.GetAsync(robotUrl); + Assert.True(response.IsSuccessStatusCode); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(robots != null); + var robot = robots[0]; + string robotId = robot.Id; + int echoMissionId = 1; // Corresponds to mock in EchoServiceMock.cs + + var query = new ScheduledMissionQuery + { + RobotId = robotId, + InstallationCode = installationCode, + AreaName = areaName, + EchoMissionId = echoMissionId, + DesiredStartTime = DateTime.UtcNow + }; + var content = new StringContent( + JsonSerializer.Serialize(query), + null, + "application/json" + ); + + // Act + string echoMissionsUrl = "/missions"; + var response1 = await _client.PostAsync(echoMissionsUrl, content); + var response2 = await _client.PostAsync(echoMissionsUrl, content); + + // Assert + Assert.True(response1.IsSuccessStatusCode); + Assert.True(response2.IsSuccessStatusCode); + var missionRun1 = await response1.Content.ReadFromJsonAsync(_serializerOptions); + var missionRun2 = await response2.Content.ReadFromJsonAsync(_serializerOptions); + Assert.NotNull(missionRun1); + Assert.NotNull(missionRun2); + string? missionId1 = missionRun1.MissionId; + string? missionId2 = missionRun2.MissionId; + Assert.Equal(missionId1, missionId2); + + string missionDefinitionsUrl = "/missions/definitions"; + var missionDefinitionsResponse = await _client.GetAsync(missionDefinitionsUrl); + var missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.NotNull(missionDefinitions); + Assert.NotNull(missionDefinitions.Find(m => m.Id == missionId1)); + } + } +} diff --git a/backend/api.test/Client/RobotTests.cs b/backend/api.test/Client/RobotTests.cs new file mode 100644 index 000000000..1f6f17a5c --- /dev/null +++ b/backend/api.test/Client/RobotTests.cs @@ -0,0 +1,77 @@ +using System; +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.Database.Models; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; +namespace Api.Test +{ + [Collection("Database collection")] + public class RobotTests : IClassFixture> + { + private readonly HttpClient _client; + private readonly JsonSerializerOptions _serializerOptions = + new() + { + Converters = + { + new JsonStringEnumConverter() + }, + PropertyNameCaseInsensitive = true + }; + + public RobotTests(TestWebApplicationFactory factory) + { + _client = factory.CreateClient(new WebApplicationFactoryClientOptions + { + AllowAutoRedirect = false, + BaseAddress = new Uri("https://localhost:8000") + }); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + TestAuthHandler.AuthenticationScheme + ); + } + + [Fact] + public async Task RobotsTest() + { + string url = "/robots"; + var response = await _client.GetAsync(url); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.True(response.IsSuccessStatusCode); + Assert.True(robots != null && robots.Count == 3); + } + + [Fact] + public async Task GetRobotById_ShouldReturnNotFound() + { + string robotId = "RandomString"; + string url = "/robots/" + robotId; + var response = await _client.GetAsync(url); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetRobotById_ShouldReturnRobot() + { + string url = "/robots"; + var response = await _client.GetAsync(url); + var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); + Assert.NotNull(robots); + + string robotId = robots[0].Id; + + var robotResponse = await _client.GetAsync("/robots/" + robotId); + var robot = await robotResponse.Content.ReadFromJsonAsync(_serializerOptions); + Assert.Equal(HttpStatusCode.OK, robotResponse.StatusCode); + Assert.NotNull(robot); + Assert.Equal(robot.Id, robotId); + } + } +} diff --git a/backend/api.test/EndpointTest.cs b/backend/api.test/EndpointTest.cs deleted file mode 100644 index baf507072..000000000 --- a/backend/api.test/EndpointTest.cs +++ /dev/null @@ -1,889 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -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.Models; -using Api.Services; -using Api.Test.Mocks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -namespace Api.Test -{ - [Collection("Database collection")] - public class EndpointTests : IClassFixture>, IDisposable - { - private readonly HttpClient _client; - private readonly JsonSerializerOptions _serializerOptions = - new() - { - Converters = - { - new JsonStringEnumConverter() - }, - PropertyNameCaseInsensitive = true - }; - - public EndpointTests(WebApplicationFactory factory) - { - string projectDir = Directory.GetCurrentDirectory(); - string configPath = Path.Combine(projectDir, "appsettings.Test.json"); - var client = factory - .WithWebHostBuilder( - builder => - { - var configuration = new ConfigurationBuilder() - .AddJsonFile(configPath) - .Build(); - builder.UseEnvironment("Test"); - builder.ConfigureAppConfiguration( - (context, config) => - { - config.AddJsonFile(configPath).AddEnvironmentVariables(); - } - ); - builder.ConfigureTestServices( - services => - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddAuthorization( - options => - { - options.FallbackPolicy = new AuthorizationPolicyBuilder( - TestAuthHandler.AuthenticationScheme - ) - .RequireAuthenticatedUser() - .Build(); - } - ); - services - .AddAuthentication( - options => - { - options.DefaultAuthenticateScheme = - TestAuthHandler.AuthenticationScheme; - options.DefaultChallengeScheme = - TestAuthHandler.AuthenticationScheme; - } - ) - .AddScheme( - TestAuthHandler.AuthenticationScheme, - options => { } - ); - } - ); - } - ) - .CreateClient( - new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false - } - ); - client.BaseAddress = new Uri("https://localhost:8000"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( - TestAuthHandler.AuthenticationScheme - ); - _client = client; - } - - public void Dispose() - { - _client.Dispose(); - GC.SuppressFinalize(this); - } - - private async Task<(string installationId, string plantId, string deckId, string areaId)> PopulateAreaDb(string installationCode, string plantCode, string deckName, string areaName) - { - string installationUrl = "/installations"; - string plantUrl = "/plants"; - string deckUrl = "/decks"; - string areaUrl = "/areas"; - var testPose = new Pose - { - Position = new Position - { - X = 1, - Y = 2, - Z = 2 - }, - Orientation = new Orientation - { - X = 0, - Y = 0, - Z = 0, - W = 1 - } - }; - - var installationQuery = new CreateInstallationQuery - { - InstallationCode = installationCode, - Name = installationCode - }; - - var plantQuery = new CreatePlantQuery - { - InstallationCode = installationCode, - PlantCode = plantCode, - Name = plantCode - }; - - var deckQuery = new CreateDeckQuery - { - InstallationCode = installationCode, - PlantCode = plantCode, - Name = deckName - }; - - var areaQuery = new CreateAreaQuery - { - InstallationCode = installationCode, - PlantCode = plantCode, - DeckName = deckName, - AreaName = areaName, - DefaultLocalizationPose = testPose - }; - - var installationContent = new StringContent( - JsonSerializer.Serialize(installationQuery), - null, - "application/json" - ); - - var plantContent = new StringContent( - JsonSerializer.Serialize(plantQuery), - null, - "application/json" - ); - - var deckContent = new StringContent( - JsonSerializer.Serialize(deckQuery), - null, - "application/json" - ); - - var areaContent = new StringContent( - JsonSerializer.Serialize(areaQuery), - null, - "application/json" - ); - - // Act - var installationResponse = await _client.PostAsync(installationUrl, installationContent); - Assert.NotNull(installationResponse); - Assert.True(installationResponse.IsSuccessStatusCode); - var installation = await installationResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(installation); - - var plantResponse = await _client.PostAsync(plantUrl, plantContent); - Assert.NotNull(plantResponse); - Assert.True(plantResponse.IsSuccessStatusCode); - var plant = await plantResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(plant); - - var deckResponse = await _client.PostAsync(deckUrl, deckContent); - Assert.NotNull(deckResponse); - Assert.True(deckResponse.IsSuccessStatusCode); - var deck = await deckResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(deck); - - var areaResponse = await _client.PostAsync(areaUrl, areaContent); - Assert.NotNull(areaResponse); - Assert.True(areaResponse.IsSuccessStatusCode); - var area = await areaResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(area); - - return (installation.Id, plant.Id, deck.Id, area.Id); - } - - [Fact] - public async Task AreaTest() - { - // Arrange - string testInstallation = "TestInstallationAreaTest"; - string testPlant = "TestPlantAreaTest"; - string testDeck = "testDeckAreaTest"; - string testArea = "testAreaAreaTest"; - string installationUrl = "/installations"; - string plantUrl = "/plants"; - string deckUrl = "/decks"; - string areaUrl = "/areas"; - var testPose = new Pose - { - Position = new Position - { - X = 1, - Y = 2, - Z = 2 - }, - Orientation = new Orientation - { - X = 0, - Y = 0, - Z = 0, - W = 1 - } - }; - - var installationQuery = new CreateInstallationQuery - { - InstallationCode = testInstallation, - Name = testInstallation - }; - - var plantQuery = new CreatePlantQuery - { - InstallationCode = testInstallation, - PlantCode = testPlant, - Name = testPlant - }; - - var deckQuery = new CreateDeckQuery - { - InstallationCode = testInstallation, - PlantCode = testPlant, - Name = testDeck - }; - - var areaQuery = new CreateAreaQuery - { - InstallationCode = testInstallation, - PlantCode = testPlant, - DeckName = testDeck, - AreaName = testArea, - DefaultLocalizationPose = testPose - }; - - var installationContent = new StringContent( - JsonSerializer.Serialize(installationQuery), - null, - "application/json" - ); - - var plantContent = new StringContent( - JsonSerializer.Serialize(plantQuery), - null, - "application/json" - ); - - var deckContent = new StringContent( - JsonSerializer.Serialize(deckQuery), - null, - "application/json" - ); - - var areaContent = new StringContent( - JsonSerializer.Serialize(areaQuery), - null, - "application/json" - ); - - // Act - var installationResponse = await _client.PostAsync(installationUrl, installationContent); - var plantResponse = await _client.PostAsync(plantUrl, plantContent); - var deckResponse = await _client.PostAsync(deckUrl, deckContent); - var areaResponse = await _client.PostAsync(areaUrl, areaContent); - - // Assert - Assert.True(installationResponse.IsSuccessStatusCode); - Assert.True(plantResponse.IsSuccessStatusCode); - Assert.True(deckResponse.IsSuccessStatusCode); - Assert.True(areaResponse.IsSuccessStatusCode); - var area = await areaResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.True(area != null); - } - - [Fact] - public async Task GetMissionsInAreaTest() - { - // Arrange - string testInstallation = "TestInstallationMissionsInAreaTest"; - string testPlant = "TestPlantMissionsInAreaTest"; - string testDeck = "testDeckMissionsInAreaTest"; - string testArea = "testAreaMissionsInAreaTest"; - string testMissionName = "testMissionInAreaTest"; - string areaUrl = "/areas"; - string missionUrl = "/missions/custom"; - (_, _, _, string areaId) = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - string url = "/robots"; - var robotResponse = await _client.GetAsync(url); - Assert.True(robotResponse.IsSuccessStatusCode); - var robots = await robotResponse.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - - var missionQuery = new CustomMissionQuery - { - RobotId = robotId, - DesiredStartTime = DateTime.UtcNow, - InstallationCode = testInstallation, - AreaName = testArea, - Name = testMissionName, - Tasks = new List() - }; - - var missionContent = new StringContent( - JsonSerializer.Serialize(missionQuery), - null, - "application/json" - ); - - // Act - var missionResponse = await _client.PostAsync(missionUrl, missionContent); - - Assert.True(missionResponse.IsSuccessStatusCode); - var mission = await missionResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(mission); - Assert.NotNull(mission.MissionId); - - var areaMissionsResponse = await _client.GetAsync(areaUrl + $"/{areaId}/mission-definitions"); - - // Assert - Assert.True(areaMissionsResponse.IsSuccessStatusCode); - var missions = await areaMissionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.NotNull(missions); - Assert.Single(missions); - Assert.Equal(missions[0].Id, mission.MissionId); - } - - [Fact] - public async Task SafePositionTest() - { - // Arrange - Add Safe Position - string testInstallation = "testInstallationSafePositionTest"; - string testPlant = "testPlantSafePositionTest"; - string testDeck = "testDeckSafePositionTest"; - string testArea = "testAreaSafePositionTest"; - string addSafePositionUrl = $"/areas/{testInstallation}/{testArea}/safe-position"; - var testPosition = new Position - { - X = 1, - Y = 2, - Z = 2 - }; - var query = new Pose - { - Position = testPosition, - Orientation = new Orientation - { - X = 0, - Y = 0, - Z = 0, - W = 1 - } - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - _ = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - var areaResponse = await _client.PostAsync(addSafePositionUrl, content); - Assert.True(areaResponse.IsSuccessStatusCode); - var area = await areaResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.True(area != null); - - // Act - string goToSafePositionUrl = $"/emergency-action/{testInstallation}/abort-current-missions-and-send-all-robots-to-safe-zone"; - var missionResponse = await _client.PostAsync(goToSafePositionUrl, null); - - // Assert - Assert.True(missionResponse.IsSuccessStatusCode); - - } - - [Fact] - public async Task GetMapMetadata() - { - string testInstallation = "testInstallationGetMapMetadata"; - string testPlant = "testPlantGetMapMetadata"; - string testDeck = "testDeckGetMapMetadata"; - string testArea = "testAreaGetMapMetadata"; - string invalidAreaId = "InvalidId"; - - (_, _, _, string areaId) = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - string url = $"/areas/{areaId}/map-metadata"; - var response = await _client.GetAsync(url); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string invalidUrl = $"/areas/{invalidAreaId}/map-metadata"; - var responseInvalid = await _client.GetAsync(invalidUrl); - Assert.Equal(HttpStatusCode.NotFound, responseInvalid.StatusCode); - } - - [Fact] - public async Task UpdateDefaultLocalizationPoseOnDeck() - { - string testInstallation = "testInstallationUpdateDefaultLocalizationPoseOnDeck"; - string testPlant = "testPlantUpdateDefaultLocalizationPoseOnDeck"; - string testDeck = "testDeckUpdateDefaultLocalizationPoseOnDeck"; - string testArea = "testAreaUpdateDefaultLocalizationPoseOnDeck"; - - (_, _, string deckId, _) = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - string url = $"/decks/{deckId}/update-default-localization-pose"; - var query = new Pose - { - Position = new Position - { - X = 1, - Y = 2, - Z = 3 - }, - Orientation = new Orientation - { - X = 0, - Y = 0, - Z = 0, - W = 1 - } - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - var response = await _client.PutAsync(url, content); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - } - - - [Fact] - public async Task GetNextRun() - { - // Arrange - Initialise areas - string customMissionsUrl = "/missions/custom"; - string scheduleMissionsUrl = "/missions/schedule"; - - string testInstallation = "testInstallationNextRun"; - string testPlant = "testPlantNextRun"; - string testDeck = "testDeckNextRun"; - string testArea = "testAreaNextRun"; - string testMissionName = "testMissionNextRun"; - - await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - // Arrange - Create custom mission definition - string robotUrl = "/robots"; - var response = await _client.GetAsync(robotUrl); - Assert.True(response.IsSuccessStatusCode); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - - var query = new CustomMissionQuery - { - RobotId = robotId, - InstallationCode = testInstallation, - AreaName = testArea, - DesiredStartTime = DateTime.SpecifyKind(new DateTime(3050, 1, 1), DateTimeKind.Utc), - InspectionFrequency = new TimeSpan(14, 0, 0, 0), - Name = testMissionName, - Tasks = new List - { - new() - { - RobotPose = new Pose(), - Inspections = new List(), - InspectionTarget = new Position(), - TaskOrder = 0 - } - } - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - response = await _client.PostAsync(customMissionsUrl, content); - - Assert.True(response.IsSuccessStatusCode); - var missionRun = await response.Content.ReadFromJsonAsync(_serializerOptions); - Assert.True(missionRun != null); - Assert.True(missionRun.MissionId != null); - Assert.True(missionRun.Id != null); - Assert.True(missionRun.Status == MissionStatus.Pending); - - // Arrange - Schedule missions from mission definition - var scheduleQuery1 = new ScheduleMissionQuery - { - RobotId = robotId, - DesiredStartTime = DateTime.SpecifyKind(new DateTime(2050, 1, 1), DateTimeKind.Utc), - MissionDefinitionId = missionRun.MissionId - }; - var scheduleContent1 = new StringContent( - JsonSerializer.Serialize(scheduleQuery1), - null, - "application/json" - ); - var scheduleQuery2 = new ScheduleMissionQuery - { - RobotId = robotId, - DesiredStartTime = DateTime.UtcNow, - MissionDefinitionId = missionRun.MissionId - }; - var scheduleContent2 = new StringContent( - JsonSerializer.Serialize(scheduleQuery2), - null, - "application/json" - ); - var scheduleQuery3 = new ScheduleMissionQuery - { - RobotId = robotId, - DesiredStartTime = DateTime.SpecifyKind(new DateTime(2100, 1, 1), DateTimeKind.Utc), - MissionDefinitionId = missionRun.MissionId - }; - var scheduleContent3 = new StringContent( - JsonSerializer.Serialize(scheduleQuery3), - null, - "application/json" - ); - 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); - var missionRun2 = await missionRun2Response.Content.ReadFromJsonAsync(_serializerOptions); - var missionRun3 = await missionRun3Response.Content.ReadFromJsonAsync(_serializerOptions); - - // Act - string nextMissionUrl = $"missions/definitions/{missionRun.MissionId}/next-run"; - var nextMissionResponse = await _client.GetAsync(nextMissionUrl); - - // Assert - Assert.True(nextMissionResponse.IsSuccessStatusCode); - var nextMissionRun = await nextMissionResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(nextMissionRun); - Assert.NotNull(missionRun1); - Assert.NotNull(missionRun2); - Assert.NotNull(missionRun3); - Assert.Equal(missionRun1.MissionId, missionRun.MissionId); - Assert.Equal(missionRun2.MissionId, missionRun.MissionId); - Assert.Equal(missionRun3.MissionId, missionRun.MissionId); - Assert.True(nextMissionRun.Id == missionRun2.Id); - } - - [Fact] - public async Task ScheduleDuplicateCustomMissionDefinitions() - { - // Arrange - Initialise areas - string customMissionsUrl = "/missions/custom"; - string missionDefinitionsUrl = "/missions/definitions"; - - string testInstallation = "testInstallationScheduleDuplicateCustomMissionDefinitions"; - string testPlant = "testPlantScheduleDuplicateCustomMissionDefinitions"; - string testDeck = "testDeckScheduleDuplicateCustomMissionDefinitions"; - string testArea = "testAreaScheduleDuplicateCustomMissionDefinitions"; - string testMissionName = "testMissionScheduleDuplicateCustomMissionDefinitions"; - - _ = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - // Arrange - Create custom mission definition - string robotUrl = "/robots"; - var response = await _client.GetAsync(robotUrl); - Assert.True(response.IsSuccessStatusCode); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - - var query = new CustomMissionQuery - { - RobotId = robotId, - InstallationCode = testInstallation, - AreaName = testArea, - DesiredStartTime = DateTime.SpecifyKind(new DateTime(3050, 1, 1), DateTimeKind.Utc), - InspectionFrequency = new TimeSpan(14, 0, 0, 0), - Name = testMissionName, - Tasks = new List - { - new() - { - RobotPose = new Pose(), - Inspections = new List(), - InspectionTarget = new Position(), - TaskOrder = 0 - }, - new() - { - RobotPose = new Pose(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f), - Inspections = new List(), - InspectionTarget = new Position(), - TaskOrder = 1 - } - } - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - // Act - 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); - var missionRun2 = await response2.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(missionRun1); - Assert.NotNull(missionRun2); - string? missionId1 = missionRun1.MissionId; - string? missionId2 = missionRun2.MissionId; - Assert.Equal(missionId1, missionId2); - - var missionDefinitionsResponse = await _client.GetAsync(missionDefinitionsUrl); - var missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.NotNull(missionDefinitions); - Assert.NotNull(missionDefinitions.Find(m => m.Id == missionId1)); - } - - [Fact] - public async Task ScheduleDuplicateEchoMissionDefinitions() - { - // Arrange - Initialise areas - string echoMissionsUrl = "/missions"; - string missionDefinitionsUrl = "/missions/definitions"; - - string testInstallation = "testInstallationScheduleDuplicateEchoMissionDefinitions"; - string testPlant = "testPlantScheduleDuplicateEchoMissionDefinitions"; - string testDeck = "testDeckScheduleDuplicateEchoMissionDefinitions"; - string testArea = "testAreaScheduleDuplicateEchoMissionDefinitions"; - - _ = await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - // Arrange - Create echo mission definition - string robotUrl = "/robots"; - var response = await _client.GetAsync(robotUrl); - Assert.True(response.IsSuccessStatusCode); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - int echoMissionId = 1; // Corresponds to mock in EchoServiceMock.cs - - var query = new ScheduledMissionQuery - { - RobotId = robotId, - InstallationCode = testInstallation, - AreaName = testArea, - EchoMissionId = echoMissionId, - DesiredStartTime = DateTime.UtcNow - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - // Act - var response1 = await _client.PostAsync(echoMissionsUrl, content); - var response2 = await _client.PostAsync(echoMissionsUrl, content); - - // Assert - Assert.True(response1.IsSuccessStatusCode); - Assert.True(response2.IsSuccessStatusCode); - var missionRun1 = await response1.Content.ReadFromJsonAsync(_serializerOptions); - var missionRun2 = await response2.Content.ReadFromJsonAsync(_serializerOptions); - Assert.NotNull(missionRun1); - Assert.NotNull(missionRun2); - string? missionId1 = missionRun1.MissionId; - string? missionId2 = missionRun2.MissionId; - Assert.Equal(missionId1, missionId2); - - var missionDefinitionsResponse = await _client.GetAsync(missionDefinitionsUrl); - var missionDefinitions = await missionDefinitionsResponse.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.NotNull(missionDefinitions); - Assert.NotNull(missionDefinitions.Find(m => m.Id == missionId1)); - } - - #region MissionsController - - [Fact] - public async Task MissionsTest() - { - // Arrange - string robotUrl = "/robots"; - string missionsUrl = "/missions"; - var robotResponse = await _client.GetAsync(robotUrl); - Assert.True(robotResponse.IsSuccessStatusCode); - var robots = await robotResponse.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - string testInstallation = "TestInstallationMissionsTest"; - string testPlant = "TestPlantMissionsTest"; - string testDeck = "TestDeckMissionsTest"; - string testArea = "testAreaMissionsTest"; - - await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - int echoMissionId = 97; - - // Act - var query = new ScheduledMissionQuery - { - RobotId = robotId, - InstallationCode = testInstallation, - AreaName = testArea, - EchoMissionId = echoMissionId, - DesiredStartTime = DateTime.UtcNow - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - await _client.PostAsync(missionsUrl, content); - await _client.PostAsync(missionsUrl, content); - await _client.PostAsync(missionsUrl, content); - - // 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 missionRuns = await response.Content.ReadFromJsonAsync>( - _serializerOptions - ); - - // Assert - Assert.True(response.IsSuccessStatusCode); - Assert.True(missionRuns != null); - missionRuns = missionRuns.FindAll(m => m.Area!.Name == testArea); - Assert.True(missionRuns.Count == 3); - } - - [Fact] - public async Task GetMissionById_ShouldReturnNotFound() - { - string missionId = "RandomString"; - string url = "/missions/runs/" + missionId; - var response = await _client.GetAsync(url); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } - - [Fact] - public async Task DeleteMission_ShouldReturnNotFound() - { - string missionId = "RandomString"; - string url = "/missions/runs/" + missionId; - var response = await _client.DeleteAsync(url); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } - - [Fact] - public async Task StartMissionTest() - { - // Arrange - string robotUrl = "/robots"; - string missionsUrl = "/missions"; - var response = await _client.GetAsync(robotUrl); - Assert.True(response.IsSuccessStatusCode); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(robots != null); - var robot = robots[0]; - string robotId = robot.Id; - string testInstallation = "TestInstallationStartMissionTest"; - string testPlant = "TestPlantStartMissionTest"; - string testDeck = "TestDeckStartMissionTest"; - string testArea = "testAreaStartMissionTest"; - int echoMissionId = 95; - - await PopulateAreaDb(testInstallation, testPlant, testDeck, testArea); - - // Act - var query = new ScheduledMissionQuery - { - RobotId = robotId, - InstallationCode = testInstallation, - AreaName = testArea, - EchoMissionId = echoMissionId, - DesiredStartTime = DateTime.UtcNow - }; - var content = new StringContent( - JsonSerializer.Serialize(query), - null, - "application/json" - ); - - response = await _client.PostAsync(missionsUrl, content); - - // Assert - Assert.True(response.IsSuccessStatusCode); - var missionRun = await response.Content.ReadFromJsonAsync(_serializerOptions); - Assert.True(missionRun != null); - Assert.True(missionRun.Id != null); - Assert.True(missionRun.Status == MissionStatus.Pending); - } - - #endregion MissionsController - - #region RobotController - - [Fact] - public async Task RobotsTest() - { - string url = "/robots"; - var response = await _client.GetAsync(url); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.True(response.IsSuccessStatusCode); - Assert.True(robots != null && robots.Count == 3); - } - - [Fact] - public async Task GetRobotById_ShouldReturnNotFound() - { - string robotId = "RandomString"; - string url = "/robots/" + robotId; - var response = await _client.GetAsync(url); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } - - [Fact] - public async Task GetRobotById_ShouldReturnRobot() - { - string url = "/robots"; - var response = await _client.GetAsync(url); - var robots = await response.Content.ReadFromJsonAsync>(_serializerOptions); - Assert.NotNull(robots); - - string robotId = robots[0].Id; - - var robotResponse = await _client.GetAsync("/robots/" + robotId); - var robot = await robotResponse.Content.ReadFromJsonAsync(_serializerOptions); - Assert.Equal(HttpStatusCode.OK, robotResponse.StatusCode); - Assert.NotNull(robot); - Assert.Equal(robot.Id, robotId); - } - - #endregion RobotController - - } -} diff --git a/backend/api.test/Mocks/EchoServiceMock.cs b/backend/api.test/Mocks/EchoServiceMock.cs index 8180aaa67..9aa2045d3 100644 --- a/backend/api.test/Mocks/EchoServiceMock.cs +++ b/backend/api.test/Mocks/EchoServiceMock.cs @@ -14,23 +14,13 @@ public class MockEchoService : IEchoService { new EchoPlantInfo { - PlantCode = "testInstallationNextRun", - ProjectDescription = "testInstallationNextRun" + PlantCode = "testInstallation", + ProjectDescription = "testInstallation" }, new EchoPlantInfo { - PlantCode = "TestInstallationMissionsInAreaTest", - ProjectDescription = "TestInstallationMissionsInAreaTest" - }, - new EchoPlantInfo - { - PlantCode = "testInstallationScheduleDuplicateCustomMissionDefinitions", - ProjectDescription = "testInstallationScheduleDuplicateCustomMissionDefinitions" - }, - new EchoPlantInfo - { - PlantCode = "testInstallationScheduleDuplicateEchoMissionDefinitions", - ProjectDescription = "testInstallationScheduleDuplicateEchoMissionDefinitions" + PlantCode = "JSV", + ProjectDescription = "JSVtestInstallation" } }; @@ -39,7 +29,7 @@ public class MockEchoService : IEchoService { Id = 1, Name = "test", - InstallationCode = "JSV", + InstallationCode = "testInstallation", URL = new Uri("https://www.I-am-echo-stid-tag-url.com"), Tags = new List() }; diff --git a/backend/api.test/TestWebApplicationFactory.cs b/backend/api.test/TestWebApplicationFactory.cs new file mode 100644 index 000000000..4ed2464c6 --- /dev/null +++ b/backend/api.test/TestWebApplicationFactory.cs @@ -0,0 +1,66 @@ +using System.IO; +using Api.Services; +using Api.Test.Mocks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +namespace Api.Test +{ + public class TestWebApplicationFactory : WebApplicationFactory where TProgram : class + { + 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(); + builder.UseEnvironment("Test"); + builder.ConfigureAppConfiguration( + (context, config) => + { + config.AddJsonFile(configPath).AddEnvironmentVariables(); + } + ); + builder.ConfigureTestServices( + services => + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddAuthorization( + options => + { + options.FallbackPolicy = new AuthorizationPolicyBuilder( + TestAuthHandler.AuthenticationScheme + ) + .RequireAuthenticatedUser() + .Build(); + } + ); + services + .AddAuthentication( + options => + { + options.DefaultAuthenticateScheme = + TestAuthHandler.AuthenticationScheme; + options.DefaultChallengeScheme = + TestAuthHandler.AuthenticationScheme; + } + ) + .AddScheme( + TestAuthHandler.AuthenticationScheme, + options => { } + ); + } + ); + } + } +} diff --git a/backend/api/Controllers/MissionSchedulingController.cs b/backend/api/Controllers/MissionSchedulingController.cs index 9c2620c9e..d4b5cb06c 100644 --- a/backend/api/Controllers/MissionSchedulingController.cs +++ b/backend/api/Controllers/MissionSchedulingController.cs @@ -13,6 +13,7 @@ namespace Api.Controllers public class MissionSchedulingController : ControllerBase { private readonly IAreaService _areaService; + private readonly IInstallationService _installationService; private readonly ICustomMissionService _customMissionService; private readonly IEchoService _echoService; private readonly ILogger _logger; @@ -28,6 +29,7 @@ public MissionSchedulingController( IMissionDefinitionService missionDefinitionService, IMissionRunService missionRunService, IAreaService areaService, + IInstallationService installationService, IRobotService robotService, IEchoService echoService, ICustomMissionService customMissionService, @@ -41,6 +43,7 @@ ICustomMissionSchedulingService customMissionSchedulingService _missionDefinitionService = missionDefinitionService; _missionRunService = missionRunService; _areaService = areaService; + _installationService = installationService; _robotService = robotService; _echoService = echoService; _customMissionService = customMissionService; @@ -279,12 +282,8 @@ [FromBody] CustomMissionQuery customMissionQuery var robot = await _robotService.ReadById(customMissionQuery.RobotId); if (robot is null) { return NotFound($"Could not find robot with id {customMissionQuery.RobotId}"); } - var installationResults = await _echoService.GetEchoPlantInfos(); - if (installationResults == null) { return NotFound("Unable to retrieve plant information from Echo"); } - - var installationResult = - installationResults.FirstOrDefault(installation => installation.PlantCode.ToUpperInvariant() == customMissionQuery.InstallationCode.ToUpperInvariant()); - if (installationResult == null) { return NotFound($"Could not find installation with id {customMissionQuery.InstallationCode}"); } + var installation = await _installationService.ReadByName(customMissionQuery.InstallationCode); + if (installation == null) { return NotFound($"Could not find installation with name {customMissionQuery.InstallationCode}"); } var missionTasks = customMissionQuery.Tasks.Select(task => new MissionTask(task)).ToList(); diff --git a/backend/api/Controllers/Models/AreaResponse.cs b/backend/api/Controllers/Models/AreaResponse.cs index 498602ff7..8cda25006 100644 --- a/backend/api/Controllers/Models/AreaResponse.cs +++ b/backend/api/Controllers/Models/AreaResponse.cs @@ -1,10 +1,11 @@ -using Api.Database.Models; +using System.Text.Json.Serialization; +using Api.Database.Models; namespace Api.Controllers.Models { public class AreaResponse { - public string Id { get; set; } + public string Id { get; set; } = string.Empty; public string DeckName { get; set; } @@ -22,6 +23,11 @@ public class AreaResponse public IList SafePositions { get; set; } + [JsonConstructor] +#nullable disable + public AreaResponse() { } +#nullable enable + public AreaResponse(Area area) { Id = area.Id; diff --git a/backend/api/Controllers/Models/DeckResponse.cs b/backend/api/Controllers/Models/DeckResponse.cs index ab10c7ce0..7adbbb8c5 100644 --- a/backend/api/Controllers/Models/DeckResponse.cs +++ b/backend/api/Controllers/Models/DeckResponse.cs @@ -1,4 +1,5 @@ -using Api.Database.Models; +using System.Text.Json.Serialization; +using Api.Database.Models; namespace Api.Controllers.Models { @@ -14,6 +15,11 @@ public class DeckResponse public DefaultLocalizationPose? DefaultLocalizationPose { get; set; } + [JsonConstructor] +#nullable disable + public DeckResponse() { } +#nullable enable + public DeckResponse(Deck deck) { Id = deck.Id; diff --git a/backend/api/Controllers/Models/MissionDefinitionResponse.cs b/backend/api/Controllers/Models/MissionDefinitionResponse.cs index b694683bf..320f60d4b 100644 --- a/backend/api/Controllers/Models/MissionDefinitionResponse.cs +++ b/backend/api/Controllers/Models/MissionDefinitionResponse.cs @@ -8,13 +8,13 @@ namespace Api.Controllers.Models public class CondensedMissionDefinitionResponse { [JsonPropertyName("id")] - public string Id { get; set; } + public string Id { get; set; } = string.Empty; [JsonPropertyName("name")] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [JsonPropertyName("installationCode")] - public string InstallationCode { get; set; } + public string InstallationCode { get; set; } = string.Empty; [JsonPropertyName("comment")] public string? Comment { get; set; } @@ -34,6 +34,9 @@ public class CondensedMissionDefinitionResponse [JsonPropertyName("sourceType")] public MissionSourceType SourceType { get; set; } + [JsonConstructor] + public CondensedMissionDefinitionResponse() { } + public CondensedMissionDefinitionResponse(MissionDefinition missionDefinition) { Id = missionDefinition.Id;