diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs index 647f07c0e..a7d54ca10 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs @@ -11,14 +11,20 @@ public string DebugIdentity(JourneyInstanceId journeyInstanceId) => public string NotVerified(JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/NotVerified", journeyInstanceId: journeyInstanceId); - public string NationalInsuranceNumber(JourneyInstanceId journeyInstanceId) => - GetRequiredPathByPage("/NationalInsuranceNumber", journeyInstanceId: journeyInstanceId); + public string Connect(JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/Connect", journeyInstanceId: journeyInstanceId); - public string NationalInsuranceNumberContinueWithout(JourneyInstanceId journeyInstanceId) => - GetRequiredPathByPage("/NationalInsuranceNumber", handler: "ContinueWithout", journeyInstanceId: journeyInstanceId); + public string NationalInsuranceNumber(JourneyInstanceId journeyInstanceId, bool? fromCheckAnswers = null) => + GetRequiredPathByPage("/NationalInsuranceNumber", routeValues: new { fromCheckAnswers }, journeyInstanceId: journeyInstanceId); - public string Trn(JourneyInstanceId journeyInstanceId) => - GetRequiredPathByPage("/Trn", journeyInstanceId: journeyInstanceId); + public string Trn(JourneyInstanceId journeyInstanceId, bool? fromCheckAnswers = null) => + GetRequiredPathByPage("/Trn", routeValues: new { fromCheckAnswers }, journeyInstanceId: journeyInstanceId); + + public string CheckAnswers(JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/CheckAnswers", journeyInstanceId: journeyInstanceId); + + public string Found(JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/Found", journeyInstanceId: journeyInstanceId); public string NotFound(JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/NotFound", journeyInstanceId: journeyInstanceId); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OidcController.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs similarity index 96% rename from TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OidcController.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs index 5b2203714..8add1d424 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OidcController.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs @@ -12,13 +12,13 @@ namespace TeachingRecordSystem.AuthorizeAccess.Controllers; -public class OidcController( +public class OAuth2Controller( TrsDbContext dbContext, IOpenIddictAuthorizationManager authorizationManager, IOpenIddictScopeManager scopeManager) : Controller { - [HttpGet("~/connect/authorize")] - [HttpPost("~/connect/authorize")] + [HttpGet("~/oauth2/authorize")] + [HttpPost("~/oauth2/authorize")] [IgnoreAntiforgeryToken] public async Task Authorize() { @@ -82,7 +82,7 @@ public async Task Authorize() return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } - [HttpPost("~/connect/token")] + [HttpPost("~/oauth2/token")] [IgnoreAntiforgeryToken] [Produces("application/json")] public async Task Token() @@ -109,8 +109,8 @@ public async Task Token() } [Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)] - [HttpGet("~/connect/userinfo")] - [HttpPost("~/connect/userinfo")] + [HttpGet("~/oauth2/userinfo")] + [HttpPost("~/oauth2/userinfo")] [Produces("application/json")] public IActionResult UserInfo() { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml new file mode 100644 index 000000000..3847308b3 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml @@ -0,0 +1,48 @@ +@page "/check-answers" +@model TeachingRecordSystem.AuthorizeAccess.Pages.CheckAnswersModel +@{ + ViewBag.Title = "Check your answers"; +} + +@section BeforeContent { + +} + +
+
+
+

@ViewBag.Title

+ +

Your GOV.UK One Login could not be connected to your teaching record.

+ +

Check your answers and try again.

+ + + + + National Insurance number + + + @(Model.NationalInsuranceNumber ?? "None") + + + Change + + + + + Teacher reference number + + + @(Model.Trn ?? "None") + + + Change + + + + + Continue +
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml.cs new file mode 100644 index 000000000..d48275245 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/CheckAnswers.cshtml.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.FormFlow; + +namespace TeachingRecordSystem.AuthorizeAccess.Pages; + +[Journey(SignInJourneyState.JourneyName), RequireJourneyInstance] +public class CheckAnswersModel(SignInJourneyHelper helper) : PageModel +{ + public JourneyInstance? JourneyInstance { get; set; } + + public string? NationalInsuranceNumber => JourneyInstance!.State.NationalInsuranceNumber; + + public string? Trn => JourneyInstance!.State.Trn; + + public void OnGet() + { + } + + public IActionResult OnPost() => Redirect(helper.LinkGenerator.NotFound(JourneyInstance!.InstanceId)); + + public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + var state = JourneyInstance!.State; + + if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) + { + // Not authenticated/verified with One Login + context.Result = BadRequest(); + } + else if (state.AuthenticationTicket is not null) + { + // Already matched to a Teaching Record + context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + } + else if (!state.HaveNationalInsuranceNumber.HasValue) + { + // Not answered the NINO question + context.Result = Redirect(helper.LinkGenerator.NationalInsuranceNumber(JourneyInstance.InstanceId)); + } + else if (!state.HaveTrn.HasValue) + { + // Not answered the TRN question + context.Result = Redirect(helper.LinkGenerator.Trn(JourneyInstance.InstanceId)); + } + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml new file mode 100644 index 000000000..9be6930ba --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml @@ -0,0 +1,31 @@ +@page "/connect" +@model TeachingRecordSystem.AuthorizeAccess.Pages.ConnectModel +@{ + ViewBag.Title = "Connect your GOV.UK One Login to your teaching record"; +} + +
+
+
+
+

@ViewBag.Title

+ +
+

+ You’ve verified your identity for your GOV.UK One Login. +

+ +

+ You now need to connect it to your teaching record. You’ll be asked for your: +

    +
  • National Insurance number
  • +
  • teacher reference number
  • +
+

+ + Connect to your teaching record + +
+
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml.cs new file mode 100644 index 000000000..5a85d445d --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Connect.cshtml.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.FormFlow; + +namespace TeachingRecordSystem.AuthorizeAccess.Pages; + +[Journey(SignInJourneyState.JourneyName), RequireJourneyInstance] +public class ConnectModel(SignInJourneyHelper helper) : PageModel +{ + public JourneyInstance? JourneyInstance { get; set; } + + public void OnGet() + { + } + + public IActionResult OnPost() => Redirect(helper.LinkGenerator.NationalInsuranceNumber(JourneyInstance!.InstanceId)); + + public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + var state = JourneyInstance!.State; + + if (state.AuthenticationTicket is not null) + { + // Already matched to a Teaching Record + context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + } + else if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) + { + // Not authenticated/verified with One Login + context.Result = BadRequest(); + } + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs index a9581056c..dfcc935b2 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs @@ -107,9 +107,14 @@ public async Task OnPost() await JourneyInstance!.UpdateStateAsync(state => { - state.IdentityVerified = IdentityVerified; - state.VerifiedNames = verifiedNames; - state.VerifiedDatesOfBirth = verifiedDatesOfBirth; + if (IdentityVerified) + { + state.SetVerified(verifiedNames!, verifiedDatesOfBirth!); + } + else + { + state.ClearVerified(); + } }); if (DetachPerson && _oneLoginUser?.PersonId is not null) diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml new file mode 100644 index 000000000..daa370c4b --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml @@ -0,0 +1,24 @@ +@page "/found" +@model TeachingRecordSystem.AuthorizeAccess.Pages.FoundModel +@{ + ViewBag.Title = "We’ve found your teaching record"; +} + +
+
+
+ + + Your GOV.UK One Login is connected to your teaching record + + +

+ You won’t need to do this again. +

+ + Access your teaching record +
+
+
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml.cs new file mode 100644 index 000000000..8c44e9535 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Found.cshtml.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.FormFlow; + +namespace TeachingRecordSystem.AuthorizeAccess.Pages; + +[Journey(SignInJourneyState.JourneyName), RequireJourneyInstance] +public class FoundModel(SignInJourneyHelper helper) : PageModel +{ + public JourneyInstance? JourneyInstance { get; set; } + + public void OnGet() + { + } + + public IActionResult OnPost() => Redirect(helper.GetNextPage(JourneyInstance!)); + + public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + var state = JourneyInstance!.State; + + if (state.AuthenticationTicket is null) + { + // Not matched + context.Result = Redirect(helper.GetNextPage(JourneyInstance)); + } + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml index 60cd11172..4994927ed 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml @@ -4,26 +4,25 @@ ViewBag.Title = Html.DisplayNameFor(m => m.NationalInsuranceNumber); } +@section BeforeContent { + +} +
-
- - - - - @* Pressing the Enter key will submit the first submit button - make sure it's the default action *@ - - - - I cannot provide my National Insurance number - -

You can find a lost National Insurance number (opens in new tab).

-

If you do not have a National Insurance number you can continue without it

- - Continue without it - -
-
+ + + + + + Yes + + + + + No + + Continue
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml.cs index 3b7ab9812..fc7a76ebd 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NationalInsuranceNumber.cshtml.cs @@ -7,81 +7,68 @@ namespace TeachingRecordSystem.AuthorizeAccess.Pages; [Journey(SignInJourneyState.JourneyName), RequireJourneyInstance] -public class NationalInsuranceNumberModel(SignInJourneyHelper helper, AuthorizeAccessLinkGenerator linkGenerator) : PageModel +public class NationalInsuranceNumberModel(SignInJourneyHelper helper) : PageModel { public JourneyInstance? JourneyInstance { get; set; } + [FromQuery] + public bool? FromCheckAnswers { get; set; } + + [BindProperty] + [Display(Name = "Do you have a National Insurance number?", Description = "It’s on your National Insurance card, benefit letter, payslip or P60. For example, ‘QQ 12 34 56 C’.")] + [Required(ErrorMessage = "Select yes if you have a National Insurance number")] + public bool? HaveNationalInsuranceNumber { get; set; } + [BindProperty] - [Display(Name = "What is your National Insurance number?", Description = "It’s on your National Insurance card, benefit letter, payslip or P60. For example, ‘QQ 12 34 56 C’.")] + [Display(Name = "National Insurance number")] [Required(ErrorMessage = "Enter a National Insurance number")] public string? NationalInsuranceNumber { get; set; } - public bool PreviouslyAnsweredCannotProvide { get; set; } - public void OnGet() { + HaveNationalInsuranceNumber = JourneyInstance!.State.HaveNationalInsuranceNumber; NationalInsuranceNumber = JourneyInstance!.State.NationalInsuranceNumber; } public async Task OnPost() { - if (ModelState[nameof(NationalInsuranceNumber)]!.Errors.Count == 0) + if (HaveNationalInsuranceNumber == true) { - if (!NationalInsuranceNumberHelper.IsValid(NationalInsuranceNumber)) + if (!string.IsNullOrEmpty(NationalInsuranceNumber) && !NationalInsuranceNumberHelper.IsValid(NationalInsuranceNumber)) { ModelState.AddModelError(nameof(NationalInsuranceNumber), "Enter a National Insurance number in the correct format"); } } - - if (!ModelState.IsValid) + else { - return this.PageWithErrors(); + ModelState.Remove(nameof(NationalInsuranceNumber)); } - await JourneyInstance!.UpdateStateAsync(state => - { - state.NationalInsuranceNumberSpecified = true; - state.NationalInsuranceNumber = NationalInsuranceNumber; - }); - - if (await helper.TryMatchToTeachingRecord(JourneyInstance!)) - { - return new RedirectResult(helper.GetNextPage(JourneyInstance)); - } - else + if (!ModelState.IsValid) { - return RedirectToNextPage(); + return this.PageWithErrors(); } - } - public async Task OnPostContinueWithout() - { - await JourneyInstance!.UpdateStateAsync(state => - { - state.NationalInsuranceNumberSpecified = true; - state.NationalInsuranceNumber = null; - }); + await JourneyInstance!.UpdateStateAsync(state => state.SetNationalInsuranceNumber(HaveNationalInsuranceNumber!.Value, NationalInsuranceNumber)); - return RedirectToNextPage(); + return await helper.TryMatchToTeachingRecord(JourneyInstance!) ? Redirect(helper.LinkGenerator.Found(JourneyInstance!.InstanceId)) : + FromCheckAnswers == true ? Redirect(helper.LinkGenerator.CheckAnswers(JourneyInstance!.InstanceId)) : + Redirect(helper.LinkGenerator.Trn(JourneyInstance!.InstanceId)); } public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) { var state = JourneyInstance!.State; - if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) - { - // Not authenticated/verified with One Login - context.Result = BadRequest(); - } - else if (state.AuthenticationTicket is not null) + if (state.AuthenticationTicket is not null) { // Already matched to a Teaching Record context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); } - - PreviouslyAnsweredCannotProvide = state.NationalInsuranceNumberSpecified && state.NationalInsuranceNumber is null; + else if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) + { + // Not authenticated/verified with One Login + context.Result = BadRequest(); + } } - - private IActionResult RedirectToNextPage() => Redirect(linkGenerator.Trn(JourneyInstance!.InstanceId)); } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml index ac799266f..1bfa5de6f 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml @@ -1,11 +1,29 @@ @page "/not-found" @model TeachingRecordSystem.AuthorizeAccess.Pages.NotFoundModel @{ - ViewBag.Title = "[PLACEHOLDER] Not found"; + ViewBag.Title = "We haven’t found your teaching record"; } @section BeforeContent { - + } -

@ViewBag.Title

+
+
+
+

Your GOV.UK One Login could not be connected to your teaching record

+ +
+

+ You need to get support to access your teaching qualifications. +

+ +

+ Your details will be sent to the Teaching Regulation Agency support team. +

+ + Get support +
+
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml.cs index 28ea0a1bb..afdc32314 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/NotFound.cshtml.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; using TeachingRecordSystem.FormFlow; @@ -18,20 +17,25 @@ public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) { var state = JourneyInstance!.State; - if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) + if (state.AuthenticationTicket is not null) + { + // Already matched to a Teaching Record + context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + } + else if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) { // Not authenticated/verified with One Login context.Result = BadRequest(); } - else if (!state.NationalInsuranceNumberSpecified || !state.TrnSpecified) + else if (!state.HaveNationalInsuranceNumber.HasValue) { - // Not specified NINO or TRN - context.Result = new RedirectResult(helper.GetNextPage(JourneyInstance)); + // Not answered the NINO question + context.Result = Redirect(helper.LinkGenerator.NationalInsuranceNumber(JourneyInstance.InstanceId)); } - else if (state.AuthenticationTicket is not null) + else if (!state.HaveTrn.HasValue) { - // Already matched to a Teaching Record - context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + // Not answered the TRN question + context.Result = Redirect(helper.LinkGenerator.Trn(JourneyInstance.InstanceId)); } } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml similarity index 92% rename from TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml rename to TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml index 19a4003a7..1cb17a8c0 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml @@ -1,7 +1,7 @@ -@page "/connect/logout" +@page "/oauth2/logout" @using Microsoft.AspNetCore.Http.Extensions @using Microsoft.Extensions.Primitives -@using TeachingRecordSystem.AuthorizeAccess.Pages.Oidc +@using TeachingRecordSystem.AuthorizeAccess.Pages.OAuth2 @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders @model SignOutModel @{ diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml.cs similarity index 97% rename from TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml.cs index 6e41e43a8..194a3c461 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Oidc/SignOut.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OAuth2/SignOut.cshtml.cs @@ -10,7 +10,7 @@ using TeachingRecordSystem.Core.DataStore.Postgres; using TeachingRecordSystem.Core.DataStore.Postgres.Models; -namespace TeachingRecordSystem.AuthorizeAccess.Pages.Oidc; +namespace TeachingRecordSystem.AuthorizeAccess.Pages.OAuth2; public class SignOutModel(TrsDbContext dbContext) : PageModel { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/_Layout.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/_Layout.cshtml index 28735c6dd..190013dda 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/_Layout.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/_Layout.cshtml @@ -6,7 +6,6 @@ @RenderSection("Styles", required: false) - @RenderSection("Scripts", required: false) } @section Header { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Shared/_Layout.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Shared/_Layout.cshtml index 863278c0c..14b5c68ee 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Shared/_Layout.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Shared/_Layout.cshtml @@ -1,3 +1,5 @@ +@inject GovUk.Frontend.AspNetCore.PageTemplateHelper PageTemplateHelper +@inject Joonasw.AspNetCore.SecurityHeaders.Csp.ICspNonceService NonceService @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders @{ Layout = "_GovUkPageTemplate"; @@ -6,13 +8,17 @@ { throw new InvalidOperationException("ViewBag.Title must be set."); } + + if (ViewBag.ServiceName is not null) + { + ViewBag.Title += $" - {ViewBag.ServiceName}"; + } } @section Head { @RenderSection("Styles", required: false) - @RenderSection("Scripts", required: false) } @section Header { @@ -23,7 +29,6 @@ else { var serviceName = ViewBag.ServiceName ?? throw new InvalidOperationException("ViewBag.ServiceName is not set."); - ViewBag.Title += $" - {serviceName}"; var serviceUrl = ViewBag.ServiceUrl ?? throw new InvalidOperationException("ViewBag.ServiceUrl is not set."); var signOutLink = ViewBag.SignOutLink ?? throw new InvalidOperationException("ViewBag.SignOutLink is not set."); @@ -154,6 +159,8 @@ } @section BodyEnd { + @PageTemplateHelper.GenerateScriptImports(NonceService.GetNonce()) + @RenderSection("Scripts", required: false) } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml index 21afc243c..39d005672 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml @@ -10,18 +10,39 @@
-
-

@ViewBag.Title

+ + + + + + Yes + + + + + No + + -

A TRN is 7 digits long, for example 4567814

-

It might include the letters RP and a slash, for example RP99/12345

-

It’s previously been known as a QTS, GTC, DfE, DfES and DCSF number

- - + + About TRNs + +

You’ll have a TRN if you:

+
    +
  • hold qualified teacher status (QTS) in England or Wales
  • +
  • hold early years teacher status (EYTS) in England
  • +
  • already hold a national professional qualification (NPQ)
  • +
  • are working towards QTS or a EYTS
  • +
+

+ Learn about TRNs including how to request one, or find a lost TRN. +

+

+ You may not have one yet if you work in early years, further education or qualified as a teacher outside of + England. +

+
+
Continue diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml.cs index 88aabf2d0..bbd420fb4 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Trn.cshtml.cs @@ -7,57 +7,66 @@ namespace TeachingRecordSystem.AuthorizeAccess.Pages; [Journey(SignInJourneyState.JourneyName), RequireJourneyInstance] -public class TrnModel(SignInJourneyHelper helper, AuthorizeAccessLinkGenerator linkGenerator) : PageModel +public class TrnModel(SignInJourneyHelper helper) : PageModel { public JourneyInstance? JourneyInstance { get; set; } + [FromQuery] + public bool? FromCheckAnswers { get; set; } + + [BindProperty] + [Display(Name = "Do you have a teacher reference number (TRN)?")] + [Required(ErrorMessage = "Select yes if you have a TRN")] + public bool? HaveTrn { get; set; } + [BindProperty] - [Display(Name = "Teacher reference number (TRN)")] + [Display(Name = "TRN")] [Required(ErrorMessage = "Enter your TRN")] [RegularExpression(@"\A\D*(\d{1}\D*){7}\D*\Z", ErrorMessage = "Your TRN should contain 7 digits")] public string? Trn { get; set; } public void OnGet() { + HaveTrn = JourneyInstance!.State.HaveTrn; Trn = JourneyInstance!.State.Trn; } public async Task OnPost() { + if (HaveTrn != true) + { + ModelState.Remove(nameof(Trn)); + } + if (!ModelState.IsValid) { return this.PageWithErrors(); } - await JourneyInstance!.UpdateStateAsync(state => - { - state.TrnSpecified = true; - state.Trn = Trn; - }); + await JourneyInstance!.UpdateStateAsync(state => state.SetTrn(HaveTrn!.Value, Trn)); - if (await helper.TryMatchToTeachingRecord(JourneyInstance!)) - { - return new RedirectResult(helper.GetNextPage(JourneyInstance)); - } - else - { - return Redirect(linkGenerator.NotFound(JourneyInstance.InstanceId)); - } + return await helper.TryMatchToTeachingRecord(JourneyInstance!) ? Redirect(helper.LinkGenerator.Found(JourneyInstance.InstanceId)) : + Redirect(helper.LinkGenerator.CheckAnswers(JourneyInstance.InstanceId)); } public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) { var state = JourneyInstance!.State; - if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) + if (state.AuthenticationTicket is not null) + { + // Already matched to a Teaching Record + context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + } + else if (state.OneLoginAuthenticationTicket is null || !state.IdentityVerified) { // Not authenticated/verified with One Login context.Result = BadRequest(); } - else if (state.AuthenticationTicket is not null) + else if (!state.HaveNationalInsuranceNumber.HasValue) { - // Already matched to a Teaching Record - context.Result = Redirect(helper.GetSafeRedirectUri(JourneyInstance)); + // Not answered the NINO question + context.Result = Redirect(helper.LinkGenerator.NationalInsuranceNumber(JourneyInstance.InstanceId)); } } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs index a8c5adee4..da7e8c9b8 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs @@ -76,10 +76,10 @@ .AddServer(options => { options - .SetAuthorizationEndpointUris("connect/authorize") - .SetLogoutEndpointUris("connect/logout") - .SetTokenEndpointUris("connect/token") - .SetUserinfoEndpointUris("connect/userinfo"); + .SetAuthorizationEndpointUris("oauth2/authorize") + .SetLogoutEndpointUris("oauth2/logout") + .SetTokenEndpointUris("oauth2/token") + .SetUserinfoEndpointUris("oauth2/userinfo"); // TODO - add teaching record scopes options.RegisterScopes(Scopes.Email, Scopes.Profile); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs index 35ebec310..7b28627ff 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs @@ -24,6 +24,8 @@ public class SignInJourneyHelper( public const string AuthenticationOnlyVtr = @"[""Cl.Cm""]"; public const string AuthenticationAndIdentityVerificationVtr = @"[""Cl.Cm.P2""]"; + public AuthorizeAccessLinkGenerator LinkGenerator { get; } = linkGenerator; + public IUserInstanceStateProvider UserInstanceStateProvider { get; } = userInstanceStateProvider; public bool ShowDebugPages => optionsAccessor.Value.ShowDebugPages; @@ -49,17 +51,18 @@ await journeyInstance.UpdateStateAsync(state => state.AttemptedIdentityVerification = attemptedIdentityVerification; var vc = ticket.Principal.FindFirstValue("vc") is string vcStr ? JsonDocument.Parse(vcStr) : null; - state.IdentityVerified = vc is not null; - if (state.IdentityVerified) + var identityVerified = vc is not null; + if (identityVerified) { - state.VerifiedNames = ticket.Principal.GetCoreIdentityNames().Select(n => n.NameParts.Select(part => part.Value).ToArray()).ToArray(); - state.VerifiedDatesOfBirth = ticket.Principal.GetCoreIdentityBirthDates().Select(d => d.Value).ToArray(); + state.SetVerified( + verifiedNames: ticket.Principal.GetCoreIdentityNames().Select(n => n.NameParts.Select(part => part.Value).ToArray()).ToArray(), + verifiedDatesOfBirth: ticket.Principal.GetCoreIdentityBirthDates().Select(d => d.Value).ToArray()); } }); if (ShowDebugPages) { - return Results.Redirect(linkGenerator.DebugIdentity(journeyInstance.InstanceId)); + return Results.Redirect(LinkGenerator.DebugIdentity(journeyInstance.InstanceId)); } return await CreateOrUpdateOneLoginUser(journeyInstance); @@ -122,10 +125,10 @@ public async Task CreateOrUpdateOneLoginUser(JourneyInstance - linkGenerator.NationalInsuranceNumber(journeyInstance.InstanceId), + LinkGenerator.Connect(journeyInstance.InstanceId), // Authenticated with OneLogin, identity verification failed - { OneLoginAuthenticationTicket: not null, IdentityVerified: false } => linkGenerator.NotVerified(journeyInstance.InstanceId), + { OneLoginAuthenticationTicket: not null, IdentityVerified: false } => LinkGenerator.NotVerified(journeyInstance.InstanceId), _ => throw new InvalidOperationException("Cannot determine next page.") }; diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs index 9a9479948..3dea9bda2 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs @@ -33,19 +33,26 @@ public class SignInJourneyState( public bool AttemptedIdentityVerification { get; set; } - public bool IdentityVerified { get; set; } + [JsonInclude] + public bool IdentityVerified { get; private set; } - public string[][]? VerifiedNames { get; set; } + [JsonInclude] + public string[][]? VerifiedNames { get; private set; } - public DateOnly[]? VerifiedDatesOfBirth { get; set; } + [JsonInclude] + public DateOnly[]? VerifiedDatesOfBirth { get; private set; } - public string? NationalInsuranceNumber { get; set; } + [JsonInclude] + public bool? HaveNationalInsuranceNumber { get; private set; } - public bool NationalInsuranceNumberSpecified { get; set; } + [JsonInclude] + public string? NationalInsuranceNumber { get; private set; } - public string? Trn { get; set; } + [JsonInclude] + public bool? HaveTrn { get; private set; } - public bool TrnSpecified { get; set; } + [JsonInclude] + public string? Trn { get; private set; } public void Reset() { @@ -54,6 +61,43 @@ public void Reset() VerifiedNames = null; VerifiedDatesOfBirth = null; } + + public void SetVerified(string[][] verifiedNames, DateOnly[] verifiedDatesOfBirth) + { + IdentityVerified = true; + VerifiedNames = verifiedNames; + VerifiedDatesOfBirth = verifiedDatesOfBirth; + } + + // Used by the Debug page + public void ClearVerified() + { + IdentityVerified = false; + VerifiedNames = null; + VerifiedDatesOfBirth = null; + } + + public void SetNationalInsuranceNumber(bool haveNationalInsuranceNumber, string? nationalInsuranceNumber) + { + if (haveNationalInsuranceNumber && nationalInsuranceNumber is null) + { + throw new ArgumentException("National Insurance number must be specified.", nameof(nationalInsuranceNumber)); + } + + HaveNationalInsuranceNumber = haveNationalInsuranceNumber; + NationalInsuranceNumber = haveNationalInsuranceNumber ? nationalInsuranceNumber! : null; + } + + public void SetTrn(bool haveTrn, string? trn) + { + if (haveTrn && trn is null) + { + throw new ArgumentException("TRN must be specified.", nameof(trn)); + } + + HaveTrn = haveTrn; + Trn = haveTrn ? trn! : null; + } } public class AuthenticationTicketJsonConverter : JsonConverter diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/Components/interruption-card.scss b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/Components/interruption-card.scss new file mode 100644 index 000000000..5839d6173 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/Components/interruption-card.scss @@ -0,0 +1,36 @@ +// This is borrowed from https://github.com/DFE-Digital/claim-additional-payments-for-teaching/blob/master/app/assets/stylesheets/components/panel.scss +// until it's rolled into the design system fully (https://github.com/alphagov/govuk-design-system-backlog/issues/27) + +.govuk-panel--interruption { + background-color: $govuk-brand-colour; + text-align: left; + + * { + color: govuk-colour("white"); + } + + .govuk-radios__label::before, + & ::after { + color: govuk-colour("black"); + border-color: govuk-colour("black"); + background-color: govuk-colour("white"); + } + + .govuk-list--definition { + dt, + dd { + color: govuk-colour("blue"); + } + } + + .govuk-button, + .govuk-button:disabled:hover { + color: $govuk-brand-colour; + background-color: govuk-colour("white"); + box-shadow: 0 2px 0 govuk-colour("dark-blue"); + } + + .govuk-button:hover { + background-color: govuk-colour("light-grey"); + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/site.scss b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/site.scss index 486345a02..3a5e87c8a 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/site.scss +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/wwwroot/Styles/site.scss @@ -1,2 +1,22 @@ @import '../lib/govuk-frontend/govuk/all.scss'; @import './service-header-no-imports.scss'; +@import './Components/interruption-card.scss'; + +.trs-panel--left { + text-align: left; + + .govuk-body, .govuk-body-l { + color: govuk-colour("white"); + } + + .govuk-button, + .govuk-button:disabled:hover { + color: $govuk-text-colour; + background-color: govuk-colour("white"); + box-shadow: 0 2px 0 govuk-colour("dark-grey"); + } + + .govuk-button:hover { + background-color: govuk-colour("light-grey"); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs index eae13682b..b7867433b 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs @@ -32,15 +32,56 @@ public async Task SignIn_UnknownVerifiedUser_MatchesWithNino() await page.GoToTestStartPage(); + await page.WaitForUrlPathAsync("/connect"); + await page.ClickButton("Connect to your teaching record"); + await page.WaitForUrlPathAsync("/national-insurance-number"); - await page.FillAsync("text=What is your National Insurance number?", person.NationalInsuranceNumber!); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('National Insurance number')", person.NationalInsuranceNumber!); await page.ClickButton("Continue"); + await page.WaitForUrlPathAsync("/found"); + await page.ClickButton("Access your teaching record"); + + await page.AssertSignedIn(person.Trn!); + } + + [Fact] + public async Task SignIn_UnknownVerifiedUserWithUnmatchedNino_MatchesWithTrn() + { + var person = await TestData.CreatePerson(x => x.WithTrn().WithNationalInsuranceNumber(false)); + + var subject = TestData.CreateOneLoginUserSubject(); + var email = Faker.Internet.Email(); + var coreIdentityVc = TestData.CreateOneLoginCoreIdentityVc(person.FirstName, person.LastName, person.DateOfBirth); + SetCurrentOneLoginUser(OneLoginUserInfo.Create(subject, email, coreIdentityVc)); + + await using var context = await HostFixture.CreateBrowserContext(); + var page = await context.NewPageAsync(); + + await page.GoToTestStartPage(); + + await page.WaitForUrlPathAsync("/connect"); + await page.ClickButton("Connect to your teaching record"); + + await page.WaitForUrlPathAsync("/national-insurance-number"); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('National Insurance number')", TestData.GenerateNationalInsuranceNumber()); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/trn"); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('TRN')", person.Trn!); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/found"); + await page.ClickButton("Access your teaching record"); + await page.AssertSignedIn(person.Trn!); } [Fact] - public async Task SignIn_UnknownVerifiedUser_MatchesWithTrn() + public async Task SignIn_UnknownVerifiedUserWithoutNino_MatchesWithTrn() { var person = await TestData.CreatePerson(x => x.WithTrn().WithNationalInsuranceNumber(false)); @@ -54,14 +95,21 @@ public async Task SignIn_UnknownVerifiedUser_MatchesWithTrn() await page.GoToTestStartPage(); + await page.WaitForUrlPathAsync("/connect"); + await page.ClickButton("Connect to your teaching record"); + await page.WaitForUrlPathAsync("/national-insurance-number"); - await page.FillAsync("text=What is your National Insurance number?", Faker.Identification.UkNationalInsuranceNumber()); + await page.CheckAsync("text=No"); await page.ClickButton("Continue"); await page.WaitForUrlPathAsync("/trn"); - await page.FillAsync("label:text-is('Teacher reference number (TRN)')", person.Trn!); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('TRN')", person.Trn!); await page.ClickButton("Continue"); + await page.WaitForUrlPathAsync("/found"); + await page.ClickButton("Access your teaching record"); + await page.AssertSignedIn(person.Trn!); } @@ -80,41 +128,65 @@ public async Task SignIn_UnknownVerifiedUser_DoesNotMatchWithNinoOrTrn() await page.GoToTestStartPage(); + await page.WaitForUrlPathAsync("/connect"); + await page.ClickButton("Connect to your teaching record"); + await page.WaitForUrlPathAsync("/national-insurance-number"); - await page.FillAsync("text=What is your National Insurance number?", Faker.Identification.UkNationalInsuranceNumber()); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('National Insurance number')", TestData.GenerateNationalInsuranceNumber()); await page.ClickButton("Continue"); await page.WaitForUrlPathAsync("/trn"); - await page.FillAsync("label:text-is('Teacher reference number (TRN)')", await TestData.GenerateTrn()); + await page.CheckAsync("text=Yes"); + await page.FillAsync("label:text-is('TRN')", await TestData.GenerateTrn()); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/check-answers"); await page.ClickButton("Continue"); await page.WaitForUrlPathAsync("/not-found"); } [Fact] - public async Task SignIn_KnownUser() + public async Task SignIn_UnknownVerifiedUserWithNeitherNinoNorTrn_DoesNotMatch() { - var person = await TestData.CreatePerson(x => x.WithTrn()); - var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + var person = await TestData.CreatePerson(x => x.WithTrn().WithNationalInsuranceNumber(false)); + var subject = TestData.CreateOneLoginUserSubject(); + var email = Faker.Internet.Email(); var coreIdentityVc = TestData.CreateOneLoginCoreIdentityVc(person.FirstName, person.LastName, person.DateOfBirth); - SetCurrentOneLoginUser(OneLoginUserInfo.Create(oneLoginUser.Subject, oneLoginUser.Email, coreIdentityVc)); + SetCurrentOneLoginUser(OneLoginUserInfo.Create(subject, email, coreIdentityVc)); await using var context = await HostFixture.CreateBrowserContext(); var page = await context.NewPageAsync(); await page.GoToTestStartPage(); - await page.AssertSignedIn(person.Trn!); + await page.WaitForUrlPathAsync("/connect"); + await page.ClickButton("Connect to your teaching record"); + + await page.WaitForUrlPathAsync("/national-insurance-number"); + await page.CheckAsync("text=No"); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/trn"); + await page.CheckAsync("text=No"); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/check-answers"); + await page.ClickButton("Continue"); + + await page.WaitForUrlPathAsync("/not-found"); } [Fact] - public async Task SignIn_KnownButUnverifiedUser() + public async Task SignIn_KnownUser() { var person = await TestData.CreatePerson(x => x.WithTrn()); var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); - SetCurrentOneLoginUser(OneLoginUserInfo.Create(oneLoginUser.Subject, oneLoginUser.Email)); + var coreIdentityVc = TestData.CreateOneLoginCoreIdentityVc(person.FirstName, person.LastName, person.DateOfBirth); + SetCurrentOneLoginUser(OneLoginUserInfo.Create(oneLoginUser.Subject, oneLoginUser.Email, coreIdentityVc)); await using var context = await HostFixture.CreateBrowserContext(); var page = await context.NewPageAsync(); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs new file mode 100644 index 000000000..9bf1f0189 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs @@ -0,0 +1,283 @@ +using System.Diagnostics; + +namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests; + +public class CheckAnswersTests(HostFixture hostFixture) : TestBase(hostFixture) +{ + [Fact] + public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalInsuranceNumberPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_TrnNotSpecified_RedirectsToTrnPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/trn?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_ValidRequest_RendersExpectedContent() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var trn = await TestData.GenerateTrn(); + + await journeyInstance.UpdateStateAsync(state => + { + state.SetNationalInsuranceNumber(true, nationalInsuranceNumber); + state.SetTrn(true, trn); + }); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + var doc = await AssertEx.HtmlResponse(response); + Assert.Equal(nationalInsuranceNumber, doc.GetSummaryListValueForKey("National Insurance number")); + Assert.Equal(trn, doc.GetSummaryListValueForKey("Teacher reference number")); + } + + [Fact] + public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Post_NationalInsuranceNumberNotSpecified_RedirectsToNationalInsuranceNumberPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_TrnNotSpecified_RedirectsToTrnPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/trn?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_ValidRequests_RedirectsToNotFoundPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var trn = await TestData.GenerateTrn(); + + await journeyInstance.UpdateStateAsync(state => + { + state.SetNationalInsuranceNumber(true, nationalInsuranceNumber); + state.SetTrn(true, trn); + }); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs new file mode 100644 index 000000000..48e913e6c --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs @@ -0,0 +1,170 @@ +namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests; + +public class ConnectTests(HostFixture hostFixture) : TestBase(hostFixture) +{ + [Fact] + public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_ValidRequest_RendersExpectedContent() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponse(response); + } + [Fact] + public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); + } + + [Fact] + public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_ValidRequest_RedirectsToStartOfMatchingJourney() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs new file mode 100644 index 000000000..8ff1de224 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs @@ -0,0 +1,105 @@ +using System.Diagnostics; + +namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests; + +public class FoundTests(HostFixture hostFixture) : TestBase(hostFixture) +{ + [Fact] + public async Task Get_NotAuthenticated_RedirectsToStartOfMatchingJourney() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + Debug.Assert(state.AuthenticationTicket is null); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/connect?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_ValidRequest_RendersExpectedContent() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponse(response); + } + + [Fact] + public async Task Post_NotAuthenticated_RedirectsToStartOfMatchingJourney() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + Debug.Assert(state.AuthenticationTicket is null); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/connect?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_ValidRequest_RedirectsToStateRedirectUri() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn()); + var oneLoginUser = await TestData.CreateOneLoginUser(person.PersonId); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs index beafff133..a88e4e818 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs @@ -6,7 +6,7 @@ public class NationalInsuranceNumberTests(HostFixture hostFixture) : TestBase(ho public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var request = new HttpRequestMessage(HttpMethod.Get, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -22,7 +22,7 @@ public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); @@ -41,8 +41,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn()); @@ -64,7 +63,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); } [Theory] @@ -73,7 +72,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValueInState) { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); @@ -82,11 +81,7 @@ public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValue var existingNationalInsuranceNumber = haveExistingValueInState ? Faker.Identification.UkNationalInsuranceNumber() : null; if (existingNationalInsuranceNumber is not null) { - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumber = existingNationalInsuranceNumber; - state.NationalInsuranceNumberSpecified = true; - }); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, existingNationalInsuranceNumber)); } var request = new HttpRequestMessage(HttpMethod.Get, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -103,7 +98,7 @@ await journeyInstance.UpdateStateAsync(state => public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); @@ -127,7 +122,7 @@ public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); @@ -154,8 +149,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -184,14 +178,47 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_HaveNationalInsuranceNumberNotAnswered_RendersError() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); + var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContentBuilder + { + } + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "HaveNationalInsuranceNumber", "Select yes if you have a National Insurance number"); } [Fact] public async Task Post_EmptyNationalInsuranceNumber_RendersError() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -211,6 +238,7 @@ public async Task Post_EmptyNationalInsuranceNumber_RendersError() { Content = new FormUrlEncodedContentBuilder { + { "HaveNationalInsuranceNumber", bool.TrueString }, { "NationalInsuranceNumber", nationalInsuranceNumber } } }; @@ -226,7 +254,7 @@ public async Task Post_EmptyNationalInsuranceNumber_RendersError() public async Task Post_InvalidNationalInsuranceNumber_RendersError() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -246,6 +274,7 @@ public async Task Post_InvalidNationalInsuranceNumber_RendersError() { Content = new FormUrlEncodedContentBuilder { + { "HaveNationalInsuranceNumber", bool.TrueString }, { "NationalInsuranceNumber", nationalInsuranceNumber } } }; @@ -258,14 +287,13 @@ public async Task Post_InvalidNationalInsuranceNumber_RendersError() } [Fact] - public async Task Post_ValidNationalInsuranceNumberButLookupFailed_UpdatesStateAndRedirectsToTrnPage() + public async Task Post_NoNationalInsuranceNumberSpecified_UpdatesStateAndRedirectsToTrnPage() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); - var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); var ticket = CreateOneLoginAuthenticationTicket( @@ -281,7 +309,7 @@ public async Task Post_ValidNationalInsuranceNumberButLookupFailed_UpdatesStateA { Content = new FormUrlEncodedContentBuilder { - { "NationalInsuranceNumber", nationalInsuranceNumber } + { "HaveNationalInsuranceNumber", bool.FalseString } } }; @@ -294,21 +322,19 @@ public async Task Post_ValidNationalInsuranceNumberButLookupFailed_UpdatesStateA journeyInstance = await ReloadJourneyInstance(journeyInstance); state = journeyInstance.State; - Assert.Equal(nationalInsuranceNumber, state.NationalInsuranceNumber); - Assert.True(state.NationalInsuranceNumberSpecified); + Assert.False(state.HaveNationalInsuranceNumber); Assert.Null(state.AuthenticationTicket); } [Fact] - public async Task Post_ValidNationalInsuranceNumberAndLookupSucceeded_UpdatesStateUpdatesOneLoginUserCompletesAuthenticationAndRedirectsToStateRedirectUri() + public async Task Post_ValidNationalInsuranceNumberButLookupFailed_UpdatesStateAndRedirectsToTrnPage() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); - var nationalInsuranceNumber = person.NationalInsuranceNumber!; + var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); var ticket = CreateOneLoginAuthenticationTicket( @@ -324,6 +350,7 @@ public async Task Post_ValidNationalInsuranceNumberAndLookupSucceeded_UpdatesSta { Content = new FormUrlEncodedContentBuilder { + { "HaveNationalInsuranceNumber", bool.TrueString }, { "NationalInsuranceNumber", nationalInsuranceNumber } } }; @@ -333,26 +360,20 @@ public async Task Post_ValidNationalInsuranceNumberAndLookupSucceeded_UpdatesSta // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"/trn?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); journeyInstance = await ReloadJourneyInstance(journeyInstance); state = journeyInstance.State; + Assert.True(state.HaveNationalInsuranceNumber); Assert.Equal(nationalInsuranceNumber, state.NationalInsuranceNumber); - Assert.True(state.NationalInsuranceNumberSpecified); - Assert.NotNull(state.AuthenticationTicket); - - oneLoginUser = await WithDbContext(dbContext => dbContext.OneLoginUsers.SingleAsync(u => u.Subject == oneLoginUser.Subject)); - Assert.Equal(Clock.UtcNow, oneLoginUser.FirstSignIn); - Assert.Equal(Clock.UtcNow, oneLoginUser.LastSignIn); - Assert.Equal(person.PersonId, oneLoginUser.PersonId); + Assert.Null(state.AuthenticationTicket); } [Fact] - public async Task Post_ContinueWithout_UpdatesStateAndRedirectsToTrnPage() + public async Task Post_ValidNationalInsuranceNumberAndLookupSucceeded_UpdatesStateUpdatesOneLoginUserCompletesAuthenticationAndRedirectsToFoundPage() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -368,18 +389,31 @@ public async Task Post_ContinueWithout_UpdatesStateAndRedirectsToTrnPage() dateOfBirth: person.DateOfBirth); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - var request = new HttpRequestMessage(HttpMethod.Post, $"/national-insurance-number/ContinueWithout?{journeyInstance.GetUniqueIdQueryParameter()}"); + var request = new HttpRequestMessage(HttpMethod.Post, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContentBuilder + { + { "HaveNationalInsuranceNumber", bool.TrueString }, + { "NationalInsuranceNumber", nationalInsuranceNumber } + } + }; // Act var response = await HttpClient.SendAsync(request); // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"/trn?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"/found?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); journeyInstance = await ReloadJourneyInstance(journeyInstance); state = journeyInstance.State; - Assert.Null(state.NationalInsuranceNumber); - Assert.True(state.NationalInsuranceNumberSpecified); + Assert.True(state.HaveNationalInsuranceNumber); + Assert.Equal(nationalInsuranceNumber, state.NationalInsuranceNumber); + Assert.NotNull(state.AuthenticationTicket); + + oneLoginUser = await WithDbContext(dbContext => dbContext.OneLoginUsers.SingleAsync(u => u.Subject == oneLoginUser.Subject)); + Assert.Equal(Clock.UtcNow, oneLoginUser.FirstSignIn); + Assert.Equal(Clock.UtcNow, oneLoginUser.LastSignIn); + Assert.Equal(person.PersonId, oneLoginUser.PersonId); } } diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs index 42bb22d83..82bf7ccd0 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests; public class NotFoundTests(HostFixture hostFixture) : TestBase(hostFixture) @@ -6,7 +8,7 @@ public class NotFoundTests(HostFixture hostFixture) : TestBase(hostFixture) public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -22,7 +24,7 @@ public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); @@ -41,8 +43,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn()); @@ -64,23 +65,20 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); } [Fact] - public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToStartOfMatchingQuestions() + public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalInsuranceNumberPage() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumberSpecified = false; - }); + Debug.Assert(state.NationalInsuranceNumber is null); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -93,20 +91,17 @@ await journeyInstance.UpdateStateAsync(state => } [Fact] - public async Task Get_TrnNotSpecified_RedirectsToStartOfMatchingQuestions() + public async Task Get_TrnNotSpecified_RedirectsToTrnPage() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumberSpecified = true; - state.TrnSpecified = false; - }); + Debug.Assert(state.NationalInsuranceNumber is null); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -115,14 +110,14 @@ await journeyInstance.UpdateStateAsync(state => // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"/trn?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); } [Fact] public async Task Get_ValidRequest_RendersExpectedContent() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); @@ -130,9 +125,8 @@ public async Task Get_ValidRequest_RendersExpectedContent() await journeyInstance.UpdateStateAsync(async state => { - state.NationalInsuranceNumberSpecified = true; - state.Trn = await TestData.GenerateTrn(); - state.TrnSpecified = true; + state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber()); + state.SetTrn(true, await TestData.GenerateTrn()); }); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs index 35646820b..022ae2c25 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs @@ -6,7 +6,7 @@ public class NotVerifiedTests(HostFixture hostFixture) : TestBase(hostFixture) public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-verified?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -22,7 +22,7 @@ public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Get_VerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: true); @@ -41,7 +41,7 @@ public async Task Get_VerifiedWithOneLogin_ReturnsBadRequest() public async Task Get_ValidRequest_ReturnsExpectedContent() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs index 75e7be405..d18a5e188 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests; public class TrnTests(HostFixture hostFixture) : TestBase(hostFixture) @@ -6,7 +8,7 @@ public class TrnTests(HostFixture hostFixture) : TestBase(hostFixture) public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var request = new HttpRequestMessage(HttpMethod.Get, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -22,7 +24,7 @@ public async Task Get_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, createCoreIdentityVc: false); @@ -41,8 +43,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn()); @@ -64,7 +65,29 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalInsuranceNumberPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); } [Theory] @@ -73,7 +96,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValueInState) { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr); @@ -83,12 +106,11 @@ public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValue await journeyInstance.UpdateStateAsync(state => { - state.NationalInsuranceNumberSpecified = true; + state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber()); if (existingTrn is not null) { - state.Trn = existingTrn; - state.TrnSpecified = true; + state.SetTrn(true, existingTrn); } }); @@ -106,10 +128,10 @@ await journeyInstance.UpdateStateAsync(state => public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); - await journeyInstance.UpdateStateAsync(state => state.NationalInsuranceNumberSpecified = true); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var trn = await TestData.GenerateTrn(); @@ -117,6 +139,7 @@ public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -132,7 +155,7 @@ public async Task Post_NotAuthenticatedWithOneLogin_ReturnsBadRequest() public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var trn = await TestData.GenerateTrn(); @@ -144,6 +167,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -159,8 +183,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -180,6 +203,7 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -189,14 +213,88 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_NationalInsuranceNumberNotSpecified_RedirectsToNationalInsuranceNumberPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); + var trn = person.Trn!; + var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + Debug.Assert(state.NationalInsuranceNumber is null); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContentBuilder + { + { "HaveTrn", bool.TrueString }, + { "Trn", trn } + } + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task Post_HaveTrnNotAnswered_RendersError() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); + var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContentBuilder + { + } + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + await AssertEx.HtmlResponseHasError(response, "HaveTrn", "Select yes if you have a TRN"); } [Fact] public async Task Post_EmptyTrn_RendersError() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -212,12 +310,13 @@ public async Task Post_EmptyTrn_RendersError() dateOfBirth: person.DateOfBirth); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => state.NationalInsuranceNumberSpecified = true); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -233,7 +332,7 @@ public async Task Post_EmptyTrn_RendersError() public async Task Post_InvalidTrn_RendersError() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -249,12 +348,13 @@ public async Task Post_InvalidTrn_RendersError() dateOfBirth: person.DateOfBirth); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => state.NationalInsuranceNumberSpecified = true); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -267,10 +367,54 @@ public async Task Post_InvalidTrn_RendersError() } [Fact] - public async Task Post_ValidTrnButLookupFailed_UpdatesStateAndRedirectsToNotFoundPage() + public async Task Post_NoTrnSpecified_UpdatesStateAndRedirectsToCheckAnswersPage() + { + // Arrange + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + + var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); + var trn = await TestData.GenerateTrn(); + var oneLoginUser = await TestData.CreateOneLoginUser(personId: null); + + var ticket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: oneLoginUser.Subject, + email: oneLoginUser.Email, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth); + await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContentBuilder + { + { "HaveTrn", bool.FalseString } + } + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + + journeyInstance = await ReloadJourneyInstance(journeyInstance); + state = journeyInstance.State; + Assert.False(state.HaveTrn); + Assert.Null(state.Trn); + Assert.Null(state.AuthenticationTicket); + } + + [Fact] + public async Task Post_ValidTrnButLookupFailed_UpdatesStateAndRedirectsToCheckAnswersPage() { // Arrange - var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -286,12 +430,13 @@ public async Task Post_ValidTrnButLookupFailed_UpdatesStateAndRedirectsToNotFoun dateOfBirth: person.DateOfBirth); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => state.NationalInsuranceNumberSpecified = true); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -301,21 +446,20 @@ public async Task Post_ValidTrnButLookupFailed_UpdatesStateAndRedirectsToNotFoun // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); journeyInstance = await ReloadJourneyInstance(journeyInstance); state = journeyInstance.State; + Assert.True(state.HaveTrn); Assert.Equal(trn, state.Trn); - Assert.True(state.TrnSpecified); Assert.Null(state.AuthenticationTicket); } [Fact] - public async Task Post_ValidTrnAndLookupSucceeded_UpdatesStateUpdatesOneLoginUserCompletesAuthenticationAndRedirectsToStateRedirectUri() + public async Task Post_ValidTrnAndLookupSucceeded_UpdatesStateUpdatesOneLoginUserCompletesAuthenticationAndRedirectsToFoundPage() { // Arrange - var redirectUri = "/"; - var state = new SignInJourneyState(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); + var state = CreateNewState(); var journeyInstance = await CreateJourneyInstance(state); var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber()); @@ -331,12 +475,13 @@ public async Task Post_ValidTrnAndLookupSucceeded_UpdatesStateUpdatesOneLoginUse dateOfBirth: person.DateOfBirth); await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => state.NationalInsuranceNumberSpecified = true); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") { Content = new FormUrlEncodedContentBuilder { + { "HaveTrn", bool.TrueString }, { "Trn", trn } } }; @@ -346,12 +491,12 @@ public async Task Post_ValidTrnAndLookupSucceeded_UpdatesStateUpdatesOneLoginUse // Assert Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); - Assert.Equal($"{redirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + Assert.Equal($"/found?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); journeyInstance = await ReloadJourneyInstance(journeyInstance); state = journeyInstance.State; + Assert.True(state.HaveTrn); Assert.Equal(trn, state.Trn); - Assert.True(state.TrnSpecified); Assert.NotNull(state.AuthenticationTicket); oneLoginUser = await WithDbContext(dbContext => dbContext.OneLoginUsers.SingleAsync(u => u.Subject == oneLoginUser.Subject)); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs index 28a828b92..042b5bf63 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs @@ -171,7 +171,7 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationSuc Assert.NotEqual(Clock.UtcNow, user.LastSignIn); var redirectResult = Assert.IsType(result); - Assert.Equal($"/NationalInsuranceNumber?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); }); [Fact] @@ -189,11 +189,7 @@ public Task TryMatchToTeachingRecord_MatchesZeroResults_ReturnsFalseAndDoesNotSe var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, sub: subject, createCoreIdentityVc: true); await helper.OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); - state.NationalInsuranceNumberSpecified = true; - }); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, Faker.Identification.UkNationalInsuranceNumber())); personSearchServiceMock .Setup(mock => mock.Search( @@ -233,11 +229,7 @@ public Task TryMatchToTeachingRecord_MatchesMultipleResults_ReturnsFalseAndDoesN var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, sub: subject, createCoreIdentityVc: true); await helper.OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumber = person1.NationalInsuranceNumber; - state.NationalInsuranceNumberSpecified = true; - }); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person1.NationalInsuranceNumber)); personSearchServiceMock .Setup(mock => mock.Search( @@ -298,11 +290,7 @@ public Task TryMatchToTeachingRecord_MatchesSingleResultWithoutTrn_ReturnsFalseA var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, sub: subject, createCoreIdentityVc: true); await helper.OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumber = person.NationalInsuranceNumber; - state.NationalInsuranceNumberSpecified = true; - }); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person.NationalInsuranceNumber)); personSearchServiceMock .Setup(mock => mock.Search( @@ -353,11 +341,7 @@ public Task TryMatchToTeachingRecord_MatchesSingleResult_ReturnsTrueAndUpdatesOn var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, sub: subject, createCoreIdentityVc: true); await helper.OnSignedInWithOneLogin(journeyInstance, ticket); - await journeyInstance.UpdateStateAsync(state => - { - state.NationalInsuranceNumber = person.NationalInsuranceNumber; - state.NationalInsuranceNumberSpecified = true; - }); + await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person.NationalInsuranceNumber)); personSearchServiceMock .Setup(mock => mock.Search( diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TestBase.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TestBase.cs index 88f2867ab..675c2aedd 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TestBase.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TestBase.cs @@ -145,4 +145,7 @@ public AuthenticationTicket CreateOneLoginAuthenticationTicket( return new AuthenticationTicket(principal, properties, authenticationScheme: "OneLogin"); } + + public SignInJourneyState CreateNewState(string redirectUri = "/") => + new(redirectUri, serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy"); }