Skip to content

Commit

Permalink
Merge pull request #1629 from ministryofjustice/MLPAB-2454-back-from-…
Browse files Browse the repository at this point in the history
…the-post-office

MLPAB-2454 Collect identity info when they have completed at post office
  • Loading branch information
hawx authored Nov 19, 2024
2 parents dafa547 + e20fb01 commit 5c0af64
Show file tree
Hide file tree
Showing 32 changed files with 809 additions and 60 deletions.
10 changes: 10 additions & 0 deletions internal/certificateprovider/certificateproviderdata/provided.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ func (c *Provided) CertificateProviderIdentityConfirmed(firstNames, lastName str
c.IdentityUserData.DateOfBirth.Equals(c.DateOfBirth)
}

// IdentityDeadline gives the date which the certificate provider must complete
// their identity confirmation, otherwise the signature will expire.
func (c *Provided) IdentityDeadline() time.Time {
if c.SignedAt.IsZero() {
return time.Time{}
}

return c.SignedAt.AddDate(0, 6, 0)
}

type Tasks struct {
ConfirmYourDetails task.State
ConfirmYourIdentity task.IdentityState
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package certificateproviderpage

import (
"net/http"
"time"

"github.com/ministryofjustice/opg-go-common/template"
"github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext"
"github.com/ministryofjustice/opg-modernising-lpa/internal/certificateprovider"
"github.com/ministryofjustice/opg-modernising-lpa/internal/certificateprovider/certificateproviderdata"
"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
)

type completingYourIdentityConfirmationData struct {
App appcontext.Data
Errors validation.List
Form *howWillYouConfirmYourIdentityForm
Options howYouWillConfirmYourIdentityOptions
Deadline time.Time
}

func CompletingYourIdentityConfirmation(tmpl template.Template) Handler {
return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *certificateproviderdata.Provided) error {
data := &completingYourIdentityConfirmationData{
App: appData,
Form: &howWillYouConfirmYourIdentityForm{},
Options: howYouWillConfirmYourIdentityValues,
Deadline: provided.IdentityDeadline(),
}

if r.Method == http.MethodPost {
data.Form = readHowWillYouConfirmYourIdentityForm(r, "howYouWouldLikeToContinue")
data.Errors = data.Form.Validate()

if data.Errors.None() {
return certificateprovider.PathIdentityWithOneLogin.Redirect(w, r, appData, provided.LpaID)
}
}

return tmpl(w, data)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package certificateproviderpage

import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

"github.com/ministryofjustice/opg-modernising-lpa/internal/certificateprovider"
"github.com/ministryofjustice/opg-modernising-lpa/internal/certificateprovider/certificateproviderdata"
"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestGetCompletingYourIdentityConfirmation(t *testing.T) {
w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodGet, "/", nil)

template := newMockTemplate(t)
template.EXPECT().
Execute(w, &completingYourIdentityConfirmationData{
App: testAppData,
Form: &howWillYouConfirmYourIdentityForm{},
Options: howYouWillConfirmYourIdentityValues,
}).
Return(nil)

err := CompletingYourIdentityConfirmation(template.Execute)(testAppData, w, r, &certificateproviderdata.Provided{})
resp := w.Result()

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

func TestGetCompletingYourIdentityConfirmationWhenTemplateErrors(t *testing.T) {
w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodGet, "/", nil)

template := newMockTemplate(t)
template.EXPECT().
Execute(w, mock.Anything).
Return(expectedError)

err := CompletingYourIdentityConfirmation(template.Execute)(testAppData, w, r, &certificateproviderdata.Provided{})
assert.Equal(t, expectedError, err)
}

