diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Extensions.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Extensions.cs index 5f57a095a5..ef75b7bbf5 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Extensions.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Extensions.cs @@ -95,6 +95,7 @@ public static IServiceCollection AddSupportUiServices(this IServiceCollection se .AddTransient() .AddTransient() .AddTransient() + .AddTransient() .AddSingleton, FormTagHelperInitializer>() .AddSingleton, TextInputTagHelperInitializer>() .AddScoped() diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonCanBeMergedFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonCanBeMergedFilter.cs new file mode 100644 index 0000000000..cb496681ad --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonCanBeMergedFilter.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using TeachingRecordSystem.Core.DataStore.Postgres; + +namespace TeachingRecordSystem.SupportUi.Infrastructure.Filters; + +public class CheckPersonCanBeMergedFilter(TrsDbContext dbContext) : IAsyncResourceFilter +{ + public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) + { + var currentPerson = context.HttpContext.Features.GetRequiredFeature(); + var person = await dbContext.Persons + .SingleAsync(p => p.PersonId == currentPerson.PersonId); + + var hasOpenAlert = await dbContext.Alerts + .AnyAsync(a => a.PersonId == currentPerson.PersonId && a.EndDate == null); + + var hasMandatoryQualification = await dbContext.MandatoryQualifications + .AnyAsync(mq => mq.PersonId == currentPerson.PersonId); + + var hasProfessionalStatus = + person.EytsDate.HasValue || + person.HasEyps || + person.PqtsDate.HasValue || + person.QtsDate.HasValue || + person.QtlsStatus != QtlsStatus.None; + + if (hasOpenAlert || + hasMandatoryQualification || + hasProfessionalStatus || + person.InductionStatus != InductionStatus.None) + { + context.Result = new BadRequestResult(); + return; + } + + await next(); + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonExistsFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonExistsFilter.cs index e991dc0742..2874623e32 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonExistsFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckPersonExistsFilter.cs @@ -54,7 +54,7 @@ public class CheckPersonExistsFilterFactory : IFilterFactory, IOrderedFilter { public bool IsReusable => false; - public int Order => -200; + public int Order => FilterOrders.CheckPersonExistsFilterOrder; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => ActivatorUtilities.CreateInstance(serviceProvider); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckSupportTaskExistsFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckSupportTaskExistsFilter.cs index 80b17c158c..3545dcb435 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckSupportTaskExistsFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckSupportTaskExistsFilter.cs @@ -60,7 +60,7 @@ public class CheckSupportTaskExistsFilterFactory(bool openOnly, params SupportTa { public bool IsReusable => false; - public int Order => -200; + public int Order => FilterOrders.CheckSupportTaskExistsFilterOrder; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => ActivatorUtilities.CreateInstance(serviceProvider, openOnly, supportTaskTypes); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckUserExistsFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckUserExistsFilter.cs index 4c2d5618da..475326be7c 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckUserExistsFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/CheckUserExistsFilter.cs @@ -5,7 +5,7 @@ namespace TeachingRecordSystem.SupportUi.Infrastructure.Filters; public class CheckUserExistsFilter : IAsyncResourceFilter, IOrderedFilter { - public int Order => int.MinValue; + public int Order => FilterOrders.CheckUserExistsFilterOrder; public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/FilterOrders.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/FilterOrders.cs new file mode 100644 index 0000000000..a11c5e331d --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/FilterOrders.cs @@ -0,0 +1,21 @@ +namespace TeachingRecordSystem.SupportUi.Infrastructure.Filters; + +public class FilterOrders +{ + public const int CheckUserExistsFilterOrder = int.MinValue; + public const int RedirectWithPersonIdFilterOrder = -1000; + public const int RequireFeatureEnabledFilterOrder = -300; + public const int CheckPersonExistsFilterOrder = -200; + public const int CheckMandatoryQualificationExistsFilterOrder = -200; + public const int CheckSupportTaskExistsFilterOrder = -200; + public const int CheckPersonCanBeMergedFilterOrder = -150; + public const int ActivateInstanceFilterOrder = -100; // Form Flow + public const int MissingInstanceFilterOrder = -100; // Form Flow + public const int CheckJourneyStepsFilterOrder = -10; // Form Flow + public const int CheckAlertExistsFilterOrder = 0; + public const int CheckRouteToProfessionalStatusExistsFilterOrder = 0; + public const int RequireOpenAlertFilterOrder = 1; + public const int RequireClosedAlertFilterOrder = 1; + + public const int RequireActivePersonFilterOrder = 100; +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RedirectWithPersonIdFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RedirectWithPersonIdFilter.cs index 07eae36e0b..453651d4b0 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RedirectWithPersonIdFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RedirectWithPersonIdFilter.cs @@ -10,7 +10,7 @@ public partial class RedirectWithPersonIdFilter(TrsDbContext dbContext) : IAsync [GeneratedRegex(@"^\/persons\/([0-9]{7})($|\/)")] private static partial Regex _personWithTrnPathRegex(); - public static int Order => -1000; + public static int Order => FilterOrders.RedirectWithPersonIdFilterOrder; public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireActivePersonFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireActivePersonFilter.cs index bb7a110eaa..f51a13d00b 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireActivePersonFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireActivePersonFilter.cs @@ -29,5 +29,5 @@ public void OnResourceExecuted(ResourceExecutedContext context) { } - public int Order => 100; + public int Order => FilterOrders.RequireActivePersonFilterOrder; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireClosedAlertFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireClosedAlertFilter.cs index 0b6b1b7f8b..f744c656f8 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireClosedAlertFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireClosedAlertFilter.cs @@ -6,7 +6,7 @@ namespace TeachingRecordSystem.SupportUi.Infrastructure.Filters; public class RequireClosedAlertFilter : IResourceFilter, IOrderedFilter { - public int Order => 1; // After CheckAlertExistsFilter + public int Order => FilterOrders.RequireClosedAlertFilterOrder; // After CheckAlertExistsFilter public void OnResourceExecuted(ResourceExecutedContext context) { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireFeatureEnabledFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireFeatureEnabledFilter.cs index 93458e6386..77ec0a46c2 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireFeatureEnabledFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireFeatureEnabledFilter.cs @@ -23,7 +23,7 @@ public class RequireFeatureEnabledFilterFactoryAttribute(string featureName) : A { public bool IsReusable => false; - public int Order => -300; + public int Order => FilterOrders.RequireFeatureEnabledFilterOrder; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => ActivatorUtilities.CreateInstance(serviceProvider, featureName); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireOpenAlertFilter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireOpenAlertFilter.cs index 2a580c105d..21fc4ef226 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireOpenAlertFilter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Infrastructure/Filters/RequireOpenAlertFilter.cs @@ -6,7 +6,7 @@ namespace TeachingRecordSystem.SupportUi.Infrastructure.Filters; public class RequireOpenAlertFilter : IResourceFilter, IOrderedFilter { - public int Order => 1; // After CheckAlertExistsFilter + public int Order => FilterOrders.RequireOpenAlertFilterOrder; // After CheckAlertExistsFilter public void OnResourceExecuted(ResourceExecutedContext context) { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/DeleteMq/Conventions.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/DeleteMq/Conventions.cs index 80392ee271..20569c098f 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/DeleteMq/Conventions.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/DeleteMq/Conventions.cs @@ -12,7 +12,7 @@ public void Configure(RazorPagesOptions options) this.GetFolderPathFromNamespace(), model => { - model.Filters.Add(new ServiceFilterAttribute()); + model.Filters.Add(new ServiceFilterAttribute() { Order = FilterOrders.CheckMandatoryQualificationExistsFilterOrder }); }); } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/EditMq/Conventions.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/EditMq/Conventions.cs index 527fc96a85..66877c0d6a 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/EditMq/Conventions.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Mqs/EditMq/Conventions.cs @@ -12,7 +12,7 @@ public void Configure(RazorPagesOptions options) this.GetFolderPathFromNamespace(), model => { - model.Filters.Add(new ServiceFilterAttribute() { Order = -200 }); + model.Filters.Add(new ServiceFilterAttribute() { Order = FilterOrders.CheckMandatoryQualificationExistsFilterOrder }); }); } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Conventions.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Conventions.cs index b75a00532d..1c61ad8301 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Conventions.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Conventions.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.SupportUi.Infrastructure.Filters; using TeachingRecordSystem.SupportUi.Infrastructure.Security; namespace TeachingRecordSystem.SupportUi.Pages.Persons.MergePerson; @@ -12,6 +14,8 @@ public void Configure(RazorPagesOptions options) this.GetFolderPathFromNamespace(), model => { + model.Filters.Add(new CheckPersonExistsFilterFactory()); + model.Filters.Add(new ServiceFilterAttribute() { Order = FilterOrders.CheckPersonCanBeMergedFilterOrder }); model.EndpointMetadata.Add(new AuthorizeAttribute() { Policy = AuthorizationPolicies.PersonDataEdit diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Index.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Index.cshtml.cs index d2c4ea9994..c8f80ab170 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Index.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/MergePerson/Index.cshtml.cs @@ -11,5 +11,8 @@ public class IndexModel(SupportUiLinkGenerator linkGenerator) : PageModel [FromRoute] public Guid PersonId { get; set; } - public IActionResult OnGet() => Redirect(linkGenerator.Persons.MergePerson.EnterTrn(PersonId, JourneyInstance!.InstanceId)); + public IActionResult OnGet() + { + return Redirect(linkGenerator.Persons.MergePerson.EnterTrn(PersonId, JourneyInstance!.InstanceId)); + } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Index.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Index.cshtml.cs index 23422c9801..049e0192d3 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Index.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Index.cshtml.cs @@ -10,8 +10,6 @@ namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail; [AllowDeactivatedPerson] public class IndexModel(TrsDbContext dbContext, IAuthorizationService authorizationService) : PageModel { - private static readonly InductionStatus[] _invalidInductionStatusesForMerge = [InductionStatus.InProgress, InductionStatus.Passed, InductionStatus.Failed]; - [FromRoute] public Guid PersonId { get; set; } @@ -33,6 +31,7 @@ public async Task OnGetAsync() .IgnoreQueryFilters() .Include(p => p.PreviousNames).AsSplitQuery() .Include(p => p.Alerts).AsSplitQuery() + .Include(p => p.Qualifications).AsSplitQuery() .SingleAsync(p => p.PersonId == PersonId); Person = BuildPersonInfo(person); @@ -48,12 +47,15 @@ public async Task OnGetAsync() canEditPersonData && Person.IsActive; + var hasMandatoryQualification = person.Qualifications! + .Any(q => q.QualificationType == QualificationType.MandatoryQualification); + CanMerge = canEditNonPersonOrAlertData && !HasOpenAlert && + !hasMandatoryQualification && Person!.IsActive && - (PersonProfessionalStatus is not PersonProfessionalStatusInfo professionalStatus || - !_invalidInductionStatusesForMerge.Contains(professionalStatus.InductionStatus)); + PersonProfessionalStatus is not PersonProfessionalStatusInfo professionalStatus; // Person cannot be reactivated if they were deactivated as part of a merge // where they were merged into another Person (i.e. they were the secondary diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/MergePerson/IndexTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/MergePerson/IndexTests.cs new file mode 100644 index 0000000000..0e11f27ba8 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/MergePerson/IndexTests.cs @@ -0,0 +1,185 @@ +namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Persons.MergePerson; + +public class IndexTests(HostFixture hostFixture) : TestBase(hostFixture) +{ + [Fact] + public async Task Get_RedirectsToEnterTrn() + { + // Arrange + var person = await TestData.CreatePersonAsync(); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); // Initializes journey + response = await response.FollowRedirectAsync(HttpClient); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.StartsWith($"/persons/{person.PersonId}/merge/enter-trn", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_WithPersonIdForNonExistentPerson_ReturnsNotFound() + { + // Arrange + var nonExistentPersonId = Guid.NewGuid(); + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{nonExistentPersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasOpenAlert_ReturnsBadRequest() + { + // Arrange + var person = await TestData.CreatePersonAsync(p => p + .WithAlert(a => a.WithEndDate(null))); + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasMandatoryQualification_ReturnsBadRequest() + { + // Arrange + var person = await TestData.CreatePersonAsync(p => p + .WithMandatoryQualification()); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Theory] + [InlineData(InductionStatus.InProgress, false)] + [InlineData(InductionStatus.Passed, false)] + [InlineData(InductionStatus.Failed, false)] + [InlineData(InductionStatus.None, true)] + [InlineData(InductionStatus.Exempt, false)] + [InlineData(InductionStatus.FailedInWales, false)] + [InlineData(InductionStatus.RequiredToComplete, false)] + public async Task Get_PersonWithInductionStatus_ReturnsExpectedResult(InductionStatus status, bool expectIsValid) + { + // Arrange + var person = await TestData.CreatePersonAsync(p => p + .WithInductionStatus(i => i + .WithStatus(status) + .WithStartDate(new DateOnly(2024, 1, 1)) + .WithCompletedDate(new DateOnly(2024, 1, 1)))); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + if (expectIsValid) + { + response = await response.FollowRedirectAsync(HttpClient); + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.StartsWith($"/persons/{person.PersonId}/merge/enter-trn", response.Headers.Location?.OriginalString); + } + else + { + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + } + + [Fact] + public async Task Get_PersonHasQts_ReturnsBadRequest() + { + // Arrange + var awardDate = Clock.Today; + var person = await TestData.CreatePersonAsync(p => p + .WithQts(awardDate)); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasQtls_ReturnsBadRequest() + { + // Arrange + var awardDate = Clock.Today; + var person = await TestData.CreatePersonAsync(p => p + .WithQtls(awardDate)); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasEyts_ReturnsBadRequest() + { + // Arrange + var awardDate = Clock.Today; + var person = await TestData.CreatePersonAsync(p => p + .WithHoldsRouteToProfessionalStatus(ProfessionalStatusType.EarlyYearsTeacherStatus, awardDate)); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasEyps_ReturnsBadRequest() + { + // Arrange + var person = await TestData.CreatePersonAsync(p => p + .WithHoldsRouteToProfessionalStatus(ProfessionalStatusType.EarlyYearsProfessionalStatus)); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_PersonHasPqts_ReturnsBadRequest() + { + // Arrange + var awardDate = Clock.Today; + var person = await TestData.CreatePersonAsync(p => p + .WithHoldsRouteToProfessionalStatus(ProfessionalStatusType.PartialQualifiedTeacherStatus, awardDate)); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}/merge"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/PersonDetail/IndexTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/PersonDetail/IndexTests.cs index d50d357b4e..1fe5d937d8 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/PersonDetail/IndexTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Persons/PersonDetail/IndexTests.cs @@ -246,6 +246,7 @@ public async Task Get_PersonHasQts_ShowsDetails() Assert.Equal("No", doc.GetSummaryListValueByKey("Early years professional status (EYPS)")); Assert.Equal("No", doc.GetSummaryListValueByKey("Partial qualified teacher status (PQTS)")); Assert.Null(doc.GetSummaryListValueByKey("PQTS held since")); + Assert.Null(doc.GetElementByTestId("merge-button")); } [Fact] @@ -275,6 +276,7 @@ public async Task Get_PersonHasQtls_ShowsDetails() Assert.Equal("No", doc.GetSummaryListValueByKey("Early years professional status (EYPS)")); Assert.Equal("No", doc.GetSummaryListValueByKey("Partial qualified teacher status (PQTS)")); Assert.Null(doc.GetSummaryListValueByKey("PQTS held since")); + Assert.Null(doc.GetElementByTestId("merge-button")); } [Fact] @@ -304,14 +306,13 @@ public async Task Get_PersonHasEyts_ShowsDetails() Assert.Equal("No", doc.GetSummaryListValueByKey("Early years professional status (EYPS)")); Assert.Equal("No", doc.GetSummaryListValueByKey("Partial qualified teacher status (PQTS)")); Assert.Null(doc.GetSummaryListValueByKey("PQTS held since")); + Assert.Null(doc.GetElementByTestId("merge-button")); } [Fact] public async Task Get_PersonHasEyps_ShowsDetails() { // Arrange - var awardDate = Clock.Today; - var person = await TestData.CreatePersonAsync(p => p .WithHoldsRouteToProfessionalStatus(ProfessionalStatusType.EarlyYearsProfessionalStatus)); @@ -333,6 +334,7 @@ public async Task Get_PersonHasEyps_ShowsDetails() Assert.Equal("Holds", doc.GetSummaryListValueByKey("Early years professional status (EYPS)")); Assert.Equal("No", doc.GetSummaryListValueByKey("Partial qualified teacher status (PQTS)")); Assert.Null(doc.GetSummaryListValueByKey("PQTS held since")); + Assert.Null(doc.GetElementByTestId("merge-button")); } [Fact] @@ -362,6 +364,7 @@ public async Task Get_PersonHasPqts_ShowsDetails() Assert.Equal("No", doc.GetSummaryListValueByKey("Early years professional status (EYPS)")); Assert.Equal("Holds", doc.GetSummaryListValueByKey("Partial qualified teacher status (PQTS)")); Assert.Equal(awardDate.ToString(UiDefaults.DateOnlyDisplayFormat), doc.GetSummaryListValueByKey("PQTS held since")); + Assert.Null(doc.GetElementByTestId("merge-button")); } [Theory] @@ -478,14 +481,31 @@ public async Task Get_PersonDoesNotHaveOpenAlert_ShowsMergeButton() Assert.NotNull(doc.GetElementByTestId("merge-button")); } + [Fact] + public async Task Get_PersonHasMandatoryQualification_DoesNotShowMergeButton() + { + // Arrange + var person = await TestData.CreatePersonAsync(p => p + .WithMandatoryQualification()); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/persons/{person.PersonId}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + var doc = await AssertEx.HtmlResponseAsync(response); + Assert.Null(doc.GetElementByTestId("merge-button")); + } + [Theory] [InlineData(InductionStatus.InProgress, false)] [InlineData(InductionStatus.Passed, false)] [InlineData(InductionStatus.Failed, false)] [InlineData(InductionStatus.None, true)] - [InlineData(InductionStatus.Exempt, true)] - [InlineData(InductionStatus.FailedInWales, true)] - [InlineData(InductionStatus.RequiredToComplete, true)] + [InlineData(InductionStatus.Exempt, false)] + [InlineData(InductionStatus.FailedInWales, false)] + [InlineData(InductionStatus.RequiredToComplete, false)] public async Task Get_PersonWithInductionStatus_ShowsMergeButtonAsExpected(InductionStatus status, bool expectMergeButtonToBeShown) { // Arrange