Skip to content

Commit

Permalink
Merge pull request #1744 from ministryofjustice/MLPAB-2719-identity-m…
Browse files Browse the repository at this point in the history
…ismatch-not-withdrawn

MLPAB-2719 Allow continuing when identity mismatch
  • Loading branch information
hawx authored Jan 29, 2025
2 parents 49a7fbf + fadc32f commit d9dacc4
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 65 deletions.
11 changes: 8 additions & 3 deletions cypress/e2e/donor/confirm-your-identity.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ describe('Confirm your identity', () => {
cy.get('main').should('not.contain', '2 January 2000');
})


it('can withdraw LPA', () => {
it('can get OPG to check', () => {
cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa');
cy.contains('li', "Confirm your identity")
.should('contain', 'Not started')
Expand All @@ -191,8 +190,14 @@ describe('Confirm your identity', () => {
cy.contains('label', 'No').click();
cy.contains('button', 'Continue').click();

cy.url().should('contain', '/withdraw-this-lpa');
cy.url().should('contain', '/identity-details');
cy.checkA11yApp();

cy.contains('The Office of the Public Guardian (OPG) is checking your mismatched identity details');
cy.contains('a', 'Return to task list').click();

cy.contains('li', "Confirm your identity")
.should('contain', 'Pending');
})

it('errors when option not selected', () => {
Expand Down
22 changes: 22 additions & 0 deletions cypress/e2e/donor/progress.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,27 @@ describe('Progress', () => {
cy.contains('Your LPA is awaiting registration');
cy.contains('at the end of our statutory 4-week waiting period');
});

it('when continue with identity mismatch', () => {
cy.visit(`/fixtures?redirect=/task-list&progress=payForTheLpa`);

cy.contains('a', 'Confirm your identity').click();
cy.contains('button', 'Continue').click();
cy.origin('http://localhost:7012', () => {
cy.contains('label', 'Charlie Cooper').click();
cy.contains('button', 'Continue').click();
});

cy.contains('label', 'No').click();
cy.contains('button', 'Continue').click();

cy.visitLpa('/progress');

cy.checkA11yApp();
cy.contains('Important: 1 notification from OPG');

cy.contains('Confirmation of identity pending');
cy.contains('You do not need to take any action');
});
});
});
3 changes: 3 additions & 0 deletions internal/donor/donordata/provided.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ type Provided struct {
// RegisteringWithCourtOfProtection is set when the donor wishes to take the
// Lpa to the Court of Protection for registration.
RegisteringWithCourtOfProtection bool `checkhash:"-"`
// ContinueWithMismatchedIdentity is set when the donor wishes to continue
// their application with mismatched identity details
ContinueWithMismatchedIdentity bool `checkhash:"-"`
// Version is the number of times the LPA has been updated (auto-incremented
// on PUT)
Version int `hash:"-" checkhash:"-"`
Expand Down
4 changes: 2 additions & 2 deletions internal/donor/donordata/provided_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ func TestGenerateHash(t *testing.T) {
}

// DO change this value to match the updates
const modified uint64 = 0xba6539282f5874e4
const modified uint64 = 0xe3bd213ba882ed4f

// DO NOT change these initial hash values. If a field has been added/removed
// you will need to handle the version gracefully by modifying
// (*Provided).HashInclude and adding another testcase for the new
// version.
testcases := map[uint8]uint64{
0: 0xd49c9f60fe98b302,
0: 0x3730007cdf45a8cd,
}

for version, initial := range testcases {
Expand Down
9 changes: 8 additions & 1 deletion internal/donor/donorpage/identity_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ func IdentityDetails(tmpl template.Template, donorStore DonorStore) Handler {

return donor.PathIdentityDetails.RedirectQuery(w, r, appData, provided, url.Values{"detailsUpdated": {"1"}})
} else {
return donor.PathWithdrawThisLpa.Redirect(w, r, appData, provided)
provided.ContinueWithMismatchedIdentity = true
provided.Tasks.ConfirmYourIdentity = task.IdentityStatePending

if err := donorStore.Put(r.Context(), provided); err != nil {
return err
}

return donor.PathIdentityDetails.Redirect(w, r, appData, provided)
}
}
}
Expand Down
109 changes: 61 additions & 48 deletions internal/donor/donorpage/identity_details_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/mock"
)

func TestOneLoginIdentityDetailsDataDetailsMatch(t *testing.T) {
func TestIdentityDetailsDataDetailsMatch(t *testing.T) {
assert.True(t, identityDetailsData{
FirstNamesMatch: true,
LastNameMatch: true,
Expand All @@ -30,7 +30,7 @@ func TestOneLoginIdentityDetailsDataDetailsMatch(t *testing.T) {
assert.False(t, identityDetailsData{}.DetailsMatch())
}

func TestGetOneLoginIdentityDetails(t *testing.T) {
func TestGetIdentityDetails(t *testing.T) {
dob := date.New("1", "2", "3")

testcases := map[string]struct {
Expand Down Expand Up @@ -98,13 +98,7 @@ func TestGetOneLoginIdentityDetails(t *testing.T) {
}
}

func TestPostOneLoginIdentityDetailsWhenYes(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {form.Yes.String()}}

w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")

func TestPostIdentityDetails(t *testing.T) {
existingDob := date.New("1", "2", "3")
identityDob := date.New("4", "5", "6")

Expand All @@ -116,58 +110,77 @@ func TestPostOneLoginIdentityDetailsWhenYes(t *testing.T) {
}
updated.UpdateCheckedHash()

donorStore := newMockDonorStore(t)
donorStore.EXPECT().
Put(r.Context(), updated).
Return(nil)

err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{
LpaID: "lpa-id",
Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress},
IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}},
})
resp := w.Result()
testcases := map[form.YesNo]struct {
provided *donordata.Provided
redirect string
}{
form.Yes: {
provided: updated,
redirect: donor.PathIdentityDetails.Format("lpa-id") + "?detailsUpdated=1",
},
form.No: {
provided: &donordata.Provided{
LpaID: "lpa-id",
Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress},
IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}},
Tasks: donordata.Tasks{ConfirmYourIdentity: task.IdentityStatePending},
ContinueWithMismatchedIdentity: true,
},
redirect: donor.PathIdentityDetails.Format("lpa-id"),
},
}

assert.Nil(t, err)
assert.Equal(t, http.StatusFound, resp.StatusCode)
assert.Equal(t, donor.PathIdentityDetails.Format("lpa-id")+"?detailsUpdated=1", resp.Header.Get("Location"))
}
for yesNo, tc := range testcases {
t.Run(yesNo.String(), func(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {yesNo.String()}}

func TestPostOneLoginIdentityDetailsWhenNo(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {form.No.String()}}
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")

w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
donorStore := newMockDonorStore(t)
donorStore.EXPECT().
Put(r.Context(), tc.provided).
Return(nil)

err := IdentityDetails(nil, nil)(testAppData, w, r, &donordata.Provided{LpaID: "lpa-id"})
resp := w.Result()
err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{
LpaID: "lpa-id",
Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress},
IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}},
})
resp := w.Result()