func TestPostCompletingYourIdentityConfirmation(t *testing.T) {
testCases := map[string]struct {
how howYouWillConfirmYourIdentity
provided *certificateproviderdata.Provided
redirect certificateprovider.Path
}{
"post office successful": {
how: howYouWillConfirmYourIdentityPostOfficeSuccessfully,
provided: &certificateproviderdata.Provided{LpaID: "lpa-id"},
redirect: certificateprovider.PathIdentityWithOneLogin,
},
"one login": {
how: howYouWillConfirmYourIdentityOneLogin,
provided: &certificateproviderdata.Provided{LpaID: "lpa-id"},
redirect: certificateprovider.PathIdentityWithOneLogin,
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
form := url.Values{
"how": {tc.how.String()},
}

w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
r.Header.Add("Content-Type", page.FormUrlEncoded)

err := CompletingYourIdentityConfirmation(nil)(testAppData, w, r, tc.provided)
resp := w.Result()

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

func TestPostCompletingYourIdentityConfirmationWhenValidationErrors(t *testing.T) {
w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(""))
r.Header.Add("Content-Type", page.FormUrlEncoded)

template := newMockTemplate(t)
template.EXPECT().
Execute(w, mock.MatchedBy(func(data *completingYourIdentityConfirmationData) bool {
return assert.Equal(t, validation.With("how", validation.SelectError{Label: "howYouWouldLikeToContinue"}), data.Errors)
})).
Return(nil)

err := CompletingYourIdentityConfirmation(template.Execute)(testAppData, w, r, &certificateproviderdata.Provided{})
resp := w.Result()

assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ func TestPostConfirmYourIdentity(t *testing.T) {
w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodPost, "/", nil)

err := ConfirmYourIdentity(nil, nil, nil)(testAppData, w, r, &certificateproviderdata.Provided{
LpaID: "lpa-id",
Tasks: certificateproviderdata.Tasks{ConfirmYourIdentity: task.IdentityStateInProgress},
})
resp := w.Result()

assert.Nil(t, err)
assert.Equal(t, http.StatusFound, resp.StatusCode)
assert.Equal(t, certificateprovider.PathIdentityWithOneLogin.Format("lpa-id"), resp.Header.Get("Location"))
}

func TestPostConfirmYourIdentityWhenNotStarted(t *testing.T) {
w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodPost, "/", nil)

certificateProviderStore := newMockCertificateProviderStore(t)
certificateProviderStore.EXPECT().
Put(r.Context(), &certificateproviderdata.Provided{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func HowWillYouConfirmYourIdentity(tmpl template.Template, certificateProviderSt
}

if r.Method == http.MethodPost {
data.Form = readHowWillYouConfirmYourIdentityForm(r)
data.Form = readHowWillYouConfirmYourIdentityForm(r, "howYouWillConfirmYourIdentity")
data.Errors = data.Form.Validate()

if data.Errors.None() {
Expand All @@ -50,7 +50,6 @@ func HowWillYouConfirmYourIdentity(tmpl template.Template, certificateProviderSt
return fmt.Errorf("error updating certificate provider: %w", err)
}

// TODO page will be created in MLPAB-2454
return certificateprovider.PathTaskList.Redirect(w, r, appData, provided.LpaID)

default:
Expand All @@ -64,21 +63,23 @@ func HowWillYouConfirmYourIdentity(tmpl template.Template, certificateProviderSt
}

type howWillYouConfirmYourIdentityForm struct {
How howYouWillConfirmYourIdentity
How howYouWillConfirmYourIdentity
errorLabel string
}

func readHowWillYouConfirmYourIdentityForm(r *http.Request) *howWillYouConfirmYourIdentityForm {
func readHowWillYouConfirmYourIdentityForm(r *http.Request, errorLabel string) *howWillYouConfirmYourIdentityForm {
howWillYouConfirmYourIdentity, _ := ParseHowYouWillConfirmYourIdentity(page.PostFormString(r, "how"))

return &howWillYouConfirmYourIdentityForm{
How: howWillYouConfirmYourIdentity,
How: howWillYouConfirmYourIdentity,
errorLabel: errorLabel,
}
}

func (f *howWillYouConfirmYourIdentityForm) Validate() validation.List {
var errors validation.List

errors.Enum("how", "howYouWillConfirmYourIdentity", f.How,
errors.Enum("how", f.errorLabel, f.How,
validation.Selected())

return errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func TestReadHowWillYouConfirmYourIdentityForm(t *testing.T) {
r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
r.Header.Add("Content-Type", page.FormUrlEncoded)

result := readHowWillYouConfirmYourIdentityForm(r)
result := readHowWillYouConfirmYourIdentityForm(r, "blah")
assert.Equal(t, howYouWillConfirmYourIdentityAtPostOffice, result.How)
}

Expand All @@ -172,8 +172,8 @@ func TestHowWillYouConfirmYourIdentityFormValidate(t *testing.T) {
},
},
"invalid": {
form: &howWillYouConfirmYourIdentityForm{},
errors: validation.With("how", validation.SelectError{Label: "howYouWillConfirmYourIdentity"}),
form: &howWillYouConfirmYourIdentityForm{errorLabel: "blah"},
errors: validation.With("how", validation.SelectError{Label: "blah"}),
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ func Register(
ConfirmYourIdentity(tmpls.Get("confirm_your_identity.gohtml"), certificateProviderStore, lpaStoreResolvingService))
handleCertificateProvider(certificateprovider.PathHowWillYouConfirmYourIdentity, page.None,
HowWillYouConfirmYourIdentity(tmpls.Get("how_will_you_confirm_your_identity.gohtml"), certificateProviderStore))
handleCertificateProvider(certificateprovider.PathCompletingYourIdentityConfirmation, page.None,
CompletingYourIdentityConfirmation(tmpls.Get("completing_your_identity_confirmation.gohtml")))
handleCertificateProvider(certificateprovider.PathIdentityWithOneLogin, page.None,
IdentityWithOneLogin(oneLoginClient, sessionStore, random.String))
handleCertificateProvider(certificateprovider.PathIdentityWithOneLoginCallback, page.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func TaskList(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving
switch certificateProvider.Tasks.ConfirmYourIdentity {
case task.IdentityStateInProgress:
identityTaskPage = certificateprovider.PathHowWillYouConfirmYourIdentity
case task.IdentityStatePending:
identityTaskPage = certificateprovider.PathCompletingYourIdentityConfirmation
case task.IdentityStateCompleted:
identityTaskPage = certificateprovider.PathReadTheLpa
}
Expand All @@ -60,10 +62,11 @@ func TaskList(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving
Disabled: !lpa.Paid || !lpa.SignedForDonor(),
},
{
Name: "provideYourCertificate",
Path: certificateprovider.PathReadTheLpa.Format(lpa.LpaID),
State: tasks.ProvideTheCertificate,
Disabled: !lpa.SignedForDonor() || !tasks.ConfirmYourDetails.IsCompleted() || !tasks.ConfirmYourIdentity.IsCompleted(),
Name: "provideYourCertificate",
Path: certificateprovider.PathReadTheLpa.Format(lpa.LpaID),
State: tasks.ProvideTheCertificate,
Disabled: !lpa.SignedForDonor() || !tasks.ConfirmYourDetails.IsCompleted() ||
!(tasks.ConfirmYourIdentity.IsCompleted() || tasks.ConfirmYourIdentity.IsPending()),
},
},
}
Expand Down
4 changes: 3 additions & 1 deletion internal/certificateprovider/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

const (
PathCertificateProvided = Path("/certificate-provided")
PathCompletingYourIdentityConfirmation = Path("/completing-your-identity-confirmation")
PathConfirmDontWantToBeCertificateProvider = Path("/confirm-you-do-not-want-to-be-a-certificate-provider")
PathConfirmYourDetails = Path("/confirm-your-details")
PathConfirmYourIdentity = Path("/confirm-your-identity")
Expand Down Expand Up @@ -62,7 +63,8 @@ func (p Path) canVisit(certificateProvider *certificateproviderdata.Provided) bo
PathProvideCertificate,
PathConfirmDontWantToBeCertificateProvider,
PathCertificateProvided:
return certificateProvider.Tasks.ConfirmYourDetails.IsCompleted() && certificateProvider.Tasks.ConfirmYourIdentity.IsCompleted()
return certificateProvider.Tasks.ConfirmYourDetails.IsCompleted() &&
(certificateProvider.Tasks.ConfirmYourIdentity.IsCompleted() || certificateProvider.Tasks.ConfirmYourIdentity.IsPending())

default:
return true
Expand Down
10 changes: 10 additions & 0 deletions internal/donor/donordata/provided.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,16 @@ func (p *Provided) SigningDeadline() time.Time {
return p.SignedAt.AddDate(0, 0, 28)
}

// IdentityDeadline gives the date which the donor must complete their identity
// confirmation, otherwise the signature will expire.
func (p *Provided) IdentityDeadline() time.Time {
if p.WitnessedByCertificateProviderAt.IsZero() {
return time.Time{}
}

return p.WitnessedByCertificateProviderAt.AddDate(0, 6, 0)
}

// CourtOfProtectionSubmissionDeadline gives the date at which the signed LPA
// must be submitted to the Court of Protection, if registering through this
// route.
Expand Down
52 changes: 52 additions & 0 deletions internal/donor/donorpage/completing_your_identity_confirmation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package donorpage

import (
"net/http"
"time"

"github.com/ministryofjustice/opg-go-common/template"
"github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext"
"github.com/ministryofjustice/opg-modernising-lpa/internal/donor"
"github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata"
"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
)

type completingYourIdentityConfirmationData struct {
App appcontext.Data
Errors validation.List
Form *howWillYouConfirmYourIdentityForm
Options howYouWillConfirmYourIdentityOptions
Deadline time.Time
}

func CompletingYourIdentityConfirmation(tmpl template.Template) Handler {
return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *donordata.Provided) error {
data := &completingYourIdentityConfirmationData{
App: appData,
Form: &howWillYouConfirmYourIdentityForm{},
Options: howYouWillConfirmYourIdentityValues,
Deadline: provided.IdentityDeadline(),
}

if r.Method == http.MethodPost {
data.Form = readHowWillYouConfirmYourIdentityForm(r, "howYouWouldLikeToContinue")
data.Errors = data.Form.Validate()

if data.Errors.None() {
switch data.Form.How {
case howYouWillConfirmYourIdentityWithdraw:
if provided.WitnessedByCertificateProviderAt.IsZero() {
return donor.PathDeleteThisLpa.Redirect(w, r, appData, provided)
}

return donor.PathWithdrawThisLpa.Redirect(w, r, appData, provided)

default:
return donor.PathIdentityWithOneLogin.Redirect(w, r, appData, provided)
}
}
}

return tmpl(w, data)
}
}
Loading

0 comments on commit 5c0af64

Please sign in to comment.