diff --git a/internal/voucher/path.go b/internal/voucher/path.go index 9932e70172..7397008f9f 100644 --- a/internal/voucher/path.go +++ b/internal/voucher/path.go @@ -47,7 +47,7 @@ func (p Path) Redirect(w http.ResponseWriter, r *http.Request, appData appcontex func (p Path) CanGoTo(provided *voucherdata.Provided) bool { switch p { case PathYourName: - return !provided.Tasks.ConfirmYourIdentity.IsCompleted() + return provided.Tasks.ConfirmYourIdentity.IsNotStarted() case PathVerifyDonorDetails: return provided.Tasks.ConfirmYourName.IsCompleted() && diff --git a/internal/voucher/voucherpage/confirm_allowed_to_vouch.go b/internal/voucher/voucherpage/confirm_allowed_to_vouch.go index 7228bbc058..09dcfa4497 100644 --- a/internal/voucher/voucherpage/confirm_allowed_to_vouch.go +++ b/internal/voucher/voucherpage/confirm_allowed_to_vouch.go @@ -3,6 +3,7 @@ package voucherpage import ( "errors" "net/http" + "strings" "github.com/ministryofjustice/opg-go-common/template" "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" @@ -20,6 +21,7 @@ type confirmAllowedToVouchData struct { Form *form.YesNoForm Lpa *lpadata.Lpa SurnameMatchesDonor bool + MatchIdentity bool } func ConfirmAllowedToVouch(tmpl template.Template, lpaStoreResolvingService LpaStoreResolvingService, voucherStore VoucherStore) Handler { @@ -33,7 +35,8 @@ func ConfirmAllowedToVouch(tmpl template.Template, lpaStoreResolvingService LpaS App: appData, Form: form.NewYesNoForm(form.YesNoUnknown), Lpa: lpa, - SurnameMatchesDonor: provided.LastName == lpa.Donor.LastName, + SurnameMatchesDonor: strings.EqualFold(provided.LastName, lpa.Donor.LastName), + MatchIdentity: provided.Tasks.ConfirmYourIdentity.IsInProgress(), } if r.Method == http.MethodPost { @@ -45,7 +48,12 @@ func ConfirmAllowedToVouch(tmpl template.Template, lpaStoreResolvingService LpaS return errors.New("// TODO there should be a page here but it hasn't been built yet") } - provided.Tasks.ConfirmYourName = task.StateCompleted + if provided.Tasks.ConfirmYourIdentity.IsInProgress() { + provided.Tasks.ConfirmYourIdentity = task.StateCompleted + } else { + provided.Tasks.ConfirmYourName = task.StateCompleted + } + if err := voucherStore.Put(r.Context(), provided); err != nil { return err } diff --git a/internal/voucher/voucherpage/confirm_allowed_to_vouch_test.go b/internal/voucher/voucherpage/confirm_allowed_to_vouch_test.go index 8850b206c8..765ee95e77 100644 --- a/internal/voucher/voucherpage/confirm_allowed_to_vouch_test.go +++ b/internal/voucher/voucherpage/confirm_allowed_to_vouch_test.go @@ -18,33 +18,96 @@ import ( ) func TestGetConfirmAllowedToVouch(t *testing.T) { - w := httptest.NewRecorder() - r, _ := http.NewRequest(http.MethodGet, "/", nil) - - lpaStoreResolvingService := newMockLpaStoreResolvingService(t) - lpaStoreResolvingService.EXPECT(). - Get(r.Context()). - Return(&lpadata.Lpa{ - Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, - }, nil) - - template := newMockTemplate(t) - template.EXPECT(). - Execute(w, &confirmAllowedToVouchData{ - App: testAppData, - Form: form.NewYesNoForm(form.YesNoUnknown), - Lpa: &lpadata.Lpa{ + testcases := map[string]struct { + lpa *lpadata.Lpa + provided *voucherdata.Provided + data *confirmAllowedToVouchData + }{ + "actor matches": { + lpa: &lpadata.Lpa{ Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, }, - SurnameMatchesDonor: true, - }). - Return(nil) + provided: &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "V", + LastName: "W", + }, + data: &confirmAllowedToVouchData{ + App: testAppData, + Form: form.NewYesNoForm(form.YesNoUnknown), + Lpa: &lpadata.Lpa{ + Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, + }, + }, + }, + "surname matches donor": { + lpa: &lpadata.Lpa{ + Donor: lpadata.Donor{FirstNames: "A", LastName: "W"}, + Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, + }, + provided: &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "V", + LastName: "W", + }, + data: &confirmAllowedToVouchData{ + App: testAppData, + Form: form.NewYesNoForm(form.YesNoUnknown), + Lpa: &lpadata.Lpa{ + Donor: lpadata.Donor{FirstNames: "A", LastName: "W"}, + Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, + }, + SurnameMatchesDonor: true, + }, + }, + "matches actor after identity": { + lpa: &lpadata.Lpa{ + Donor: lpadata.Donor{FirstNames: "A", LastName: "W"}, + Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, + }, + provided: &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "V", + LastName: "W", + Tasks: voucherdata.Tasks{ + ConfirmYourIdentity: task.StateInProgress, + }, + }, + data: &confirmAllowedToVouchData{ + App: testAppData, + Form: form.NewYesNoForm(form.YesNoUnknown), + Lpa: &lpadata.Lpa{ + Donor: lpadata.Donor{FirstNames: "A", LastName: "W"}, + Voucher: lpadata.Voucher{FirstNames: "V", LastName: "W"}, + }, + SurnameMatchesDonor: true, + MatchIdentity: true, + }, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) - err := ConfirmAllowedToVouch(template.Execute, lpaStoreResolvingService, nil)(testAppData, w, r, &voucherdata.Provided{LpaID: "lpa-id"}) - resp := w.Result() + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(tc.lpa, nil) - assert.Nil(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, tc.data). + Return(nil) + + err := ConfirmAllowedToVouch(template.Execute, lpaStoreResolvingService, nil)(testAppData, w, r, tc.provided) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + } } func TestGetConfirmAllowedToVouchWhenLpaStoreResolvingServiceErrors(t *testing.T) { @@ -83,33 +146,39 @@ func TestGetConfirmAllowedToVouchWhenTemplateErrors(t *testing.T) { } func TestPostConfirmAllowedToVouch(t *testing.T) { - f := url.Values{ - form.FieldNames.YesNo: {form.Yes.String()}, + testcases := map[task.State]voucherdata.Tasks{ + task.StateNotStarted: voucherdata.Tasks{ConfirmYourName: task.StateCompleted}, + task.StateInProgress: voucherdata.Tasks{ConfirmYourIdentity: task.StateCompleted}, } - w := httptest.NewRecorder() - r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) - r.Header.Add("Content-Type", page.FormUrlEncoded) - - lpaStoreResolvingService := newMockLpaStoreResolvingService(t) - lpaStoreResolvingService.EXPECT(). - Get(r.Context()). - Return(&lpadata.Lpa{Donor: lpadata.Donor{LastName: "Smith"}}, nil) - - voucherStore := newMockVoucherStore(t) - voucherStore.EXPECT(). - Put(r.Context(), &voucherdata.Provided{ - LpaID: "lpa-id", - Tasks: voucherdata.Tasks{ConfirmYourName: task.StateCompleted}, - }). - Return(nil) - - err := ConfirmAllowedToVouch(nil, lpaStoreResolvingService, voucherStore)(testAppData, w, r, &voucherdata.Provided{LpaID: "lpa-id"}) - resp := w.Result() - - assert.Nil(t, err) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, voucher.PathTaskList.Format("lpa-id"), resp.Header.Get("Location")) + for taskState, tasks := range testcases { + t.Run(taskState.String(), func(t *testing.T) { + f := url.Values{ + form.FieldNames.YesNo: {form.Yes.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(&lpadata.Lpa{Donor: lpadata.Donor{LastName: "Smith"}}, nil) + + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Put(r.Context(), &voucherdata.Provided{LpaID: "lpa-id", Tasks: tasks}). + Return(nil) + + err := ConfirmAllowedToVouch(nil, lpaStoreResolvingService, voucherStore)(testAppData, w, r, &voucherdata.Provided{LpaID: "lpa-id", Tasks: voucherdata.Tasks{ConfirmYourIdentity: taskState}}) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, voucher.PathTaskList.Format("lpa-id"), resp.Header.Get("Location")) + }) + } } func TestPostConfirmAllowedToVouchWhenNo(t *testing.T) { diff --git a/internal/voucher/voucherpage/confirm_your_name.go b/internal/voucher/voucherpage/confirm_your_name.go index e7d43b6c71..fe8b9cbd2b 100644 --- a/internal/voucher/voucherpage/confirm_your_name.go +++ b/internal/voucher/voucherpage/confirm_your_name.go @@ -2,6 +2,7 @@ package voucherpage import ( "net/http" + "strings" "github.com/ministryofjustice/opg-go-common/template" "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" @@ -45,7 +46,8 @@ func ConfirmYourName(tmpl template.Template, lpaStoreResolvingService LpaStoreRe provided.FirstNames = firstNames provided.LastName = lastName - if lastName == lpa.Donor.LastName || !provided.NameMatches(lpa).IsNone() { + if !provided.Tasks.ConfirmYourName.IsCompleted() && + (strings.EqualFold(lastName, lpa.Donor.LastName) || !provided.NameMatches(lpa).IsNone()) { redirect = voucher.PathConfirmAllowedToVouch state = task.StateInProgress } diff --git a/internal/voucher/voucherpage/identity_with_one_login_callback.go b/internal/voucher/voucherpage/identity_with_one_login_callback.go index 143903309f..4bc49588a0 100644 --- a/internal/voucher/voucherpage/identity_with_one_login_callback.go +++ b/internal/voucher/voucherpage/identity_with_one_login_callback.go @@ -45,7 +45,12 @@ func IdentityWithOneLoginCallback(oneLoginClient OneLoginClient, sessionStore Se } provided.IdentityUserData = userData - provided.Tasks.ConfirmYourIdentity = task.StateCompleted + if provided.NameMatches(lpa).IsNone() { + provided.Tasks.ConfirmYourIdentity = task.StateCompleted + } else { + provided.Tasks.ConfirmYourIdentity = task.StateInProgress + } + if err := voucherStore.Put(r.Context(), provided); err != nil { return err } @@ -66,6 +71,10 @@ func IdentityWithOneLoginCallback(oneLoginClient OneLoginClient, sessionStore Se return voucher.PathUnableToConfirmIdentity.Redirect(w, r, appData, appData.LpaID) } - return voucher.PathOneLoginIdentityDetails.Redirect(w, r, appData, appData.LpaID) + if provided.Tasks.ConfirmYourIdentity.IsCompleted() { + return voucher.PathOneLoginIdentityDetails.Redirect(w, r, appData, appData.LpaID) + } + + return voucher.PathConfirmAllowedToVouch.Redirect(w, r, appData, appData.LpaID) } } diff --git a/internal/voucher/voucherpage/identity_with_one_login_callback_test.go b/internal/voucher/voucherpage/identity_with_one_login_callback_test.go index 5b5556ff53..a09f4c1e3c 100644 --- a/internal/voucher/voucherpage/identity_with_one_login_callback_test.go +++ b/internal/voucher/voucherpage/identity_with_one_login_callback_test.go @@ -23,56 +23,85 @@ import ( func TestGetIdentityWithOneLoginCallback(t *testing.T) { now := time.Now() - w := httptest.NewRecorder() - r, _ := http.NewRequest(http.MethodGet, "/?code=a-code", nil) - userInfo := onelogin.UserInfo{CoreIdentityJWT: "an-identity-jwt"} userData := identity.UserData{Status: identity.StatusConfirmed, FirstNames: "John", LastName: "Doe", RetrievedAt: now} - updatedVoucher := &voucherdata.Provided{ - LpaID: "lpa-id", - FirstNames: "John", - LastName: "Doe", - IdentityUserData: userData, - Tasks: voucherdata.Tasks{ConfirmYourIdentity: task.StateCompleted}, + testcases := map[string]struct { + lpa *lpadata.Lpa + updatedVoucher *voucherdata.Provided + redirect voucher.Path + }{ + "confirmed": { + lpa: &lpadata.Lpa{LpaUID: "lpa-uid", Voucher: lpadata.Voucher{FirstNames: "John", LastName: "Doe"}}, + updatedVoucher: &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "John", + LastName: "Doe", + IdentityUserData: userData, + Tasks: voucherdata.Tasks{ConfirmYourIdentity: task.StateCompleted}, + }, + redirect: voucher.PathOneLoginIdentityDetails, + }, + "matches other actor": { + lpa: &lpadata.Lpa{ + LpaUID: "lpa-uid", + Donor: lpadata.Donor{FirstNames: "John", LastName: "Doe"}, + Voucher: lpadata.Voucher{FirstNames: "John", LastName: "Doe"}, + }, + updatedVoucher: &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "John", + LastName: "Doe", + IdentityUserData: userData, + Tasks: voucherdata.Tasks{ConfirmYourIdentity: task.StateInProgress}, + }, + redirect: voucher.PathConfirmAllowedToVouch, + }, } - voucherStore := newMockVoucherStore(t) - voucherStore.EXPECT(). - Put(r.Context(), updatedVoucher). - Return(nil) - - lpaStoreResolvingService := newMockLpaStoreResolvingService(t) - lpaStoreResolvingService.EXPECT(). - Get(r.Context()). - Return(&lpadata.Lpa{LpaUID: "lpa-uid", Voucher: lpadata.Voucher{FirstNames: "John", LastName: "Doe"}}, nil) - - sessionStore := newMockSessionStore(t) - sessionStore.EXPECT(). - OneLogin(r). - Return(&sesh.OneLoginSession{State: "a-state", Nonce: "a-nonce", Redirect: "/redirect"}, nil) - - oneLoginClient := newMockOneLoginClient(t) - oneLoginClient.EXPECT(). - Exchange(r.Context(), "a-code", "a-nonce"). - Return("id-token", "a-jwt", nil) - oneLoginClient.EXPECT(). - UserInfo(r.Context(), "a-jwt"). - Return(userInfo, nil) - oneLoginClient.EXPECT(). - ParseIdentityClaim(r.Context(), userInfo). - Return(userData, nil) + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/?code=a-code", nil) - err := IdentityWithOneLoginCallback(oneLoginClient, sessionStore, voucherStore, lpaStoreResolvingService, nil, "www.example.com")(testAppData, w, r, &voucherdata.Provided{ - LpaID: "lpa-id", - FirstNames: "John", - LastName: "Doe", - }) - resp := w.Result() + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(tc.lpa, nil) + + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Put(r.Context(), tc.updatedVoucher). + Return(nil) + + sessionStore := newMockSessionStore(t) + sessionStore.EXPECT(). + OneLogin(r). + Return(&sesh.OneLoginSession{State: "a-state", Nonce: "a-nonce", Redirect: "/redirect"}, nil) + + oneLoginClient := newMockOneLoginClient(t) + oneLoginClient.EXPECT(). + Exchange(r.Context(), "a-code", "a-nonce"). + Return("id-token", "a-jwt", nil) + oneLoginClient.EXPECT(). + UserInfo(r.Context(), "a-jwt"). + Return(userInfo, nil) + oneLoginClient.EXPECT(). + ParseIdentityClaim(r.Context(), userInfo). + Return(userData, nil) + + err := IdentityWithOneLoginCallback(oneLoginClient, sessionStore, voucherStore, lpaStoreResolvingService, nil, "www.example.com")(testAppData, w, r, &voucherdata.Provided{ + LpaID: "lpa-id", + FirstNames: "John", + LastName: "Doe", + }) + resp := w.Result() - assert.Nil(t, err) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, voucher.PathOneLoginIdentityDetails.Format("lpa-id"), resp.Header.Get("Location")) + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, tc.redirect.Format("lpa-id"), resp.Header.Get("Location")) + }) + } } func TestGetIdentityWithOneLoginCallbackWhenFailedIdentityCheck(t *testing.T) { diff --git a/internal/voucher/voucherpage/task_list.go b/internal/voucher/voucherpage/task_list.go index b14fb9d562..7a8bf96e8e 100644 --- a/internal/voucher/voucherpage/task_list.go +++ b/internal/voucher/voucherpage/task_list.go @@ -32,7 +32,10 @@ func TaskList(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving } confirmYourIdentityPath := voucher.PathConfirmYourIdentity - if provided.Tasks.ConfirmYourIdentity.IsCompleted() { + switch provided.Tasks.ConfirmYourIdentity { + case task.StateInProgress: + confirmYourIdentityPath = voucher.PathConfirmAllowedToVouch + case task.StateCompleted: confirmYourIdentityPath = voucher.PathOneLoginIdentityDetails } diff --git a/internal/voucher/voucherpage/task_list_test.go b/internal/voucher/voucherpage/task_list_test.go index 95890da05d..f6fcc5de64 100644 --- a/internal/voucher/voucherpage/task_list_test.go +++ b/internal/voucher/voucherpage/task_list_test.go @@ -29,6 +29,26 @@ func TestGetTaskList(t *testing.T) { return items }, }, + "identity in progress": { + lpa: &lpadata.Lpa{ + LpaID: "lpa-id", + Donor: lpadata.Donor{FirstNames: "John", LastName: "Smith"}, + }, + voucher: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + VerifyDonorDetails: task.StateCompleted, + ConfirmYourIdentity: task.StateInProgress, + }, + }, + expected: func(items []taskListItem) []taskListItem { + items[0].State = task.StateCompleted + items[1].State = task.StateCompleted + items[2].State = task.StateInProgress + items[2].Path = voucher.PathConfirmAllowedToVouch + return items + }, + }, "completed": { lpa: &lpadata.Lpa{ LpaID: "lpa-id", diff --git a/lang/cy.json b/lang/cy.json index 5445036afc..e595a8c844 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -1351,5 +1351,6 @@ "thankYou": "Welsh", "youHaveVouchedFor": "Welsh {{.DonorFullNamePossessive}}", "voucherThankYouContent": "

Welsh {{.DonorFirstNamesPossessive}}

", - "youHaveEnteredNameWhichMatchesSomeone": "Welsh {{.DonorFullNamePossessive}}" + "youHaveEnteredNameWhichMatchesSomeone": "Welsh {{.DonorFullNamePossessive}}", + "yourConfirmedIdentityDetailsMatchSomeone": "Welsh {{.DonorFullNamePossessive}}" } diff --git a/lang/en.json b/lang/en.json index 3ff1db8624..712070c654 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1280,5 +1280,6 @@ "thankYou": "Thank you", "youHaveVouchedFor": "You have vouched for {{.DonorFullNamePossessive}} identity.", "voucherThankYouContent": "

You do not need to do anything else.

{{.DonorFirstNamesPossessive}} vouching request will no longer appear on your dashboard.

You can print this page out for your records if you wish, or close this browsing window.

", - "youHaveEnteredNameWhichMatchesSomeone": "You have entered a name that matches someone named on {{.DonorFullNamePossessive}} lasting power of attorney (LPA)." + "youHaveEnteredNameWhichMatchesSomeone": "You have entered a name that matches someone named on {{.DonorFullNamePossessive}} lasting power of attorney (LPA).", + "yourConfirmedIdentityDetailsMatchSomeone": "Your confirmed identity details match someone named on {{.DonorFullNamePossessive}} lasting power of attorney (LPA)." } diff --git a/web/template/voucher/confirm_allowed_to_vouch.gohtml b/web/template/voucher/confirm_allowed_to_vouch.gohtml index 78be805a02..7bbc9727b9 100644 --- a/web/template/voucher/confirm_allowed_to_vouch.gohtml +++ b/web/template/voucher/confirm_allowed_to_vouch.gohtml @@ -10,7 +10,10 @@

{{ tr .App "confirmThatYouAreAllowedToVouch" }}

- {{ if .SurnameMatchesDonor }} + {{ if .MatchIdentity }} + {{ trFormat .App "yourConfirmedIdentityDetailsMatchSomeone" + "DonorFullNamePossessive" (possessive .App .Lpa.Donor.FullName) }} + {{ else if .SurnameMatchesDonor }} {{ trFormat .App "theDonorsLastNameMatchesYours" "DonorFullNamePossessive" (possessive .App .Lpa.Donor.FullName) }} {{ else }} diff --git a/web/template/voucher/confirm_your_name.gohtml b/web/template/voucher/confirm_your_name.gohtml index 849d77f50d..6d54baa398 100644 --- a/web/template/voucher/confirm_your_name.gohtml +++ b/web/template/voucher/confirm_your_name.gohtml @@ -3,7 +3,7 @@ {{ define "pageTitle" }}{{ tr .App "confirmYourName" }}{{ end }} {{ define "main" }} - {{ $canChange := not .Tasks.ConfirmYourIdentity.IsCompleted }} + {{ $canChange := .Tasks.ConfirmYourIdentity.IsNotStarted }}