assert.Nil(t, err)
assert.Equal(t, http.StatusFound, resp.StatusCode)
assert.Equal(t, donor.PathWithdrawThisLpa.Format("lpa-id"), resp.Header.Get("Location"))
assert.Nil(t, err)
assert.Equal(t, http.StatusFound, resp.StatusCode)
assert.Equal(t, tc.redirect, resp.Header.Get("Location"))
})
}
}

func TestPostOneLoginIdentityDetailsWhenDonorStoreError(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {form.Yes.String()}}
func TestPostIdentityDetailsWhenDonorStoreError(t *testing.T) {
for _, yesNo := range []form.YesNo{form.Yes, form.No} {
t.Run(yesNo.String(), func(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {yesNo.String()}}

w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")

donorStore := newMockDonorStore(t)
donorStore.EXPECT().
Put(r.Context(), mock.Anything).
Return(expectedError)
donorStore := newMockDonorStore(t)
donorStore.EXPECT().
Put(r.Context(), mock.Anything).
Return(expectedError)

err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{})
resp := w.Result()
err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{})
resp := w.Result()

assert.Equal(t, expectedError, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, expectedError, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
}

func TestPostOneLoginIdentityDetailsWhenValidationError(t *testing.T) {
func TestPostIdentityDetailsWhenValidationError(t *testing.T) {
f := url.Values{form.FieldNames.YesNo: {""}}

w := httptest.NewRecorder()
Expand Down
7 changes: 7 additions & 0 deletions internal/donor/donorpage/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ func Progress(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving
})
}

if donor.Tasks.ConfirmYourIdentity.IsPending() && donor.ContinueWithMismatchedIdentity {
data.InfoNotifications = append(data.InfoNotifications, progressNotification{
Heading: appData.Localizer.T("confirmationOfIdentityPending"),
Body: appData.Localizer.T("youDoNotNeedToTakeAnyAction"),
})
}

return tmpl(w, data)
}
}
20 changes: 20 additions & 0 deletions internal/donor/donorpage/progress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,26 @@ func TestGetProgress(t *testing.T) {
return l
},
},
"identity mismatch pending": {
donor: &donordata.Provided{
ContinueWithMismatchedIdentity: true,
IdentityUserData: identity.UserData{Status: identity.StatusConfirmed},
Tasks: donordata.Tasks{
ConfirmYourIdentity: task.IdentityStatePending,
},
},
lpa: &lpadata.Lpa{},
setupCertificateProviderStore: certificateProviderStoreNotFound,
infoNotifications: []progressNotification{
{Heading: "H", Body: "B"},
},
setupLocalizer: func(t *testing.T) *mockLocalizer {
l := newMockLocalizer(t)
l.EXPECT().T("confirmationOfIdentityPending").Return("H")
l.EXPECT().T("youDoNotNeedToTakeAnyAction").Return("B")
return l
},
},
}

