Skip to content

Commit

Permalink
MLPAB-1365 Prevent re-checking LPA when no changes have been made (#850)
Browse files Browse the repository at this point in the history
  • Loading branch information
hawx authored Nov 16, 2023
1 parent 2fd4c1c commit 8c42ac5
Show file tree
Hide file tree
Showing 15 changed files with 363 additions and 203 deletions.
15 changes: 15 additions & 0 deletions cypress/e2e/donor/check-your-lpa.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ describe('Check the LPA', () => {
cy.url().should('contain', '/lpa-details-saved');
});

it('does not allow checking when no changes', () => {
cy.get('#f-checked-and-happy').check()
cy.contains('button', 'Confirm').click();

cy.visitLpa('/check-your-lpa');
cy.contains('button', 'Confirm').should('not.exist');

cy.visitLpa('/restrictions');
cy.get('#f-restrictions').type('2');
cy.contains('button', 'Save and continue').click();

cy.visitLpa('/check-your-lpa');
cy.contains('button', 'Confirm');
});

describe('CP acting on paper', () => {
describe('on first check', () => {
it('content is tailored for paper CPs, a details component is shown and nav redirects to payment', () => {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ require (
github.com/hashicorp/go-version v1.5.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/ministryofjustice/opg-go-common v0.0.0-20231106092059-b3dcf8bd1eeb h1:nZ2pEcU9r5sAewHyQ0ZrXnPNLSHgfvxa7xf7rpZpbno=
github.com/ministryofjustice/opg-go-common v0.0.0-20231106092059-b3dcf8bd1eeb/go.mod h1:qktwZb46YkojkLVHU2QNnVK6yVktXkNpBuJ+TyobvuY=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
Expand Down
12 changes: 12 additions & 0 deletions internal/app/donor_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,14 @@ func (s *donorStore) Create(ctx context.Context) (*page.Lpa, error) {
Version: 1,
}

if lpa.Hash, err = lpa.GenerateHash(); err != nil {
return nil, err
}

if err := s.dynamoClient.Create(ctx, lpa); err != nil {
return nil, err
}

if err := s.dynamoClient.Create(ctx, lpaLink{
PK: lpaKey(lpaID),
SK: subKey(data.SessionID),
Expand Down Expand Up @@ -130,6 +135,13 @@ func (s *donorStore) Latest(ctx context.Context) (*page.Lpa, error) {
}

func (s *donorStore) Put(ctx context.Context, lpa *page.Lpa) error {
newHash, err := lpa.GenerateHash()
if newHash == lpa.Hash || err != nil {
return err
}

lpa.Hash = newHash

// By not setting UpdatedAt until a UID exists, queries for SK=#DONOR#xyz on
// ActorUpdatedAtIndex will not return UID-less LPAs.
if lpa.UID != "" {
Expand Down
113 changes: 65 additions & 48 deletions internal/app/donor_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ import (
"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
"github.com/ministryofjustice/opg-modernising-lpa/internal/place"
"github.com/ministryofjustice/opg-modernising-lpa/internal/uid"
"github.com/mitchellh/hashstructure/v2"
"github.com/stretchr/testify/assert"
mock "github.com/stretchr/testify/mock"
)

var expectedError = errors.New("err")
var (
expectedError = errors.New("err")
testNow = time.Date(2023, time.April, 2, 3, 4, 5, 6, time.UTC)
testNowFn = func() time.Time { return testNow }
)

func (m *mockDynamoClient) ExpectOne(ctx, pk, sk, data interface{}, err error) {
m.
Expand Down Expand Up @@ -180,36 +185,48 @@ func TestDonorStoreLatestWhenDataStoreError(t *testing.T) {

func TestDonorStorePut(t *testing.T) {
ctx := context.Background()
now := time.Now()

testcases := map[string]struct {
input, saved *page.Lpa
}{
"no uid": {
input: &page.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true},
input: &page.Lpa{PK: "LPA#5", Hash: 5, SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true},
saved: &page.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true},
},
"with uid": {
input: &page.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true, UID: "M"},
saved: &page.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true, UID: "M", UpdatedAt: now},
input: &page.Lpa{PK: "LPA#5", Hash: 5, SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true, UID: "M"},
saved: &page.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", ID: "5", HasSentApplicationUpdatedEvent: true, UID: "M", UpdatedAt: testNow},
},
}

for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
tc.saved.Hash, _ = tc.saved.GenerateHash()

dynamoClient := newMockDynamoClient(t)
dynamoClient.
On("Put", ctx, tc.saved).
Return(nil)

donorStore := &donorStore{dynamoClient: dynamoClient, now: func() time.Time { return now }}
donorStore := &donorStore{dynamoClient: dynamoClient, now: testNowFn}

err := donorStore.Put(ctx, tc.input)
assert.Nil(t, err)
})
}
}

func TestDonorStorePutWhenNoChange(t *testing.T) {
ctx := context.Background()
donorStore := &donorStore{}

lpa := &page.Lpa{ID: "an-id"}
lpa.Hash, _ = hashstructure.Hash(lpa, hashstructure.FormatV2, nil)

err := donorStore.Put(ctx, lpa)
assert.Nil(t, err)
}

func TestDonorStorePutWhenError(t *testing.T) {
ctx := context.Background()

Expand All @@ -224,7 +241,6 @@ func TestDonorStorePutWhenError(t *testing.T) {

func TestDonorStorePutWhenUIDNeeded(t *testing.T) {
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"})
now := time.Now()

eventClient := newMockEventClient(t)
eventClient.
Expand All @@ -240,27 +256,30 @@ func TestDonorStorePutWhenUIDNeeded(t *testing.T) {
}).
Return(nil)

updatedLpa := &page.Lpa{
PK: "LPA#5",
SK: "#DONOR#an-id",
ID: "5",
Donor: actor.Donor{
FirstNames: "John",
LastName: "Smith",
DateOfBirth: date.New("2000", "01", "01"),
Address: place.Address{
Line1: "line",
Postcode: "F1 1FF",
},
},
Type: page.LpaTypeHealthWelfare,
HasSentUidRequestedEvent: true,
}
updatedLpa.Hash, _ = updatedLpa.GenerateHash()

dynamoClient := newMockDynamoClient(t)
dynamoClient.
On("Put", ctx, &page.Lpa{
PK: "LPA#5",
SK: "#DONOR#an-id",
ID: "5",
Donor: actor.Donor{
FirstNames: "John",
LastName: "Smith",
DateOfBirth: date.New("2000", "01", "01"),
Address: place.Address{
Line1: "line",
Postcode: "F1 1FF",
},
},
Type: page.LpaTypeHealthWelfare,
HasSentUidRequestedEvent: true,
}).
On("Put", ctx, updatedLpa).
Return(nil)

donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient, now: func() time.Time { return now }}
donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient}

err := donorStore.Put(ctx, &page.Lpa{
PK: "LPA#5",
Expand Down Expand Up @@ -335,14 +354,13 @@ func TestDonorStorePutWhenUIDFails(t *testing.T) {

func TestDonorStorePutWhenApplicationUpdatedWhenError(t *testing.T) {
ctx := context.Background()
now := time.Now()

eventClient := newMockEventClient(t)
eventClient.
On("SendApplicationUpdated", ctx, mock.Anything).
Return(expectedError)

donorStore := &donorStore{eventClient: eventClient, now: func() time.Time { return now }}
donorStore := &donorStore{eventClient: eventClient, now: testNowFn}

err := donorStore.Put(ctx, &page.Lpa{
PK: "LPA#5",
Expand All @@ -365,7 +383,6 @@ func TestDonorStorePutWhenApplicationUpdatedWhenError(t *testing.T) {

func TestDonorStorePutWhenPreviousApplicationLinked(t *testing.T) {
ctx := context.Background()
now := time.Now()

eventClient := newMockEventClient(t)
eventClient.
Expand All @@ -375,21 +392,24 @@ func TestDonorStorePutWhenPreviousApplicationLinked(t *testing.T) {
}).
Return(nil)

updatedLpa := &page.Lpa{
PK: "LPA#5",
SK: "#DONOR#an-id",
ID: "5",
UID: "M-1111",
UpdatedAt: testNow,
PreviousApplicationNumber: "5555",
HasSentApplicationUpdatedEvent: true,
HasSentPreviousApplicationLinkedEvent: true,
}
updatedLpa.Hash, _ = updatedLpa.GenerateHash()

dynamoClient := newMockDynamoClient(t)
dynamoClient.
On("Put", ctx, &page.Lpa{
PK: "LPA#5",
SK: "#DONOR#an-id",
ID: "5",
UID: "M-1111",
UpdatedAt: now,
PreviousApplicationNumber: "5555",
HasSentApplicationUpdatedEvent: true,
HasSentPreviousApplicationLinkedEvent: true,
}).
On("Put", ctx, updatedLpa).
Return(nil)

donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient, now: func() time.Time { return now }}
donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient, now: testNowFn}

err := donorStore.Put(ctx, &page.Lpa{
PK: "LPA#5",
Expand All @@ -405,14 +425,13 @@ func TestDonorStorePutWhenPreviousApplicationLinked(t *testing.T) {

func TestDonorStorePutWhenPreviousApplicationLinkedWontResend(t *testing.T) {
ctx := context.Background()
now := time.Now()

dynamoClient := newMockDynamoClient(t)
dynamoClient.
On("Put", ctx, mock.Anything).
Return(nil)

donorStore := &donorStore{dynamoClient: dynamoClient, now: func() time.Time { return now }}
donorStore := &donorStore{dynamoClient: dynamoClient, now: testNowFn}

err := donorStore.Put(ctx, &page.Lpa{
PK: "LPA#5",
Expand All @@ -429,14 +448,13 @@ func TestDonorStorePutWhenPreviousApplicationLinkedWontResend(t *testing.T) {

func TestDonorStorePutWhenPreviousApplicationLinkedWhenError(t *testing.T) {
ctx := context.Background()
now := time.Now()

eventClient := newMockEventClient(t)
eventClient.
On("SendPreviousApplicationLinked", ctx, mock.Anything).
Return(expectedError)

donorStore := &donorStore{eventClient: eventClient, now: func() time.Time { return now }}
donorStore := &donorStore{eventClient: eventClient, now: testNowFn}

err := donorStore.Put(ctx, &page.Lpa{
PK: "LPA#5",
Expand All @@ -451,18 +469,18 @@ func TestDonorStorePutWhenPreviousApplicationLinkedWhenError(t *testing.T) {

func TestDonorStoreCreate(t *testing.T) {
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"})
now := time.Now()
lpa := &page.Lpa{PK: "LPA#10100000", SK: "#DONOR#an-id", ID: "10100000", CreatedAt: now, Version: 1}
lpa := &page.Lpa{PK: "LPA#10100000", SK: "#DONOR#an-id", ID: "10100000", CreatedAt: testNow, Version: 1}
lpa.Hash, _ = lpa.GenerateHash()

dynamoClient := newMockDynamoClient(t)
dynamoClient.
On("Create", ctx, lpa).
Return(nil)
dynamoClient.
On("Create", ctx, lpaLink{PK: "LPA#10100000", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor, UpdatedAt: now}).
On("Create", ctx, lpaLink{PK: "LPA#10100000", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor, UpdatedAt: testNow}).
Return(nil)

donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }, now: func() time.Time { return now }}
donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }, now: testNowFn}

result, err := donorStore.Create(ctx)
assert.Nil(t, err)
Expand All @@ -480,7 +498,6 @@ func TestDonorStoreCreateWithSessionMissing(t *testing.T) {

func TestDonorStoreCreateWhenError(t *testing.T) {
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"})
now := time.Now()

testcases := map[string]func(*testing.T) *mockDynamoClient{
"certificate provider record": func(t *testing.T) *mockDynamoClient {
Expand Down Expand Up @@ -512,7 +529,7 @@ func TestDonorStoreCreateWhenError(t *testing.T) {
donorStore := &donorStore{
dynamoClient: dynamoClient,
uuidString: func() string { return "10100000" },
now: func() time.Time { return now },
now: testNowFn,
}

_, err := donorStore.Create(ctx)
Expand Down
Loading

0 comments on commit 8c42ac5

Please sign in to comment.