diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml new file mode 100644 index 000000000..30127a8a0 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml @@ -0,0 +1,91 @@ +@page "/alerts/{alertId}/delete/check-answers/{handler?}" +@model TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert.CheckAnswersModel +@{ + ViewBag.Title = "Check details and delete alert"; +} + +@section BeforeContent { + Back +} + +
+
+
+ Delete an alert - @Model.PersonName +

@ViewBag.Title

+ + + + Alert type + @Model.AlertTypeName + + + Details + + + + Link + + @if (Model.Link is not null) + { + @($"{Model.Link} (opens in new tab)") + } + else + { + + } + + + + Start date + @Model.StartDate?.ToString("d MMMM yyyy") + + + End date + @Model.EndDate?.ToString("d MMMM yyyy") + + + Reason for deleting alert + + @if (Model.DeleteReasonDetail is not null) + { + + } + else + { + + } + + + Change + + + + Evidence + + @if (Model.UploadedEvidenceFileUrl is not null) + { + @($"{Model.EvidenceFileName} (opens in new tab)") + } + else + { + + } + + + Change + + + + +

Are you sure you want to delete this alert?

+
+ Deleting this alert will permanently remove it from the database +
+
+ Delete alert + Cancel and return to record +
+
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml.cs similarity index 61% rename from TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml.cs index c74a51b65..edf160131 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/CheckAnswers.cshtml.cs @@ -1,15 +1,13 @@ -using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; using TeachingRecordSystem.Core.DataStore.Postgres; using TeachingRecordSystem.Core.Services.Files; -using TeachingRecordSystem.SupportUi.Infrastructure.DataAnnotations; namespace TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; [Journey(JourneyNames.DeleteAlert), RequireJourneyInstance] -public class ConfirmModel( +public class CheckAnswersModel( TrsDbContext dbContext, TrsLinkGenerator linkGenerator, IFileService fileService, @@ -38,49 +36,17 @@ public class ConfirmModel( public DateOnly? EndDate { get; set; } - [BindProperty] - [Display(Name = "Do you want to add additional detail?")] - [Required(ErrorMessage = "Select yes if you want to add additional detail")] - public bool? HasAdditionalDetail { get; set; } + public string? DeleteReasonDetail { get; set; } - [BindProperty] - [Display(Name = "Add additional detail")] - public string? AdditionalDetail { get; set; } + public string? EvidenceFileName { get; set; } - [BindProperty] - [Display(Name = "Upload evidence")] - [Required(ErrorMessage = "Select yes if you want to upload evidence")] - public bool? UploadEvidence { get; set; } + public string? EvidenceFileSizeDescription { get; set; } - [BindProperty] - [EvidenceFile] - [FileSize(MaxFileSizeMb * 1024 * 1024, ErrorMessage = "The selected file must be smaller than 50MB")] - public IFormFile? EvidenceFile { get; set; } + public string? UploadedEvidenceFileUrl { get; set; } public async Task OnPost() { - if (HasAdditionalDetail == true && AdditionalDetail is null) - { - ModelState.AddModelError(nameof(AdditionalDetail), "Add additional detail"); - } - - if (UploadEvidence == true && EvidenceFile is null) - { - ModelState.AddModelError(nameof(EvidenceFile), "Select a file"); - } - - if (!ModelState.IsValid) - { - return this.PageWithErrors(); - } - var now = clock.UtcNow; - Guid? evidenceFileId = null; - if (UploadEvidence == true) - { - using var stream = EvidenceFile!.OpenReadStream(); - evidenceFileId = await fileService.UploadFile(stream, EvidenceFile.ContentType); - } var alert = await dbContext.Alerts .SingleAsync(a => a.AlertId == AlertId); @@ -95,15 +61,15 @@ public async Task OnPost() CreatedUtc = now, RaisedBy = User.GetUserId(), PersonId = PersonId, - Alert = EventModels.Alert.FromModel(alert), - DeletionReasonDetail = HasAdditionalDetail == true ? AdditionalDetail : null, - EvidenceFile = evidenceFileId is Guid fileId ? + Alert = oldAlertEventModel, + DeletionReasonDetail = DeleteReasonDetail, + EvidenceFile = JourneyInstance!.State.EvidenceFileId is Guid fileId ? new EventModels.File() { FileId = fileId, - Name = EvidenceFile!.FileName + Name = JourneyInstance.State.EvidenceFileName! } : - null + null, }; dbContext.AddEvent(deletedEvent); @@ -124,7 +90,7 @@ public async Task OnPostCancel() public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) { - if (JourneyInstance!.State.ConfirmDelete is null) + if (!JourneyInstance!.State.IsComplete) { context.Result = Redirect(linkGenerator.AlertDelete(AlertId, JourneyInstance.InstanceId)); return; @@ -140,6 +106,11 @@ public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingConte Link = alertInfo.Alert.ExternalLink; StartDate = alertInfo.Alert.StartDate; EndDate = alertInfo.Alert.EndDate; + DeleteReasonDetail = JourneyInstance.State.DeleteReasonDetail; + EvidenceFileName = JourneyInstance.State.EvidenceFileName; + UploadedEvidenceFileUrl = JourneyInstance!.State.EvidenceFileId is not null ? + await fileService.GetFileUrl(JourneyInstance!.State.EvidenceFileId!.Value, _fileUrlExpiresAfter) : + null; await next(); } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml deleted file mode 100644 index 23d3bcec6..000000000 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Confirm.cshtml +++ /dev/null @@ -1,89 +0,0 @@ -@page "/alerts/{alertId}/delete/confirm/{handler?}" -@model TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert.ConfirmModel -@{ - ViewBag.Title = "Delete this alert"; -} - -@section BeforeContent { - Back -} - -
-
-
- Delete an alert - @Model.PersonName -

@ViewBag.Title

- - - - Alert type - @Model.AlertTypeName - - - Details - - - - Link - - @if (Model.Link is not null) - { - @($"{Model.Link} (opens in new tab)") - } - else - { - - } - - - - Start date - @Model.StartDate?.ToString("d MMMM yyyy") - - - End date - @Model.EndDate?.ToString("d MMMM yyyy") - - - -
- - - - - Yes - - - - - - No - - - - - - - - - Yes - - - Upload a file - - - - - No - - - - -
- Delete this alert - Cancel and return to record -
-
-
-
-
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/DeleteAlertState.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/DeleteAlertState.cs index fd7cd5903..426c51edc 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/DeleteAlertState.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/DeleteAlertState.cs @@ -1,3 +1,6 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + namespace TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; public class DeleteAlertState : IRegisterJourney @@ -8,5 +11,24 @@ public class DeleteAlertState : IRegisterJourney requestDataKeys: ["alertId"], appendUniqueKey: true); - public bool? ConfirmDelete { get; set; } + public bool? HasAdditionalReasonDetail { get; set; } + + public string? DeleteReasonDetail { get; set; } + + public bool? UploadEvidence { get; set; } + + public Guid? EvidenceFileId { get; set; } + + public string? EvidenceFileName { get; set; } + + public string? EvidenceFileSizeDescription { get; set; } + + [JsonIgnore] + [MemberNotNullWhen(true, nameof(HasAdditionalReasonDetail), nameof(UploadEvidence), nameof(EvidenceFileId))] + public bool IsComplete => + HasAdditionalReasonDetail.HasValue && + (!HasAdditionalReasonDetail.Value || (HasAdditionalReasonDetail.Value && !string.IsNullOrWhiteSpace(DeleteReasonDetail))) && + UploadEvidence.HasValue && + (!UploadEvidence.Value || (UploadEvidence.Value && EvidenceFileId.HasValue)); + } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml index 489980255..3f095e417 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml @@ -1,57 +1,55 @@ @page "/alerts/{alertId}/delete/{handler?}" @model TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert.IndexModel @{ - ViewBag.Title = "Delete this alert"; + ViewBag.Title = $"Delete {Model.AlertTypeName} alert"; } @section BeforeContent { - Back + Back }
-
-
+
+ Delete an alert - @Model.PersonName

@ViewBag.Title

- - - Alert type - @Model.AlertTypeName - - - Details - - - - Link - - @if (Model.Link is not null) - { - @($"{Model.Link} (opens in new tab)") - } - else - { - - } - - - - Start date - @Model.StartDate?.ToString("d MMMM yyyy") - - - End date - @Model.EndDate?.ToString("d MMMM yyyy") - - + + + + + Yes + + + + + + No + + + - + - If you delete this alert it will be permanently removed from the database - Yes, I want to delete this alert - No, I want to keep this alert + + Yes + + @if (Model.EvidenceFileId is not null) + { + Currently uploaded file +

+ @($"{Model.EvidenceFileName} ({Model.EvidenceFileSizeDescription})") +

+ } + + Upload a file + +
+
+ + No +
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml.cs index 9210fe958..d34a5ce8c 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/DeleteAlert/Index.cshtml.cs @@ -1,13 +1,19 @@ using System.ComponentModel.DataAnnotations; +using Humanizer; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.Core.Services.Files; +using TeachingRecordSystem.SupportUi.Infrastructure.DataAnnotations; namespace TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; [Journey(JourneyNames.DeleteAlert), ActivatesJourney, RequireJourneyInstance] -public class IndexModel(TrsLinkGenerator linkGenerator) : PageModel +public class IndexModel(TrsLinkGenerator linkGenerator, IFileService fileService) : PageModel { + public const int MaxFileSizeMb = 50; + public const int DeleteReasonDetailMaxLength = 4000; + private static readonly TimeSpan _fileUrlExpiresAfter = TimeSpan.FromMinutes(15); public JourneyInstance? JourneyInstance { get; set; } @@ -15,49 +21,109 @@ public class IndexModel(TrsLinkGenerator linkGenerator) : PageModel [FromRoute] public Guid AlertId { get; set; } + [FromQuery] + public bool FromCheckAnswers { get; set; } + public Guid PersonId { get; set; } public string? PersonName { get; set; } public string? AlertTypeName { get; set; } - public string? Details { get; set; } + public DateOnly? EndDate { get; set; } - public string? Link { get; set; } + [BindProperty] + [Display(Name = "Do you want to add why you are deleting this alert?")] + [Required(ErrorMessage = "Select yes if you want to add why you are deleting this alert")] + public bool? HasAdditionalReasonDetail { get; set; } - public DateOnly? StartDate { get; set; } + [BindProperty] + [Display(Name = "Add additional detail")] + public string? DeleteReasonDetail { get; set; } - public DateOnly? EndDate { get; set; } + [BindProperty] + [Display(Name = "Do you want to upload evidence?")] + [Required(ErrorMessage = "Select yes if you want to upload evidence")] + public bool? UploadEvidence { get; set; } [BindProperty] - [Display(Name = "Are you sure you want to delete this alert?")] - [Required(ErrorMessage = "Confirm you want to delete this alert")] - public bool? ConfirmDelete { get; set; } + [EvidenceFile] + [FileSize(MaxFileSizeMb * 1024 * 1024, ErrorMessage = "The selected file must be smaller than 50MB")] + public IFormFile? EvidenceFile { get; set; } + + public Guid? EvidenceFileId { get; set; } + + public string? EvidenceFileName { get; set; } + + public string? EvidenceFileSizeDescription { get; set; } - public void OnGet() + public string? UploadedEvidenceFileUrl { get; set; } + + public async Task OnGet() { - ConfirmDelete = JourneyInstance!.State.ConfirmDelete; + HasAdditionalReasonDetail = JourneyInstance!.State.HasAdditionalReasonDetail; + DeleteReasonDetail = JourneyInstance!.State.DeleteReasonDetail; + UploadEvidence = JourneyInstance!.State.UploadEvidence; + UploadedEvidenceFileUrl = JourneyInstance?.State.EvidenceFileId is not null ? + await fileService.GetFileUrl(JourneyInstance.State.EvidenceFileId.Value, _fileUrlExpiresAfter) : + null; } public async Task OnPost() { + if (HasAdditionalReasonDetail == true && DeleteReasonDetail is null) + { + ModelState.AddModelError(nameof(DeleteReasonDetail), "Enter additional detail"); + } + + if (UploadEvidence == true && EvidenceFileId is null && EvidenceFile is null) + { + ModelState.AddModelError(nameof(EvidenceFile), "Select a file"); + } + if (!ModelState.IsValid) { return this.PageWithErrors(); } - if (ConfirmDelete == false) + if (UploadEvidence == true) { - await JourneyInstance!.DeleteAsync(); - return Redirect(EndDate is null ? linkGenerator.PersonAlerts(PersonId) : linkGenerator.AlertDetail(AlertId)); + if (EvidenceFile is not null) + { + if (EvidenceFileId is not null) + { + await fileService.DeleteFile(EvidenceFileId.Value); + } + + using var stream = EvidenceFile.OpenReadStream(); + var evidenceFileId = await fileService.UploadFile(stream, EvidenceFile.ContentType); + await JourneyInstance!.UpdateStateAsync(state => + { + state.EvidenceFileId = evidenceFileId; + state.EvidenceFileName = EvidenceFile.FileName; + state.EvidenceFileSizeDescription = EvidenceFile.Length.Bytes().Humanize(); + }); + } + } + else if (EvidenceFileId is not null) + { + await fileService.DeleteFile(EvidenceFileId.Value); + await JourneyInstance!.UpdateStateAsync(state => + { + state.EvidenceFileId = null; + state.EvidenceFileName = null; + state.EvidenceFileSizeDescription = null; + }); } await JourneyInstance!.UpdateStateAsync(state => { - state.ConfirmDelete = ConfirmDelete; + state.HasAdditionalReasonDetail = HasAdditionalReasonDetail; + state.DeleteReasonDetail = DeleteReasonDetail; + state.UploadEvidence = UploadEvidence; }); - return Redirect(linkGenerator.AlertDeleteConfirm(AlertId, JourneyInstance!.InstanceId)); + return Redirect(linkGenerator.AlertDeleteCheckAnswers(AlertId, JourneyInstance!.InstanceId)); } public async Task OnPostCancel() @@ -66,7 +132,7 @@ public async Task OnPostCancel() return Redirect(EndDate is null ? linkGenerator.PersonAlerts(PersonId) : linkGenerator.AlertDetail(AlertId)); } - public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) { var personInfo = context.HttpContext.GetCurrentPersonFeature(); var alertInfo = context.HttpContext.GetCurrentAlertFeature(); @@ -74,11 +140,9 @@ public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingConte PersonId = personInfo.PersonId; PersonName = personInfo.Name; AlertTypeName = alertInfo.Alert.AlertType.Name; - Details = alertInfo.Alert.Details; - Link = alertInfo.Alert.ExternalLink; - StartDate = alertInfo.Alert.StartDate; EndDate = alertInfo.Alert.EndDate; - - await next(); + EvidenceFileId = JourneyInstance!.State.EvidenceFileId; + EvidenceFileName = JourneyInstance!.State.EvidenceFileName; + EvidenceFileSizeDescription = JourneyInstance!.State.EvidenceFileSizeDescription; } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs index c613eaf2d..65a86ea5f 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs @@ -157,17 +157,17 @@ public string AlertReopenCheckAnswers(Guid alertId, JourneyInstanceId journeyIns public string AlertReopenCheckAnswersCancel(Guid alertId, JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/Alerts/ReopenAlert/CheckAnswers", "cancel", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); - public string AlertDelete(Guid alertId, JourneyInstanceId? journeyInstanceId) => - GetRequiredPathByPage("/Alerts/DeleteAlert/Index", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); + public string AlertDelete(Guid alertId, JourneyInstanceId? journeyInstanceId, bool? fromCheckAnswers = null) => + GetRequiredPathByPage("/Alerts/DeleteAlert/Index", routeValues: new { alertId, fromCheckAnswers }, journeyInstanceId: journeyInstanceId); public string AlertDeleteCancel(Guid alertId, JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/Alerts/DeleteAlert/Index", "cancel", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); - public string AlertDeleteConfirm(Guid alertId, JourneyInstanceId journeyInstanceId) => - GetRequiredPathByPage("/Alerts/DeleteAlert/Confirm", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); + public string AlertDeleteCheckAnswers(Guid alertId, JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/Alerts/DeleteAlert/CheckAnswers", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); - public string AlertDeleteConfirmCancel(Guid alertId, JourneyInstanceId journeyInstanceId) => - GetRequiredPathByPage("/Alerts/DeleteAlert/Confirm", "cancel", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); + public string AlertDeleteCheckAnswersCancel(Guid alertId, JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/Alerts/DeleteAlert/CheckAnswers", "cancel", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); public string EditChangeRequest(string ticketNumber) => GetRequiredPathByPage("/ChangeRequests/EditChangeRequest/Index", routeValues: new { ticketNumber }); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/AlertTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/AlertTests.cs index 663b58f6f..463822bb9 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/AlertTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/AlertTests.cs @@ -407,7 +407,7 @@ public async Task DeleteAlert() var person = await TestData.CreatePerson(b => b.WithAlert(a => a.WithStartDate(startDate).WithEndDate(endDate))); var personId = person.PersonId; var alertId = person.Alerts.First().AlertId; - var deleteReason = TestData.GenerateLoremIpsum(); + var deleteReasonDetails = TestData.GenerateLoremIpsum(); var evidenceFileName = "evidence.jpg"; var evidenceFileMimeType = "image/jpeg"; @@ -417,18 +417,10 @@ public async Task DeleteAlert() await page.GoToDeleteAlertPage(alertId); await page.AssertOnDeleteAlertPage(alertId); - await page.CheckAsync("label:text-is('Yes, I want to delete this alert')"); - - await page.ClickContinueButton(); - - await page.AssertOnDeleteAlertConfirmPage(alertId); - - await page.CheckAsync(":nth-match(label:text-is('Yes'), 1)"); - - await page.FillAsync("label:text-is('Add additional detail')", deleteReason); - - await page.CheckAsync(":nth-match(label:text-is('Yes'), 2)"); + await page.Locator("div.govuk-form-group:has-text('Do you want to add why you are deleting this alert?')").Locator("label:text-is('Yes')").CheckAsync(); + await page.FillAsync("label:text-is('Add additional detail')", deleteReasonDetails); + await page.Locator("div.govuk-form-group:has-text('Do you want to upload evidence?')").Locator("label:text-is('Yes')").CheckAsync(); await page .GetByLabel("Upload a file") .SetInputFilesAsync( @@ -439,7 +431,11 @@ await page Buffer = TestData.JpegImage }); - await page.ClickButton("Delete this alert"); + await page.ClickContinueButton(); + + await page.AssertOnDeleteAlertCheckAnswersPage(alertId); + + await page.ClickButton("Delete alert"); await page.AssertOnPersonAlertsPage(personId); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/PageExtensions.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/PageExtensions.cs index 30cc43546..dc67d2518 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/PageExtensions.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.EndToEndTests/PageExtensions.cs @@ -287,9 +287,9 @@ public static async Task AssertOnDeleteAlertPage(this IPage page, Guid alertId) await page.WaitForUrlPathAsync($"/alerts/{alertId}/delete"); } - public static async Task AssertOnDeleteAlertConfirmPage(this IPage page, Guid alertId) + public static async Task AssertOnDeleteAlertCheckAnswersPage(this IPage page, Guid alertId) { - await page.WaitForUrlPathAsync($"/alerts/{alertId}/delete/confirm"); + await page.WaitForUrlPathAsync($"/alerts/{alertId}/delete/check-answers"); } public static async Task AssertOnPersonEditNamePage(this IPage page, Guid personId) diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/CheckAnswersTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/CheckAnswersTests.cs new file mode 100644 index 000000000..54523a4b0 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/CheckAnswersTests.cs @@ -0,0 +1,239 @@ +namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Alerts.DeleteAlert; + +public class CheckAnswersTests : DeleteAlertTestBase +{ + private const string PreviousStep = JourneySteps.Index; + private const string ThisStep = JourneySteps.CheckAnswers; + + public CheckAnswersTests(HostFixture hostFixture) : base(hostFixture) + { + SetCurrentUser(TestUsers.GetUser(UserRoles.AlertsReadWrite, UserRoles.DbsAlertsReadWrite)); + } + + [Fact] + public async Task Get_UserDoesNotHavePermission_ReturnsForbidden() + { + // Arrange + SetCurrentUser(TestUsers.GetUser(roles: [])); + + var (person, alert) = await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); + } + + [Fact] + public async Task Get_AlertDoesNotExist_ReturnsNotFound() + { + // Arrange + var alertId = Guid.NewGuid(); + var journeyInstance = await CreateEmptyJourneyInstance(alertId); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Get_MissingDataInJourneyState_RedirectsToIndexPage(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateEmptyJourneyInstance(alert.AlertId); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.StartsWith($"/alerts/{alert.AlertId}/delete", response.Headers.Location?.OriginalString); + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task Get_WithValidJourneyState_ReturnsOk(bool isOpenAlert, bool populateOptional) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert(populateOptional) : await CreatePersonWithClosedAlert(populateOptional); + var journeyInstance = await CreateJourneyInstanceForAllStepsCompleted(alert, populateOptional); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + var doc = await AssertEx.HtmlResponse(response); + Assert.Equal(alert.AlertType.Name, doc.GetSummaryListValueForKey("Alert type")); + Assert.Equal(alert.Details, doc.GetSummaryListValueForKey("Details")); + Assert.Equal(populateOptional ? $"{alert.ExternalLink} (opens in new tab)" : "-", doc.GetSummaryListValueForKey("Link")); + Assert.Equal(alert.StartDate!.Value.ToString("d MMMM yyyy"), doc.GetSummaryListValueForKey("Start date")); + Assert.Equal(isOpenAlert ? "-" : alert.EndDate!.Value.ToString("d MMMM yyyy"), doc.GetSummaryListValueForKey("End date")); + Assert.Equal(populateOptional ? journeyInstance.State.DeleteReasonDetail : "-", doc.GetSummaryListValueForKey("Reason for deleting alert")); + Assert.Equal(populateOptional ? $"{journeyInstance.State.EvidenceFileName} (opens in new tab)" : "-", doc.GetSummaryListValueForKey("Evidence")); + } + + [Theory] + [RolesWithoutAlertWritePermissionData] + public async Task Post_UserDoesNotHavePermission_ReturnsForbidden(string? role) + { + // Arrange + SetCurrentUser(TestUsers.GetUser(role)); + + var (person, alert) = await CreatePersonWithOpenAlert(); + var journeyInstance = await CreateJourneyInstanceForAllStepsCompleted(alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); + } + + [Fact] + public async Task Post_AlertDoesNotExist_ReturnsNotFound() + { + // Arrange + var alertId = Guid.NewGuid(); + var journeyInstance = await CreateEmptyJourneyInstance(alertId); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GPost_MissingDataInJourneyState_RedirectsToIndexPage(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateEmptyJourneyInstance(alert.AlertId); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.StartsWith($"/alerts/{alert.AlertId}/delete", response.Headers.Location?.OriginalString); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_Confirm_DeletesAlertCreatesEventCompletesJourneyAndRedirectsWithFlashMessage(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForAllStepsCompleted(alert); + + EventPublisher.Clear(); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + + var redirectResponse = await response.FollowRedirect(HttpClient); + var redirectDoc = await redirectResponse.GetDocument(); + AssertEx.HtmlDocumentHasFlashSuccess(redirectDoc, "Alert deleted"); + + await WithDbContext(async dbContext => + { + var alertExists = await dbContext.Alerts.AnyAsync(a => a.AlertId == alert.AlertId); + Assert.False(alertExists); + }); + + EventPublisher.AssertEventsSaved(e => + { + var actualAlertDeletedEvent = Assert.IsType(e); + + var expectedAlertDeletedEvent = new AlertDeletedEvent() + { + EventId = actualAlertDeletedEvent.EventId, + CreatedUtc = Clock.UtcNow, + RaisedBy = GetCurrentUserId(), + PersonId = person.PersonId, + Alert = new() + { + AlertId = alert.AlertId, + AlertTypeId = alert.AlertTypeId, + Details = alert.Details, + ExternalLink = alert.ExternalLink, + StartDate = alert.StartDate, + EndDate = alert.EndDate, + }, + DeletionReasonDetail = journeyInstance.State.DeleteReasonDetail, + EvidenceFile = new() + { + FileId = journeyInstance.State.EvidenceFileId!.Value, + Name = journeyInstance.State.EvidenceFileName! + } + }; + + + Assert.Equivalent(expectedAlertDeletedEvent, actualAlertDeletedEvent); + }); + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + Assert.True(journeyInstance.Completed); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_Cancel_DeletesJourneyAndRedirects(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/check-answers/cancel?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + if (isOpenAlert) + { + Assert.Equal($"/persons/{person.PersonId}/alerts", response.Headers.Location!.OriginalString); + } + else + { + Assert.Equal($"/alerts/{alert.AlertId}", response.Headers.Location!.OriginalString); + } + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + Assert.Null(journeyInstance); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/ConfirmTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/ConfirmTests.cs deleted file mode 100644 index bf72afecd..000000000 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/ConfirmTests.cs +++ /dev/null @@ -1,413 +0,0 @@ -using TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; - -namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Alerts.DeleteAlert; - -public class ConfirmTests : TestBase -{ - public ConfirmTests(HostFixture hostFixture) : base(hostFixture) - { - SetCurrentUser(TestUsers.GetUser(UserRoles.AlertsReadWrite, UserRoles.DbsAlertsReadWrite)); - } - - [Fact] - public async Task Get_UserDoesNotHavePermission_ReturnsForbidden() - { - // Arrange - SetCurrentUser(TestUsers.GetUser(roles: [])); - - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alert = person.Alerts.Single(); - - var journeyInstance = await CreateJourneyInstance( - alert.AlertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); - } - - [Fact] - public async Task Get_WithAlertIdForNonExistentAlert_ReturnsNotFound() - { - // Arrange - var person = await TestData.CreatePerson(); - var alertId = Guid.NewGuid(); - var journeyInstance = await CreateJourneyInstance(alertId); - - var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); - } - - [Fact] - public async Task Get_MissingDataInJourneyState_RedirectsToIndexPage() - { - // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance(alertId); - - var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.StartsWith($"/alerts/{alertId}/delete", response.Headers.Location?.OriginalString); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task Get_WithValidJourneyState_ReturnsOk(bool populateOptional) - { - // Arrange - var alertType = await TestData.ReferenceDataCache.GetAlertTypeById(Guid.Parse("ed0cd700-3fb2-4db0-9403-ba57126090ed")); // Prohibition by the Secretary of State - misconduct - var startDate = TestData.Clock.Today.AddDays(-50); - var details = "Some details"; - var link = populateOptional ? TestData.GenerateUrl() : null; - var endDate = populateOptional ? TestData.Clock.Today.AddDays(-5) : (DateOnly?)null; - var person = await TestData.CreatePerson( - b => b.WithAlert( - a => a.WithAlertTypeId(alertType.AlertTypeId) - .WithDetails(details) - .WithExternalLink(link) - .WithStartDate(startDate) - .WithEndDate(endDate))); - var alert = person.Alerts.Single(); - var journeyInstance = await CreateJourneyInstance( - alert.AlertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - var doc = await AssertEx.HtmlResponse(response); - Assert.Equal(alertType.Name, doc.GetElementByTestId("alert-type")!.TextContent); - Assert.Equal(details, doc.GetElementByTestId("details")!.TextContent); - Assert.Equal(populateOptional ? $"{link} (opens in new tab)" : "-", doc.GetElementByTestId("link")!.TextContent); - Assert.Equal(startDate.ToString("d MMMM yyyy"), doc.GetElementByTestId("start-date")!.TextContent); - Assert.Equal(populateOptional ? endDate?.ToString("d MMMM yyyy") : "-", doc.GetElementByTestId("end-date")!.TextContent); - } - - [Fact] - public async Task Post_UserDoesNotHavePermission_ReturnsForbidden() - { - // Arrange - SetCurrentUser(TestUsers.GetUser(roles: [])); - - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alert = person.Alerts.Single(); - - var journeyInstance = await CreateJourneyInstance( - alert.AlertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); - } - - [Fact] - public async Task Post_WithAlertIdForNonExistentAlert_ReturnsNotFound() - { - // Arrange - var person = await TestData.CreatePerson(); - var alertId = Guid.NewGuid(); - var journeyInstance = await CreateJourneyInstance(alertId); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); - } - - [Fact] - public async Task Post_MissingDataInJourneyState_RedirectsToIndexPage() - { - // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance(alertId); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}"); - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.StartsWith($"/alerts/{alertId}/delete", response.Headers.Location?.OriginalString); - } - - [Fact] - public async Task Post_WhenNoAdditionalDetailOptionIsSelected_ReturnsError() - { - // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = new FormUrlEncodedContent(new Dictionary - { - ["UploadEvidence"] = "False" - }) - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - await AssertEx.HtmlResponseHasError(response, "HasAdditionalDetail", "Select yes if you want to add additional detail"); - } - - [Fact] - public async Task Post_WhenAdditionalDetailOptionIsYesAndNoAdditionalDetailIsEntered_ReturnsError() - { - // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = new FormUrlEncodedContent(new Dictionary - { - ["HasAdditionalDetail"] = "True", - ["UploadEvidence"] = "False" - }) - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - await AssertEx.HtmlResponseHasError(response, "AdditionalDetail", "Add additional detail"); - } - - [Fact] - public async Task Post_WhenNoUploadEvidenceOptionIsSelected_ReturnsError() - { - // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = new FormUrlEncodedContent(new Dictionary - { - ["HasAdditonalDetail"] = "False" - }) - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - await AssertEx.HtmlResponseHasError(response, "UploadEvidence", "Select yes if you want to upload evidence"); - } - - [Fact] - public async Task Post_WhenUploadEvidenceOptionIsYesAndNoFileIsSelected_ReturnsError() - { - // Arrange - var startDate = TestData.Clock.Today.AddDays(-50); - var person = await TestData.CreatePerson(b => b.WithAlert(q => q.WithStartDate(startDate))); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = new FormUrlEncodedContent(new Dictionary - { - ["HasAdditonalDetail"] = "False", - ["UploadEvidence"] = "True" - }) - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - await AssertEx.HtmlResponseHasError(response, "EvidenceFile", "Select a file"); - } - - [Fact] - public async Task Post_WhenEvidenceFileIsInvalidType_ReturnsError() - { - // Arrange - var startDate = TestData.Clock.Today.AddDays(-50); - var person = await TestData.CreatePerson(b => b.WithAlert(q => q.WithStartDate(startDate))); - var alertId = person.Alerts.Single().AlertId; - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var multipartContent = CreateFormFileUpload(".cs"); - multipartContent.Add(new StringContent("False"), "HasAdditionalDetail"); - multipartContent.Add(new StringContent("True"), "UploadEvidence"); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = multipartContent - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - await AssertEx.HtmlResponseHasError(response, "EvidenceFile", "The selected file must be a BMP, CSV, DOC, DOCX, EML, JPEG, JPG, MBOX, MSG, ODS, ODT, PDF, PNG, TIF, TXT, XLS or XLSX"); - } - - [Fact] - public async Task Post_WhenValidInput_SoftDeletesAlertCreatesCreatesEventCompletesJourneyAndRedirectsWithFlashMessage() - { - // Arrange - var startDate = TestData.Clock.Today.AddDays(-50); - var additionalDetail = "My additional detail"; - var person = await TestData.CreatePerson(b => b.WithAlert(q => q.WithStartDate(startDate))); - var originalAlert = person.Alerts.Single(); - var alertId = originalAlert.AlertId; - - EventPublisher.Clear(); - - var journeyInstance = await CreateJourneyInstance( - alertId, - new DeleteAlertState - { - ConfirmDelete = true - }); - - var multipartContent = CreateFormFileUpload(".pdf"); - multipartContent.Add(new StringContent("True"), "HasAdditionalDetail"); - multipartContent.Add(new StringContent(additionalDetail), "AdditionalDetail"); - multipartContent.Add(new StringContent("True"), "UploadEvidence"); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete/confirm?{journeyInstance.GetUniqueIdQueryParameter()}") - { - Content = multipartContent - }; - - // Act - var response = await HttpClient.SendAsync(request); - - // Assert - Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - - var redirectResponse = await response.FollowRedirect(HttpClient); - var redirectDoc = await redirectResponse.GetDocument(); - AssertEx.HtmlDocumentHasFlashSuccess(redirectDoc, "Alert deleted"); - - await WithDbContext(async dbContext => - { - var alertExists = await dbContext.Alerts.AnyAsync(a => a.AlertId == alertId); - Assert.False(alertExists); - }); - - EventPublisher.AssertEventsSaved(e => - { - var expectedAlertDeletedEvent = new AlertDeletedEvent() - { - EventId = Guid.Empty, - CreatedUtc = Clock.UtcNow, - RaisedBy = GetCurrentUserId(), - PersonId = person.PersonId, - DeletionReasonDetail = additionalDetail, - Alert = new() - { - AlertId = Guid.Empty, - AlertTypeId = originalAlert.AlertTypeId, - Details = originalAlert.Details, - ExternalLink = originalAlert.ExternalLink, - StartDate = startDate, - EndDate = null - }, - EvidenceFile = new() - { - FileId = Guid.Empty, - Name = "evidence.pdf" - } - }; - - var actualAlertDeletedEvent = Assert.IsType(e); - Assert.Equivalent(expectedAlertDeletedEvent with { EventId = actualAlertDeletedEvent.EventId }, actualAlertDeletedEvent); - }); - - journeyInstance = await ReloadJourneyInstance(journeyInstance); - Assert.True(journeyInstance.Completed); - } - - private MultipartFormDataContent CreateFormFileUpload(string fileExtension) - { - var byteArrayContent = new ByteArrayContent(new byte[] { }); - byteArrayContent.Headers.Add("Content-Type", "application/octet-stream"); - - var multipartContent = new MultipartFormDataContent - { - { byteArrayContent, "EvidenceFile", $"evidence{fileExtension}" } - }; - - return multipartContent; - } - - private async Task> CreateJourneyInstance(Guid alertId, DeleteAlertState? state = null) => - await CreateJourneyInstance( - JourneyNames.DeleteAlert, - state ?? new DeleteAlertState(), - new KeyValuePair("alertId", alertId)); -} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/DeleteAlertTestBase.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/DeleteAlertTestBase.cs new file mode 100644 index 000000000..f806a570c --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/DeleteAlertTestBase.cs @@ -0,0 +1,44 @@ +using TeachingRecordSystem.Core.DataStore.Postgres.Models; +using TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; + +namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Alerts.DeleteAlert; + +public class DeleteAlertTestBase(HostFixture hostFixture) : TestBase(hostFixture) +{ + protected Task> CreateEmptyJourneyInstance(Guid alertId) => + CreateJourneyInstance(alertId, new()); + + protected Task> CreateJourneyInstanceForAllStepsCompleted(Alert alert, bool populateOptional = true) => + CreateJourneyInstance(alert.AlertId, new DeleteAlertState() + { + HasAdditionalReasonDetail = populateOptional ? true : false, + DeleteReasonDetail = populateOptional ? "More details" : null, + UploadEvidence = populateOptional ? true : false, + EvidenceFileId = populateOptional ? Guid.NewGuid() : null, + EvidenceFileName = populateOptional ? "evidence.jpeg" : null, + EvidenceFileSizeDescription = populateOptional ? "5MB" : null + }); + + protected Task> CreateJourneyInstanceForCompletedStep(string step, Alert alert) => + step switch + { + JourneySteps.New => + CreateEmptyJourneyInstance(alert.AlertId), + JourneySteps.Index or JourneySteps.CheckAnswers => + CreateJourneyInstanceForAllStepsCompleted(alert, populateOptional: true), + _ => throw new ArgumentException($"Unknown {nameof(step)}: '{step}'.", nameof(step)) + }; + + private Task> CreateJourneyInstance(Guid alertId, DeleteAlertState state) => + CreateJourneyInstance( + JourneyNames.DeleteAlert, + state, + new KeyValuePair("alertId", alertId)); + + public static class JourneySteps + { + public const string New = nameof(New); + public const string Index = nameof(Index); + public const string CheckAnswers = nameof(CheckAnswers); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/IndexTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/IndexTests.cs index 4424c8d8c..8ca40962d 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/IndexTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/DeleteAlert/IndexTests.cs @@ -1,24 +1,24 @@ -using TeachingRecordSystem.SupportUi.Pages.Alerts.DeleteAlert; - namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Alerts.DeleteAlert; -public class IndexTests : TestBase +public class IndexTests : DeleteAlertTestBase { + private const string PreviousStep = JourneySteps.New; + private const string ThisStep = JourneySteps.Index; + public IndexTests(HostFixture hostFixture) : base(hostFixture) { SetCurrentUser(TestUsers.GetUser(UserRoles.AlertsReadWrite, UserRoles.DbsAlertsReadWrite)); } - [Fact] - public async Task Get_UserDoesNotHavePermission_ReturnsForbidden() + [Theory] + [RolesWithoutAlertWritePermissionData] + public async Task Get_UserDoesNotHavePermission_ReturnsForbidden(string? role) { // Arrange - SetCurrentUser(TestUsers.GetUser(roles: [])); + SetCurrentUser(TestUsers.GetUser(role)); - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alert = person.Alerts.Single(); - - var journeyInstance = await CreateJourneyInstance(alert.AlertId); + var (person, alert) = await CreatePersonWithOpenAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -30,12 +30,11 @@ public async Task Get_UserDoesNotHavePermission_ReturnsForbidden() } [Fact] - public async Task Get_WithAlertIdForNonExistentAlert_ReturnsNotFound() + public async Task Get_AlertDoesNotExist_ReturnsNotFound() { // Arrange - var person = await TestData.CreatePerson(); var alertId = Guid.NewGuid(); - var journeyInstance = await CreateJourneyInstance(alertId); + var journeyInstance = await CreateEmptyJourneyInstance(alertId); var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -46,18 +45,14 @@ public async Task Get_WithAlertIdForNonExistentAlert_ReturnsNotFound() Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); } - [Fact] - public async Task Get_ValidRequestWithPopulatedDataInJourneyState_PopulatesModelFromJourneyState() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Get_ValidRequest_ReturnsOk(bool isOpenAlert) { // Arrange - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alert = person.Alerts.Single(); - var journeyInstance = await CreateJourneyInstance( - alert.AlertId, - new DeleteAlertState - { - ConfirmDelete = true - }); + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -65,37 +60,18 @@ public async Task Get_ValidRequestWithPopulatedDataInJourneyState_PopulatesModel var response = await HttpClient.SendAsync(request); // Assert - var doc = await AssertEx.HtmlResponse(response); - var radioButtons = doc.GetElementsByName("ConfirmDelete"); - var selectedRadioButton = radioButtons.Single(r => r.HasAttribute("checked")); - Assert.Equal("True", selectedRadioButton.GetAttribute("value")); + Assert.Equal(StatusCodes.Status200OK, (int)response.StatusCode); + } [Theory] [InlineData(true)] [InlineData(false)] - public async Task Get_ValidRequest_RendersPageAsExpected(bool populateOptional) + public async Task Get_ValidRequestWithPopulatedDataInJourneyState_ReturnsExpectedContent(bool isOpenAlert) { // Arrange - var alertType = await TestData.ReferenceDataCache.GetAlertTypeById(Guid.Parse("ed0cd700-3fb2-4db0-9403-ba57126090ed")); // Prohibition by the Secretary of State - misconduct - var startDate = TestData.Clock.Today.AddDays(-50); - var details = "Some details"; - var link = populateOptional ? TestData.GenerateUrl() : null; - var endDate = populateOptional ? TestData.Clock.Today.AddDays(-5) : (DateOnly?)null; - var person = await TestData.CreatePerson( - b => b.WithAlert( - a => a.WithAlertTypeId(alertType.AlertTypeId) - .WithDetails(details) - .WithExternalLink(link) - .WithStartDate(startDate) - .WithEndDate(endDate))); - var alert = person.Alerts.Single(); - var journeyInstance = await CreateJourneyInstance( - alert.AlertId, - new DeleteAlertState - { - ConfirmDelete = true - }); + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(ThisStep, alert); var request = new HttpRequestMessage(HttpMethod.Get, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -104,46 +80,50 @@ public async Task Get_ValidRequest_RendersPageAsExpected(bool populateOptional) // Assert var doc = await AssertEx.HtmlResponse(response); - Assert.Equal(alertType.Name, doc.GetElementByTestId("alert-type")!.TextContent); - Assert.Equal(details, doc.GetElementByTestId("details")!.TextContent); - Assert.Equal(populateOptional ? $"{link} (opens in new tab)" : "-", doc.GetElementByTestId("link")!.TextContent); - Assert.Equal(startDate.ToString("d MMMM yyyy"), doc.GetElementByTestId("start-date")!.TextContent); - Assert.Equal(populateOptional ? endDate?.ToString("d MMMM yyyy") : "-", doc.GetElementByTestId("end-date")!.TextContent); + + AssertCheckedRadioOption("HasAdditionalReasonDetail", bool.TrueString); + Assert.Equal(journeyInstance.State.DeleteReasonDetail, doc.GetElementsByName("DeleteReasonDetail")[0].TextContent); + AssertCheckedRadioOption("UploadEvidence", bool.TrueString); + + var uploadedEvidenceLink = doc.GetElementByTestId("uploaded-evidence-link"); + Assert.NotNull(uploadedEvidenceLink); + Assert.Equal($"{journeyInstance.State.EvidenceFileName} ({journeyInstance.State.EvidenceFileSizeDescription})", uploadedEvidenceLink!.TextContent); + + void AssertCheckedRadioOption(string name, string expectedCheckedValue) + { + var selectedOption = doc.GetElementsByName(name).SingleOrDefault(r => r.HasAttribute("checked")); + Assert.Equal(expectedCheckedValue, selectedOption?.GetAttribute("value")); + } } - [Fact] - public async Task Post_UserDoesNotHavePermission_ReturnsForbidden() + [Theory] + [RolesWithoutAlertWritePermissionData] + public async Task Post_UserDoesNotHavePermission_ReturnsForbidden(string? role) { // Arrange - SetCurrentUser(TestUsers.GetUser(roles: [])); + SetCurrentUser(TestUsers.GetUser(role)); - var person = await TestData.CreatePerson(b => b.WithAlert()); - var alert = person.Alerts.Single(); - - var journeyInstance = await CreateJourneyInstance(alert.AlertId); + var (person, alert) = await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") { - Content = new FormUrlEncodedContentBuilder() - { - { "ConfirmDelete", bool.TrueString } - } + Content = CreateMinimumValidPostContent() }; // Act var response = await HttpClient.SendAsync(request); // Assert - Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); + Assert.Equal(StatusCodes.Status403Forbidden, (int)response.StatusCode); ; } [Fact] - public async Task Post_WithAlertIdForNonExistentAlert_ReturnsNotFound() + public async Task Post_AlertDoesNotExist_ReturnsNotFound() { // Arrange - var person = await TestData.CreatePerson(); var alertId = Guid.NewGuid(); - var journeyInstance = await CreateJourneyInstance(alertId); + var journeyInstance = await CreateEmptyJourneyInstance(alertId); var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -154,63 +134,153 @@ public async Task Post_WithAlertIdForNonExistentAlert_ReturnsNotFound() Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); } - [Fact] - public async Task Post_WhenNoConfirmDeleteOptionIsSelected_ReturnsError() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_NoHasAdditionalReasonDetailIsSelected_ReturnsError(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = CreatePostContent( + uploadEvidence: false) + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "HasAdditionalReasonDetail", "Select yes if you want to add why you are deleting this alert"); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_AdditionalDetailIsYesButAdditionalDetailsAreEmpty_ReturnsError(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = CreatePostContent( + hasAdditionalReasonDetail: true, + deleteReasonDetail: null, + uploadEvidence: false) + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "DeleteReasonDetail", "Enter additional detail"); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_WhenUploadEvidenceOptionIsYesAndNoFileIsSelected_ReturnsError(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = CreatePostContent( + hasAdditionalReasonDetail: false, + uploadEvidence: true, + evidenceFile: null) + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "EvidenceFile", "Select a file"); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_WhenEvidenceFileIsInvalidType_ReturnsError(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = CreatePostContent( + hasAdditionalReasonDetail: false, + uploadEvidence: true, + evidenceFile: (CreateEvidenceFileBinaryContent(), "invalidfile.cs")) + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "EvidenceFile", "The selected file must be a BMP, CSV, DOC, DOCX, EML, JPEG, JPG, MBOX, MSG, ODS, ODT, PDF, PNG, TIF, TXT, XLS or XLSX"); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_ValidInputWithoutEvidenceFile_UpdatesStateAndRedirectsToCheckAnswersPage(bool isOpenAlert) { // Arrange - var alertType = await TestData.ReferenceDataCache.GetAlertTypeById(Guid.Parse("ed0cd700-3fb2-4db0-9403-ba57126090ed")); // Prohibition by the Secretary of State - misconduct - var startDate = TestData.Clock.Today.AddDays(-50); - var details = "Some details"; - var link = TestData.GenerateUrl(); - var endDate = TestData.Clock.Today.AddDays(-5); - var person = await TestData.CreatePerson( - b => b.WithAlert( - a => a.WithAlertTypeId(alertType.AlertTypeId) - .WithDetails(details) - .WithExternalLink(link) - .WithStartDate(startDate) - .WithEndDate(endDate))); - var alert = person.Alerts.Single(); - var journeyInstance = await CreateJourneyInstance(alert.AlertId); - - var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}"); + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var hasAdditionalReasonDetail = true; + var reasonDetail = "More details"; + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = CreatePostContent( + hasAdditionalReasonDetail: hasAdditionalReasonDetail, + deleteReasonDetail: reasonDetail, + uploadEvidence: false) + }; // Act var response = await HttpClient.SendAsync(request); // Assert - await AssertEx.HtmlResponseHasError(response, "ConfirmDelete", "Confirm you want to delete this alert"); + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.StartsWith($"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + Assert.Equal(hasAdditionalReasonDetail, journeyInstance.State.HasAdditionalReasonDetail); + Assert.Equal(reasonDetail, journeyInstance.State.DeleteReasonDetail); + Assert.Null(journeyInstance.State.EvidenceFileName); + Assert.Null(journeyInstance.State.EvidenceFileId); + Assert.Null(journeyInstance.State.EvidenceFileSizeDescription); } [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public async Task Post_ValidInput_RedirectsToAppropriatePage(bool confirmDelete, bool isActive) + [InlineData(true)] + [InlineData(false)] + public async Task Post_ValidInputWithEvidenceFile_UpdatesStateAndRedirectsToCheckAnswersPage(bool isOpenAlert) { // Arrange - var alertType = await TestData.ReferenceDataCache.GetAlertTypeById(Guid.Parse("ed0cd700-3fb2-4db0-9403-ba57126090ed")); // Prohibition by the Secretary of State - misconduct - var startDate = TestData.Clock.Today.AddDays(-50); - var details = "Some details"; - var link = TestData.GenerateUrl(); - var endDate = isActive ? (DateOnly?)null : TestData.Clock.Today.AddDays(-5); - var person = await TestData.CreatePerson( - b => b.WithAlert( - a => a.WithAlertTypeId(alertType.AlertTypeId) - .WithDetails(details) - .WithExternalLink(link) - .WithStartDate(startDate) - .WithEndDate(endDate))); - var alert = person.Alerts.Single(); - var journeyInstance = await CreateJourneyInstance(alert.AlertId); + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var hasAdditionalReasonDetail = false; + var evidenceFileName = "evidence.pdf"; var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete?{journeyInstance.GetUniqueIdQueryParameter()}") { - Content = new FormUrlEncodedContent(new Dictionary - { - ["ConfirmDelete"] = confirmDelete ? "True" : "False" - }) + Content = CreatePostContent( + hasAdditionalReasonDetail: hasAdditionalReasonDetail, + uploadEvidence: true, + evidenceFile: (CreateEvidenceFileBinaryContent(), evidenceFileName)) }; // Act @@ -218,26 +288,78 @@ public async Task Post_ValidInput_RedirectsToAppropriatePage(bool confirmDelete, // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - if (confirmDelete) + Assert.StartsWith($"/alerts/{alert.AlertId}/delete/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + Assert.False(journeyInstance.State.HasAdditionalReasonDetail); + Assert.Null(journeyInstance.State.DeleteReasonDetail); + Assert.Equal(evidenceFileName, journeyInstance.State.EvidenceFileName); + Assert.NotNull(journeyInstance.State.EvidenceFileId); + Assert.NotNull(journeyInstance.State.EvidenceFileSizeDescription); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Post_Cancel_DeletesJourneyAndRedirects(bool isOpenAlert) + { + // Arrange + var (person, alert) = isOpenAlert ? await CreatePersonWithOpenAlert() : await CreatePersonWithClosedAlert(); + var journeyInstance = await CreateJourneyInstanceForCompletedStep(PreviousStep, alert); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/alerts/{alert.AlertId}/delete/cancel?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + if (isOpenAlert) { - Assert.StartsWith($"/alerts/{alert.AlertId}/delete/confirm", response.Headers.Location!.OriginalString); + Assert.Equal($"/persons/{person.PersonId}/alerts", response.Headers.Location!.OriginalString); } else { - if (isActive) - { - Assert.StartsWith($"/persons/{person.PersonId}/alerts", response.Headers.Location!.OriginalString); - } - else - { - Assert.StartsWith($"/alerts/{alert.AlertId}", response.Headers.Location!.OriginalString); - } + Assert.Equal($"/alerts/{alert.AlertId}", response.Headers.Location!.OriginalString); } + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + Assert.Null(journeyInstance); } - private async Task> CreateJourneyInstance(Guid alertId, DeleteAlertState? state = null) => - await CreateJourneyInstance( - JourneyNames.DeleteAlert, - state ?? new DeleteAlertState(), - new KeyValuePair("alertId", alertId)); + private static MultipartFormDataContentBuilder CreateMinimumValidPostContent() => + CreatePostContent( + hasAdditionalReasonDetail: false, + uploadEvidence: false); + + private static MultipartFormDataContentBuilder CreatePostContent( + bool? hasAdditionalReasonDetail = null, + string? deleteReasonDetail = null, + bool? uploadEvidence = null, + (HttpContent Content, string FileName)? evidenceFile = null) + { + var builder = new MultipartFormDataContentBuilder(); + + if (hasAdditionalReasonDetail is not null) + { + builder.Add("HasAdditionalReasonDetail", hasAdditionalReasonDetail); + } + + if (deleteReasonDetail is not null) + { + builder.Add("DeleteReasonDetail", deleteReasonDetail); + } + + if (uploadEvidence is not null) + { + builder.Add("UploadEvidence", uploadEvidence); + } + + if (evidenceFile is not null) + { + builder.Add("EvidenceFile", evidenceFile.Value.Content, evidenceFile.Value.FileName); + } + + return builder; + } }