for name, tc := range testCases {
Expand Down
6 changes: 5 additions & 1 deletion lang/cy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1551,5 +1551,9 @@
"voucherHasSuccessfullyConfirmedYourIdentity": "<div class=\"govuk-notification-banner__content\"><h2 class=\"govuk-notification-banner__heading\">{{.VoucherFullName}} Welsh</h2><p class=\"govuk-body\">Welsh</p></div>",
"yourLpaIsAwaitingRegistration": "Welsh",
"theOpgWillRegisterYourLpaAtEndOfWaitingPeriod": "Welsh",
"viewYourLpa": "Welsh"
"viewYourLpa": "Welsh",
"youCanUpdateTheDetailsOnYourLpaToMatchContent": "<p class=\"govuk-body\">Welsh</p>",
"theOpgIsCheckingYourMismatchedIdentityDetails": "Welsh",
"someOfTheDetailsDoNotMatchYouAskedOpgToCheck": "<p class=\"govuk-body\">Welsh</p>",
"confirmationOfIdentityPending": "Welsh"
}
12 changes: 8 additions & 4 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1160,9 +1160,9 @@
"someDetailsDoNotMatchIdentityDetailsWarning": "Some of the details on your LPA do not match your confirmed identity details",
"yourLPADetailsHaveBeenUpdatedToMatchIdentitySuccess": "Your LPA details have been updated to match your confirmed identity",
"doesNotMatch": "Does not match",
"updateMyLPADetailsToMatchIdentityHint": "Update my LPA details to match my confirmed identity details.",
"iUnderstandThisWillWithdrawLPAHint": "I understand that this will revoke my LPA and I will no longer be able to access it.",
"youCanOnlyContinueIfDetailsMatchWarning": "You can only continue with this LPA if the details match your confirmed identity details",
"updateMyLPADetailsToMatchIdentityHint": "Update your details and continue to sign your LPA",
"iUnderstandThisWillWithdrawLPAHint": "You can continue to sign your LPA, but if you do not update your details and the mismatch is serious, OPG will not be able to register it",
"youCanOnlyContinueIfDetailsMatchWarning": "Once you apply to register your LPA through the Court of Protection, you cannot change your mind. If you decide to confirm your identity another way, you’ll need to create a new LPA.",
"yesIfWouldLikeToUpdateDetails": "yes if you would like to update your details",
"wouldYouLikeToUpdateDetailsToMatchIdentityDetails": "Would you like to update the details on your LPA to match your confirmed identity details?",
"yourRegisteredLpaLanguageContent": "<h2 class=\"govuk-heading-m\">Your registered LPA</h2><p class=\"govuk-body\">You can register your LPA in either English or Welsh. It is not possible to have copies in both languages.</p><p class=\"govuk-body\">It’s best to write your LPA in just one language to avoid confusion.</p><p class=\"govuk-body\">You may need to give organisations access to your LPA when you choose for your attorneys to use it. Organisations will also see it in your preferred language.</p>",
Expand Down Expand Up @@ -1447,5 +1447,9 @@
"voucherHasSuccessfullyConfirmedYourIdentity": "<div class=\"govuk-notification-banner__content\"><h2 class=\"govuk-notification-banner__heading\">{{.VoucherFullName}} has successfully confirmed your identity</h2><p class=\"govuk-body\">Thank you for confirming your identity through vouching. Your details match those you entered in your LPA.</p></div>",
"yourLpaIsAwaitingRegistration": "Your LPA is awaiting registration",
"theOpgWillRegisterYourLpaAtEndOfWaitingPeriod": "The Office of the Public Guardian will register your LPA at the end of our statutory 4-week waiting period, if no objections are raised.",
"viewYourLpa": "View your LPA"
"viewYourLpa": "View your LPA",
"youCanUpdateTheDetailsOnYourLpaToMatchContent": "<p class=\"govuk-body\">You can update the details on your LPA to match your confirmed identity details and continue to sign your LPA.</p><p class=\"govuk-body\">Or, you can ask the Office of the Public Guardian (OPG) to check the mismatch in your identity details.</p><p class=\"govuk-body\">If the mismatch is serious, OPG will not be able to register your LPA. We’ll contact you to ask if you want to apply to the Court of Protection to register your LPA.</p><p class=\"govuk-body\">It costs £408 to register your LPA through the Court of Protection and will take several months.</p><p class=\"govuk-body\">To delete this LPA, return to ‘Manage your LPAs’.</p>",
"theOpgIsCheckingYourMismatchedIdentityDetails": "The Office of the Public Guardian (OPG) is checking your mismatched identity details",
"someOfTheDetailsDoNotMatchYouAskedOpgToCheck": "<p class=\"govuk-body\">Some of the details on your LPA do not match your confirmed identity details. You have asked OPG to check this mismatch.</p><p class=\"govuk-body\">We will contact you if you need to take further action.</p>",
"confirmationOfIdentityPending": "Confirmation of identity pending"
}
15 changes: 10 additions & 5 deletions web/template/donor/identity_details.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
{{ define "main" }}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
{{ if .DetailsUpdated }}
{{ if .Provided.ContinueWithMismatchedIdentity }}
{{ template "notification-banner" (notificationBanner .App "important" (trHtml .App "theOpgIsCheckingYourMismatchedIdentityDetails") "heading") }}
{{ else if .DetailsUpdated }}
{{ template "notification-banner" (notificationBanner .App "success" (trHtml .App "yourLPADetailsHaveBeenUpdatedToMatchIdentitySuccess") "success" "heading") }}

{{ else if .DetailsMatch }}
{{ if .Provided.WantVoucher.IsYes }}
{{ template "notification-banner" (notificationBanner .App "success" (trFormatHtml .App "voucherHasSuccessfullyConfirmedYourIdentity" "VoucherFullName" .Provided.Voucher.FullName) "success" "contents" ) }}
Expand Down Expand Up @@ -71,15 +72,19 @@

{{ template "identity-details" (card .App .Provided.IdentityUserData) }}

{{ if not .Provided.CanChange }}
<h2 class="govuk-heading-m">{{ tr .App "whatHappensNext" }}</h2>
<h2 class="govuk-heading-m">{{ tr .App "whatHappensNext" }}</h2>

{{ if .Provided.ContinueWithMismatchedIdentity }}
{{ trHtml .App "someOfTheDetailsDoNotMatchYouAskedOpgToCheck" }}
{{ else if .Provided.CanChange }}
{{ trHtml .App "youCanUpdateTheDetailsOnYourLpaToMatchContent" }}
{{ else }}
{{ trHtml .App "theDetailsOnYourLpaCannotBeUpdatedAsSigned" }}

{{ template "warning" (content .App "ifYouDoNotWantRegisteredWithConfirmedDetailsWarning") }}
{{ end }}

{{ if or .DetailsMatch (not .Provided.CanChange) }}
{{ if or .DetailsMatch .Provided.ContinueWithMismatchedIdentity (not .Provided.CanChange) }}
{{ template "button" (button .App "returnToTaskList" "link" (global.Paths.TaskList.Format .Provided.LpaID)) }}
{{ else }}
<form novalidate method="post">
Expand Down
2 changes: 1 addition & 1 deletion web/template/fixtures.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
(item "signedByCertificateProvider" "Signed by certificate provider")
(item "signedByAttorneys" "Signed by attorneys")
(item "statutoryWaitingPeriod" "Statutory waiting period" "orDivider" "1")
(item "withdrawn" "Withdrawn")
(item "withdrawn" "Revoked")
(item "certificateProviderOptedOut" "Certificate provider opted out (post signing)")
(item "doNotRegister" "Do not register")
(item "registered" "registered")
Expand Down

0 comments on commit d9dacc4

Please sign in to comment.