From 1d790bb0964f44d6f2c3e65da7ca22e533f08414 Mon Sep 17 00:00:00 2001 From: Alex Saunders Date: Tue, 4 Mar 2025 09:50:46 +0000 Subject: [PATCH] MLPAB-2760 support deleting voucherdata.Provided MLPAB-2760 fix and prevent clear chaining --- .codecov.yml | 2 +- .pre-commit-config.yaml | 6 + .../e2e/attorney/confirm-your-details.cy.js | 3 +- .../enter-date-of-birth.cy.js | 3 +- .../e2e/donor/choose-attorneys-summary.cy.js | 24 ++-- cypress/e2e/donor/choose-attorneys.cy.js | 6 +- ...choose-replacement-attorneys-summary.cy.js | 24 ++-- .../donor/choose-replacement-attorneys.cy.js | 6 +- ...-certificate-provider-is-not-related.cy.js | 3 +- cypress/e2e/donor/make-a-new-lpa.cy.js | 13 +- .../donor/you-cannot-sign-your-lpa-yet.cy.js | 16 +-- cypress/e2e/donor/your-date-of-birth.cy.js | 3 +- cypress/e2e/donor/your-name.cy.js | 6 +- cypress/e2e/supporter/edit-member.cy.js | 18 ++- .../e2e/supporter/manage-organisation.cy.js | 3 +- cypress/e2e/supporter/view-lpa.cy.js | 3 +- .../e2e/voucher/confirm-your-identity.cy.js | 6 +- go.sum | 48 ------- internal/donor/store.go | 43 ++++--- internal/donor/store_test.go | 120 +++++++++++++++++- .../voucherpage/mock_DonorStore_test.go | 23 ++-- internal/voucher/voucherpage/register.go | 3 +- internal/voucher/voucherpage/vouch_failer.go | 2 +- .../voucher/voucherpage/vouch_failer_test.go | 4 +- scripts/pre-commit/prevent_unsafe_chains.go | 72 +++++++++++ 25 files changed, 323 insertions(+), 137 deletions(-) create mode 100644 scripts/pre-commit/prevent_unsafe_chains.go diff --git a/.codecov.yml b/.codecov.yml index e436572e06..352ddfcb7b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -21,7 +21,7 @@ coverage: - "./internal/telemetry" - "./internal/validation/error.go" - "./mocks/*" - - "./scripts/*.go" + - "./scripts/pre-commit/*" status: project: default: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 76a3fe7199..4b16376bd8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,6 +51,12 @@ repos: entry: yarn run stop-only language: node args: [--folder, cypress] + - id: cypress-clear-no-chain + name: Prevent unsafe Cypress command chaining + entry: go run ./scripts/pre-commit/prevent_unsafe_chains.go + language: golang + types: [ file ] + files: \.cy.js$ # - repo: https://github.com/Yelp/detect-secrets # rev: v1.4.0 # hooks: diff --git a/cypress/e2e/attorney/confirm-your-details.cy.js b/cypress/e2e/attorney/confirm-your-details.cy.js index fe7124c878..8d02bb6d26 100644 --- a/cypress/e2e/attorney/confirm-your-details.cy.js +++ b/cypress/e2e/attorney/confirm-your-details.cy.js @@ -89,7 +89,8 @@ describe('Confirm your details', () => { it('can change the phone number', () => { cy.contains('.govuk-summary-list__row', 'Phone number').contains('a', 'Change').click(); - cy.get('#f-phone').clear().type(TestMobile2); + cy.get('#f-phone').clear(); + cy.get('#f-phone').type(TestMobile2); cy.contains('button', 'Save and continue').click() cy.contains('h2', 'Details you have given us').next().within(() => { diff --git a/cypress/e2e/certificate-provider/enter-date-of-birth.cy.js b/cypress/e2e/certificate-provider/enter-date-of-birth.cy.js index 9386143e8f..906eb6ac42 100644 --- a/cypress/e2e/certificate-provider/enter-date-of-birth.cy.js +++ b/cypress/e2e/certificate-provider/enter-date-of-birth.cy.js @@ -74,7 +74,8 @@ describe('Enter date of birth', () => { cy.get('#f-date-of-birth').type('not'); cy.get('#f-date-of-birth-month').type('valid'); - cy.get('#f-date-of-birth-year').clear().type('values'); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type('values'); cy.contains('button', 'Save and continue').click(); cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be a real date'); }); diff --git a/cypress/e2e/donor/choose-attorneys-summary.cy.js b/cypress/e2e/donor/choose-attorneys-summary.cy.js index be96646a3e..b87ee3d47a 100644 --- a/cypress/e2e/donor/choose-attorneys-summary.cy.js +++ b/cypress/e2e/donor/choose-attorneys-summary.cy.js @@ -33,7 +33,8 @@ describe('Choose attorneys summary', () => { cy.url().should('contain', '/choose-attorneys-summary'); cy.url().should('match', /id=\w*/); - cy.get('#f-first-names').clear().type('Mark'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Mark'); cy.contains('button', 'Save and continue').click(); @@ -54,7 +55,8 @@ describe('Choose attorneys summary', () => { cy.url().should('contain', '/choose-attorneys-summary'); cy.url().should('match', /id=\w*/); - cy.get('#f-address-line-1').clear().type('1 RICHMOND PLACE'); + cy.get('#f-address-line-1').clear(); + cy.get('#f-address-line-1').type('1 RICHMOND PLACE'); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/choose-attorneys-summary'); @@ -73,12 +75,18 @@ describe('Choose attorneys summary', () => { cy.url().should('contain', '/choose-attorneys'); - cy.get('#f-first-names').clear().type('Bob Arnold'); - cy.get('#f-last-name').clear().type('Jones'); - cy.get('#f-email').clear().type(TestEmail); - cy.get('input[name="date-of-birth-day"]').clear().type('31'); - cy.get('input[name="date-of-birth-month"]').clear().type('12'); - cy.get('input[name="date-of-birth-year"]').clear().type('1995'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Bob Arnold'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Jones'); + cy.get('#f-email').clear(); + cy.get('#f-email').type(TestEmail); + cy.get('input[name="date-of-birth-day"]').clear(); + cy.get('input[name="date-of-birth-day"]').type('31'); + cy.get('input[name="date-of-birth-month"]').clear(); + cy.get('input[name="date-of-birth-month"]').type('12'); + cy.get('input[name="date-of-birth-year"]').clear(); + cy.get('input[name="date-of-birth-year"]').type('1995'); cy.contains('button', 'Save and continue').click(); cy.contains('label', 'Enter a new address').click(); diff --git a/cypress/e2e/donor/choose-attorneys.cy.js b/cypress/e2e/donor/choose-attorneys.cy.js index 05a8d567ec..d66c1f9c6e 100644 --- a/cypress/e2e/donor/choose-attorneys.cy.js +++ b/cypress/e2e/donor/choose-attorneys.cy.js @@ -80,7 +80,8 @@ describe('Choose attorneys', () => { cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be in the past'); cy.get('#f-date-of-birth-month').type('2'); - cy.get('#f-date-of-birth-year').clear().type('1990'); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type('1990'); cy.contains('button', 'Save and continue').click(); cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be a real date'); }); @@ -138,7 +139,8 @@ describe('Choose attorneys', () => { cy.go(-2); cy.url().should('contain', '/choose-attorneys'); - cy.get('#f-date-of-birth-year').clear().type(new Date().getFullYear() - 20); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type(new Date().getFullYear() - 20); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/choose-attorneys-address'); diff --git a/cypress/e2e/donor/choose-replacement-attorneys-summary.cy.js b/cypress/e2e/donor/choose-replacement-attorneys-summary.cy.js index c7fdeab231..3bc108ec49 100644 --- a/cypress/e2e/donor/choose-replacement-attorneys-summary.cy.js +++ b/cypress/e2e/donor/choose-replacement-attorneys-summary.cy.js @@ -28,7 +28,8 @@ describe('Choose replacement attorneys summary', () => { cy.checkA11yApp(); cy.contains('.govuk-summary-card', 'Blake Buckley').contains('a', 'Change').click(); - cy.get('#f-first-names').clear().type('Mark'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Mark'); cy.contains('button', 'Save and continue').click(); @@ -44,7 +45,8 @@ describe('Choose replacement attorneys summary', () => { .contains('a', 'Change') .click(); - cy.get('#f-address-line-1').clear().type('4 RICHMOND PLACE'); + cy.get('#f-address-line-1').clear(); + cy.get('#f-address-line-1').type('4 RICHMOND PLACE'); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/choose-replacement-attorneys-summary'); @@ -59,12 +61,18 @@ describe('Choose replacement attorneys summary', () => { cy.url().should('contain', '/choose-replacement-attorneys'); - cy.get('#f-first-names').clear().type('Bob Arnold'); - cy.get('#f-last-name').clear().type('Jones'); - cy.get('#f-email').clear().type(TestEmail); - cy.get('input[name="date-of-birth-day"]').clear().type('31'); - cy.get('input[name="date-of-birth-month"]').clear().type('12'); - cy.get('input[name="date-of-birth-year"]').clear().type('1995'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Bob Arnold'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Jones'); + cy.get('#f-email').clear(); + cy.get('#f-email').type(TestEmail); + cy.get('input[name="date-of-birth-day"]').clear(); + cy.get('input[name="date-of-birth-day"]').type('31'); + cy.get('input[name="date-of-birth-month"]').clear(); + cy.get('input[name="date-of-birth-month"]').type('12'); + cy.get('input[name="date-of-birth-year"]').clear(); + cy.get('input[name="date-of-birth-year"]').type('1995'); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/choose-replacement-attorneys-address'); diff --git a/cypress/e2e/donor/choose-replacement-attorneys.cy.js b/cypress/e2e/donor/choose-replacement-attorneys.cy.js index 2c0c9d999b..2092106c87 100644 --- a/cypress/e2e/donor/choose-replacement-attorneys.cy.js +++ b/cypress/e2e/donor/choose-replacement-attorneys.cy.js @@ -83,7 +83,8 @@ describe('Choose replacement attorneys', () => { cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be in the past'); cy.get('#f-date-of-birth-month').type('2'); - cy.get('#f-date-of-birth-year').clear().type('1990'); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type('1990'); cy.contains('button', 'Save and continue').click(); cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be a real date'); }); @@ -141,7 +142,8 @@ describe('Choose replacement attorneys', () => { cy.go(-2); cy.url().should('contain', '/choose-replacement-attorneys'); - cy.get('#f-date-of-birth-year').clear().type(new Date().getFullYear() - 20); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type(new Date().getFullYear() - 20); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/choose-replacement-attorneys-address'); diff --git a/cypress/e2e/donor/confirm-your-certificate-provider-is-not-related.cy.js b/cypress/e2e/donor/confirm-your-certificate-provider-is-not-related.cy.js index ef246f33e8..ac575aa0b4 100644 --- a/cypress/e2e/donor/confirm-your-certificate-provider-is-not-related.cy.js +++ b/cypress/e2e/donor/confirm-your-certificate-provider-is-not-related.cy.js @@ -1,7 +1,8 @@ describe('Confirm your certificate provider is not related', () => { beforeEach(() => { cy.visit('/fixtures?redirect=/your-name&progress=addCorrespondent'); - cy.get('#f-last-name').clear().type('Cooper'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Cooper'); cy.contains('button', 'Save and continue').click(); cy.visitLpa('/task-list'); cy.contains('li', "Check and send to your certificate provider") diff --git a/cypress/e2e/donor/make-a-new-lpa.cy.js b/cypress/e2e/donor/make-a-new-lpa.cy.js index f0ba28788a..07e6b4f76e 100644 --- a/cypress/e2e/donor/make-a-new-lpa.cy.js +++ b/cypress/e2e/donor/make-a-new-lpa.cy.js @@ -9,10 +9,15 @@ describe('Make a new LPA', () => { cy.url().should('contain', '/your-name'); cy.checkA11yApp(); - cy.get('#f-first-names').should('have.value', 'Sam').focus().clear(); - cy.get('#f-first-names').type('a').should('have.value', 'a'); - cy.get('#f-last-name').should('have.value', 'Smith').focus().clear(); - cy.get('#f-last-name').type('b').should('have.value', 'b'); + cy.get('#f-first-names').should('have.value', 'Sam'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('a') + cy.get('#f-first-names').should('have.value', 'a'); + + cy.get('#f-last-name').should('have.value', 'Smith'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('b'); + cy.get('#f-last-name').should('have.value', 'b'); cy.contains("button", "Continue").click(); cy.url().should('contain', '/we-have-updated-your-details'); diff --git a/cypress/e2e/donor/you-cannot-sign-your-lpa-yet.cy.js b/cypress/e2e/donor/you-cannot-sign-your-lpa-yet.cy.js index 9c4ca0c5dc..f5931317cc 100644 --- a/cypress/e2e/donor/you-cannot-sign-your-lpa-yet.cy.js +++ b/cypress/e2e/donor/you-cannot-sign-your-lpa-yet.cy.js @@ -4,15 +4,15 @@ describe('You cannot sign your LPA yet', () => { cy.visit('/fixtures?redirect=/choose-attorneys-summary&progress=addCorrespondent'); cy.contains('.govuk-summary-card', 'Jessie Jones').contains('a', 'Change').click(); - cy.get('#f-date-of-birth-year').focus().clear(); - cy.get('#f-date-of-birth-year').focus().type(today.getFullYear() - 1); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type(today.getFullYear() - 1); cy.contains('button', 'Save and continue').click() cy.contains('button', 'Save and continue').click() cy.visitLpa('/choose-replacement-attorneys-summary') cy.contains('.govuk-summary-card', 'Blake Buckley').contains('a', 'Change').click(); - cy.get('#f-date-of-birth-year').focus().clear(); - cy.get('#f-date-of-birth-year').focus().type(today.getFullYear() - 1); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type(today.getFullYear() - 1); cy.contains('button', 'Save and continue').click() cy.contains('button', 'Save and continue').click() cy.contains('a', 'Return to task list').click() @@ -24,16 +24,16 @@ describe('You cannot sign your LPA yet', () => { cy.contains('.govuk-summary-list__row', 'Jessie Jones').contains('a', 'Change').click(); cy.url().should('contain', '/choose-attorneys') - cy.get('#f-date-of-birth-year').focus().clear(); - cy.get('#f-date-of-birth-year').focus().type("2000"); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type("2000"); cy.contains('button', 'Save and continue').click() cy.url().should('contain', '/you-cannot-sign-your-lpa-yet') cy.contains('.govuk-summary-list__row', 'Blake Buckley').contains('a', 'Change').click(); cy.url().should('contain', '/choose-replacement-attorneys') - cy.get('#f-date-of-birth-year').focus().clear(); - cy.get('#f-date-of-birth-year').focus().type("2000"); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type("2000"); cy.contains('button', 'Save and continue').click() cy.url().should('contain', '/task-list') }); diff --git a/cypress/e2e/donor/your-date-of-birth.cy.js b/cypress/e2e/donor/your-date-of-birth.cy.js index 273068de9f..e63ff794ac 100644 --- a/cypress/e2e/donor/your-date-of-birth.cy.js +++ b/cypress/e2e/donor/your-date-of-birth.cy.js @@ -37,7 +37,8 @@ describe('Your date of birth', () => { cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be in the past'); cy.get('#f-date-of-birth-month').type('2'); - cy.get('#f-date-of-birth-year').clear().type('1990'); + cy.get('#f-date-of-birth-year').clear(); + cy.get('#f-date-of-birth-year').type('1990'); cy.contains('button', 'Save and continue').click(); cy.contains('#date-of-birth-hint + .govuk-error-message', 'Date of birth must be a real date'); }); diff --git a/cypress/e2e/donor/your-name.cy.js b/cypress/e2e/donor/your-name.cy.js index 66d4e31331..f898b24a51 100644 --- a/cypress/e2e/donor/your-name.cy.js +++ b/cypress/e2e/donor/your-name.cy.js @@ -52,8 +52,10 @@ describe('Your name', () => { }); it('warns when name shared with other actor', () => { - cy.get('#f-first-names').clear().type('Jessie'); - cy.get('#f-last-name').clear().type('Jones'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Jessie'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Jones'); cy.contains('button', 'Save and continue').click(); cy.url().should('contain', '/your-name'); diff --git a/cypress/e2e/supporter/edit-member.cy.js b/cypress/e2e/supporter/edit-member.cy.js index 00be17b6ba..b68f0c5f11 100644 --- a/cypress/e2e/supporter/edit-member.cy.js +++ b/cypress/e2e/supporter/edit-member.cy.js @@ -12,8 +12,10 @@ describe('Edit member', () => { cy.checkA11yApp(); - cy.get('#f-first-names').clear().type('John'); - cy.get('#f-last-name').clear().type('Doe'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('John'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Doe'); cy.contains('button', "Save").click() @@ -32,8 +34,10 @@ describe('Edit member', () => { cy.checkA11yApp(); - cy.get('#f-first-names').clear().type('John'); - cy.get('#f-last-name').clear().type('Doe'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('John'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Doe'); cy.contains('button', "Save").click() @@ -111,8 +115,10 @@ describe('Edit member', () => { cy.checkA11yApp(); cy.contains('Your name'); - cy.get('#f-first-names').clear().type('John'); - cy.get('#f-last-name').clear().type('Doe'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('John'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Doe'); cy.contains('button', "Save").click() diff --git a/cypress/e2e/supporter/manage-organisation.cy.js b/cypress/e2e/supporter/manage-organisation.cy.js index ee8eb5b484..7c020bee07 100644 --- a/cypress/e2e/supporter/manage-organisation.cy.js +++ b/cypress/e2e/supporter/manage-organisation.cy.js @@ -10,7 +10,8 @@ describe('Manage organisation', () => { cy.url().should('contain', '/manage-organisation/organisation-details/edit-organisation-name'); cy.checkA11yApp(); - cy.get('#f-name').clear().type('My organisation'); + cy.get('#f-name').clear(); + cy.get('#f-name').type('My organisation'); cy.contains('button', 'Continue').click(); cy.url().should('contain', '/manage-organisation/organisation-details'); diff --git a/cypress/e2e/supporter/view-lpa.cy.js b/cypress/e2e/supporter/view-lpa.cy.js index 2681f47a6a..7c07f5bb77 100644 --- a/cypress/e2e/supporter/view-lpa.cy.js +++ b/cypress/e2e/supporter/view-lpa.cy.js @@ -27,7 +27,8 @@ describe('View LPA', () => { cy.contains('.govuk-summary-list__row', 'First names').find('a').click(); - cy.get('#f-first-names').clear().type('2'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('2'); cy.contains('button', 'Save and continue').click(); cy.contains('a', 'Dashboard').click(); cy.contains('2 Smith'); diff --git a/cypress/e2e/voucher/confirm-your-identity.cy.js b/cypress/e2e/voucher/confirm-your-identity.cy.js index 7918dfe415..01d759e4ee 100644 --- a/cypress/e2e/voucher/confirm-your-identity.cy.js +++ b/cypress/e2e/voucher/confirm-your-identity.cy.js @@ -35,8 +35,10 @@ describe('Confirm your identity', () => { it('warns when matches another actor', () => { cy.visitLpa('/your-name'); - cy.get('#f-first-names').clear().type('Charlie'); - cy.get('#f-last-name').clear().type('Cooper'); + cy.get('#f-first-names').clear(); + cy.get('#f-first-names').type('Charlie'); + cy.get('#f-last-name').clear(); + cy.get('#f-last-name').type('Cooper'); cy.contains('button', 'Save and continue').click(); cy.contains('button', 'Continue').click(); cy.contains('label', 'Yes').click(); diff --git a/go.sum b/go.sum index 66ebafa17d..9a78b406bf 100644 --- a/go.sum +++ b/go.sum @@ -4,108 +4,60 @@ github.com/MicahParks/keyfunc/v3 v3.3.10 h1:JtEGE8OcNeI297AMrR4gVXivV8fyAawFUMkb github.com/MicahParks/keyfunc/v3 v3.3.10/go.mod h1:1TEt+Q3FO7Yz2zWeYO//fMxZMOiar808NqjWQQpBPtU= github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI= github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= -github.com/aws/aws-sdk-go-v2 v1.36.2 h1:Ub6I4lq/71+tPb/atswvToaLGVMxKZvjYDVOWEExOcU= -github.com/aws/aws-sdk-go-v2 v1.36.2/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= -github.com/aws/aws-sdk-go-v2/config v1.29.7 h1:71nqi6gUbAUiEQkypHQcNVSFJVUFANpSeUNShiwWX2M= -github.com/aws/aws-sdk-go-v2/config v1.29.7/go.mod h1:yqJQ3nh2HWw/uxd56bicyvmDW4KSc+4wN6lL8pYjynU= github.com/aws/aws-sdk-go-v2/config v1.29.8 h1:RpwAfYcV2lr/yRc4lWhUM9JRPQqKgKWmou3LV7UfWP4= github.com/aws/aws-sdk-go-v2/config v1.29.8/go.mod h1:t+G7Fq1OcO8cXTPPXzxQSnj/5Xzdc9jAAD3Xrn9/Mgo= -github.com/aws/aws-sdk-go-v2/credentials v1.17.60 h1:1dq+ELaT5ogfmqtV1eocq8SpOK1NRsuUfmhQtD/XAh4= -github.com/aws/aws-sdk-go-v2/credentials v1.17.60/go.mod h1:HDes+fn/xo9VeszXqjBVkxOo/aUy8Mc6QqKvZk32GlE= github.com/aws/aws-sdk-go-v2/credentials v1.17.61 h1:Hd/uX6Wo2iUW1JWII+rmyCD7MMhOe7ALwQXN6sKDd1o= github.com/aws/aws-sdk-go-v2/credentials v1.17.61/go.mod h1:L7vaLkwHY1qgW0gG1zG0z/X0sQ5tpIY5iI13+j3qI80= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.5 h1:3QhGiWUMeyuAPT4RjIwNEQUu+wOmPLLOz8IpgiiKWM4= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.5/go.mod h1:J+3D/6T2wbhBJkv7BevC/8QV3GSHYrTKK30EWqWtJkU= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.6 h1:5MXQb+ASlUe0SgSmPt8V0l4EFRKLyr0krAnMqMvlAjQ= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.6/go.mod h1:V+IXONaymKaUpRMGVqdjaXhZwYFHAgFwxmJi6/132tE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.29 h1:JO8pydejFKmGcUNiiwt75dzLHRWthkwApIvPoyUtXEg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.29/go.mod h1:adxZ9i9DRmB8zAT0pO0yGnsmu0geomp5a3uq5XpgOJ8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 h1:knLyPMw3r3JsU8MFHWctE4/e2qWbPaxDYLlohPvnY8c= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33/go.mod h1:EBp2HQ3f+XCB+5J+IoEbGhoV7CpJbnrsd4asNXmTL0A= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 h1:K0+Ne08zqti8J9jwENxZ5NoUyBnaFDTu3apwQJWrwwA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33/go.mod h1:K97stwwzaWzmqxO8yLGHhClbVW1tC6VT1pDLk1pGrq4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.33 h1:/frG8aV09yhCVSOEC2pzktflJJO48NwY3xntHBwxHiA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.33/go.mod h1:8vwASlAcV366M+qxZnjNzCjeastk1Rt1bpSRaGZanGU= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.15 h1:+a0SqOtbhFDifEnt2/9ILgnTFaj0UHxS1tm3Zb1iajM= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.15/go.mod h1:jBiy3OFpD0L9Te+9hx9vcRwz4WEKH2eYSmM7qvH0Q7E= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.44.0 h1:0cF07Fs0CT8XSLGGFqp0VNJD+sb447S8UQU7hz95xJo= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.44.0/go.mod h1:HJlcOk+S/wjJuR/8jPa8GhnEKdKqqiQ5wjsE1PjuO1o= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.40.2 h1:lT4US8VW4CAsCzJy0JpH/vPuJD9nG/73ioLHDlKQDU8= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.40.2/go.mod h1:QwexjOlSUV85+ct6LohHmsaFTiW2j1s+9SQZNVjhAV0= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.41.0 h1:kSMAk72LZ5eIdY/W+tVV6VdokciajcDdVClEBVNWNP0= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.41.0/go.mod h1:yYaWRnVSPyAmexW5t7G3TcuYoalYfT+xQwzWsvtUQ7M= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.21 h1:6uTJJuQouHbWupYOhgCY3v6xZP1VbJlHQsiFqwVdebY= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.21/go.mod h1:isd8r8zEUafc7PBf+Z2QwCgbku0xYL0/ea8EI9u1AGo= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.0 h1:iTFqGH+Eel+KPW0cFvsA6JVP9/86MEbENVz60dbHxIs= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.0/go.mod h1:lUqWdw5/esjPTkITXhN4C66o1ltwDq2qQ12j3SOzhVg= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.36.12 h1:uH6GOnGSvVN9MCk6o3+HvZFpdqL7AzJKNOTM/6l+3/s= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.36.12/go.mod h1:6qtp53AQg7KEeYrsp430PNlmVVO9qK0Xw8nddE1y+ow= github.com/aws/aws-sdk-go-v2/service/eventbridge v1.37.0 h1:ecwJblEEQdV1efA5+wmRJepGN3RzODw0VXgCaZHxoZY= github.com/aws/aws-sdk-go-v2/service/eventbridge v1.37.0/go.mod h1:QiEUHcyXhCdsTzHAbfmgwlFEmW3WgfqL4L1bS+E9IlA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.1 h1:7SuukGpyIgF5EiAbf1dZRxP+xSnY1WjiHBjL08fjJeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.1/go.mod h1:k+Vce/8R28tSozjdWphkrNhK8zLmdS9RgiDNZl6p8Rw= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2 h1:t/gZFyrijKuSU0elA5kRngP/oU3mc0I+Dvp8HwRE4c0= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.14 h1:a4cztfjtvD/DDPxWzRnMskxeEVgEXUYAFHBFz+eVjIc= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.14/go.mod h1:4Z0HHlXIU+k510CCfnTtgUon5MMymnSAOp9i0/nLfpA= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 h1:M1R1rud7HzDrfCdlBQ7NjnRsDNEhXO/vGhuD189Ggmk= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15/go.mod h1:uvFKBSq9yMPV4LGAi7N4awn4tLY+hKE35f8THes2mzQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.14 h1:2scbY6//jy/s8+5vGrk7l1+UtHl0h9A4MjOO2k/TM2E= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.14/go.mod h1:bRpZPHZpSe5YRHmPfK3h1M7UBFCn2szHzyx0rw04zro= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.14 h1:fgdkfsxTehqPcIQa24G/Omwv9RocTq2UcONNX/OnrZI= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.14/go.mod h1:wMxQ3OE8fiM8z2YRAeb2J8DLTTWMvRyYYuQOs26AbTQ= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.77.1 h1:5bI9tJL2Z0FGFtp/LPDv0eyliFBHCn7LAhqpQuL+7kk= -github.com/aws/aws-sdk-go-v2/service/s3 v1.77.1/go.mod h1:njj3tSJONkfdLt4y6X8pyqeM6sJLNZxmzctKKV+n1GM= github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0 h1:EBm8lXevBWe+kK9VOU/IBeOI189WPRwPUc3LvJK9GOs= github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU= -github.com/aws/aws-sdk-go-v2/service/s3control v1.53.5 h1:UNNHUEPhlAT+ts1cxBeXaoQoYxUoQY7L2goIqa4J7YU= -github.com/aws/aws-sdk-go-v2/service/s3control v1.53.5/go.mod h1:cHlVEwxmbpbDvnXMPp/ePvQMy/NujGqY3GGWtusKhmE= github.com/aws/aws-sdk-go-v2/service/s3control v1.54.0 h1:b+KYCgKouHeiOChS9fu61DeIQsfMs2d1wEaKv0plXM8= github.com/aws/aws-sdk-go-v2/service/s3control v1.54.0/go.mod h1:hqimoWPQe+lvweuYZ2c1Fn4q3UyAFhbjSoABSl8Y7Pw= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 h1:O2xbipq7k1kTct69V7mFidwTagld9c/6iyK+3yo+QNg= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19/go.mod h1:CxTOwBy2Qs8/+yV7fkz4eZB1RB5qeWaW9SvznvFLgRA= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.0 h1:BRCDd+oBBOk/5VzR/rVk3Azy8o5oCCr8urNJQs191mE= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.0/go.mod h1:yGhDiLKguA3iFJYxbrQkQiNzuy+ddxesSZYWVeeEH5Q= github.com/aws/aws-sdk-go-v2/service/sns v1.33.12 h1:5LZIyHvSAu2DeC9X6P9c3ALFTSDu/oyJ5Cq0rLbe2mk= github.com/aws/aws-sdk-go-v2/service/sns v1.33.12/go.mod h1:W7OKlS05LPMcLvQamv12gv/hSQlWAyU1lh98jwMVf2k= -github.com/aws/aws-sdk-go-v2/service/sqs v1.37.15 h1:KRXf9/NWjoRgj2WJbX13GNjBPQ1SxUYLnIfXTz08mWs= -github.com/aws/aws-sdk-go-v2/service/sqs v1.37.15/go.mod h1:1CY54O4jz8BzgH2d6KyrzKWr2bAoqKsqUv2YZUGwMLE= github.com/aws/aws-sdk-go-v2/service/sqs v1.38.0 h1:8za7W7p6GaEbPNvNGuQty36qpQykCA+ONxh0LBp46qs= github.com/aws/aws-sdk-go-v2/service/sqs v1.38.0/go.mod h1:Bar4MrRxeqdn6XIh8JGfiXuFRmyrrsZNTJotxEJmWW0= -github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13 h1:JfPeW7F6Y+VqBg6p+8zQv4wlgceguYu5ZT0USEGZ89g= -github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13/go.mod h1:EonGQFn66wZkJJrrKXrryrxoS3V30rcHvaWvc6oGHCI= github.com/aws/aws-sdk-go-v2/service/ssm v1.57.0 h1:bfCv9klbdln2a9VBWDa190EcbimesEEZmMCDt/buEOk= github.com/aws/aws-sdk-go-v2/service/ssm v1.57.0/go.mod h1:PUWUl5MDiYNQkUHN9Pyd9kgtA/YhbxnSnHP+yQqzrM8= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.16 h1:YV6xIKDJp6U7YB2bxfud9IENO1LRpGhe2Tv/OKtPrOQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.16/go.mod h1:DvbmMKgtpA6OihFJK13gHMZOZrCHttz8wPHGKXqU+3o= github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 h1:2U9sF8nKy7UgyEeLiZTRg6ShBS22z8UnYpV6aRFL0is= github.com/aws/aws-sdk-go-v2/service/sso v1.25.0/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.15 h1:kMyK3aKotq1aTBsj1eS8ERJLjqYRRRcsmP33ozlCvlk= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.15/go.mod h1:5uPZU7vSNzb8Y0dm75xTikinegPYK3uJmIHQZFq5Aqo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 h1:wjAdc85cXdQR5uLx5FwWvGIHm4OPJhTyzUHU8craXtE= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.15 h1:ht1jVmeeo2anR7zDiYJLSnRYnO/9NILXXu42FP3rJg0= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.15/go.mod h1:xWZ5cOiFe3czngChE4LhCBqUxNwgfwndEF7XlYP/yD8= github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 h1:BHEK2Q/7CMRMCb3nySi/w8UbIcPhKvYP5s1xf8/izn0= github.com/aws/aws-sdk-go-v2/service/sts v1.33.16/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= diff --git a/internal/donor/store.go b/internal/donor/store.go index bc49c7b8c6..e83df86cf8 100644 --- a/internal/donor/store.go +++ b/internal/donor/store.go @@ -22,6 +22,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" "github.com/ministryofjustice/opg-modernising-lpa/internal/task" "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) type Logger interface { @@ -483,34 +484,40 @@ func (s *Store) DeleteVoucher(ctx context.Context, provided *donordata.Provided) } var link sharecodedata.Link - if err := s.dynamoClient.OneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey(sessionData.LpaID)), &link); err != nil { - return err + linkErr := s.dynamoClient.OneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey(sessionData.LpaID)), &link) + if linkErr != nil && !errors.As(linkErr, &dynamo.NotFoundError{}) { + return linkErr } - provided.Voucher = donordata.Voucher{} - provided.VoucherInvitedAt = time.Time{} - provided.WantVoucher = form.YesNoUnknown - - transaction := dynamo.NewTransaction(). - Delete(dynamo.Keys{PK: link.PK, SK: link.SK}). - Put(provided) + transaction := dynamo.NewTransaction() - return s.dynamoClient.WriteTransaction(ctx, transaction) -} + if errors.As(linkErr, &dynamo.NotFoundError{}) { + var voucher voucherdata.Provided + if err := s.dynamoClient.OneByPartialSK(ctx, provided.PK, dynamo.VoucherKey(""), &voucher); err != nil { + return err + } -func (s *Store) FailVoucher(ctx context.Context, provided *donordata.Provided, voucherKey dynamo.VoucherKeyType) error { - provided.FailedVoucher = provided.Voucher - provided.FailedVoucher.FailedAt = s.now() + transaction. + Delete(dynamo.Keys{PK: provided.PK, SK: voucher.SK}). + Delete(dynamo.Keys{PK: provided.PK, SK: dynamo.ReservedKey(dynamo.VoucherKey)}) + } else { + transaction. + Delete(dynamo.Keys{PK: link.PK, SK: link.SK}) + } provided.Voucher = donordata.Voucher{} provided.WantVoucher = form.YesNoUnknown provided.VoucherInvitedAt = time.Time{} provided.DetailsVerifiedByVoucher = false - transaction := dynamo.NewTransaction(). - Delete(dynamo.Keys{PK: provided.PK, SK: voucherKey}). - Delete(dynamo.Keys{PK: provided.PK, SK: dynamo.ReservedKey(dynamo.VoucherKey)}). - Put(provided) + transaction.Put(provided) return s.dynamoClient.WriteTransaction(ctx, transaction) } + +func (s *Store) FailVoucher(ctx context.Context, provided *donordata.Provided) error { + provided.FailedVoucher = provided.Voucher + provided.FailedVoucher.FailedAt = s.now() + + return s.DeleteVoucher(ctx, provided) +} diff --git a/internal/donor/store_test.go b/internal/donor/store_test.go index fd1eb2d277..1ed09d36b3 100644 --- a/internal/donor/store_test.go +++ b/internal/donor/store_test.go @@ -23,6 +23,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/search" "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" "github.com/ministryofjustice/opg-modernising-lpa/internal/task" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" "github.com/mitchellh/hashstructure/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -1070,7 +1071,7 @@ func TestDonorStoreDeleteVoucher(t *testing.T) { &donordata.Provided{}, }, }). - Return(expectedError) + Return(nil) donorStore := &Store{dynamoClient: dynamoClient} @@ -1079,7 +1080,7 @@ func TestDonorStoreDeleteVoucher(t *testing.T) { VoucherInvitedAt: testNow, WantVoucher: form.Yes, }) - assert.Equal(t, expectedError, err) + assert.Nil(t, err) } func TestDonorStoreDeleteVoucherWhenSessionMissing(t *testing.T) { @@ -1105,14 +1106,125 @@ func TestDonorStoreDeleteVoucherWhenOneBySKErrors(t *testing.T) { assert.Equal(t, expectedError, err) } +func TestDonorStoreDeleteVoucherWhenWriteTransactionError(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "an-id", LpaID: "lpa-id"}) + + dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id")), + sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("hey")), + SK: dynamo.ShareSortKey(dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id"))), + }, nil) + dynamoClient.EXPECT(). + WriteTransaction(ctx, &dynamo.Transaction{ + Deletes: []dynamo.Keys{{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("hey")), + SK: dynamo.ShareSortKey(dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id"))), + }}, + Puts: []any{ + &donordata.Provided{}, + }, + }). + Return(expectedError) + + donorStore := &Store{dynamoClient: dynamoClient} + + err := donorStore.DeleteVoucher(ctx, &donordata.Provided{ + Voucher: donordata.Voucher{FirstNames: "a"}, + VoucherInvitedAt: testNow, + WantVoucher: form.Yes, + }) + assert.Equal(t, expectedError, err) +} + +func TestDonorStoreDeleteVoucherWhenAccessCodeUsed(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "an-id", LpaID: "lpa-id"}) + + dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id")), + sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("hey")), + SK: dynamo.ShareSortKey(dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id"))), + }, dynamo.NotFoundError{}) + dynamoClient. + ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.VoucherKey(""), + voucherdata.Provided{ + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.VoucherKey("voucher-id"), + }, nil) + dynamoClient.EXPECT(). + WriteTransaction(ctx, &dynamo.Transaction{ + Deletes: []dynamo.Keys{ + {PK: dynamo.LpaKey("lpa-id"), SK: dynamo.VoucherKey("voucher-id")}, + {PK: dynamo.LpaKey("lpa-id"), SK: dynamo.ReservedKey(dynamo.VoucherKey)}, + }, + Puts: []any{ + &donordata.Provided{PK: dynamo.LpaKey("lpa-id")}, + }, + }). + Return(nil) + + donorStore := &Store{dynamoClient: dynamoClient} + + err := donorStore.DeleteVoucher(ctx, &donordata.Provided{ + PK: dynamo.LpaKey("lpa-id"), + Voucher: donordata.Voucher{FirstNames: "a"}, + VoucherInvitedAt: testNow, + WantVoucher: form.Yes, + }) + assert.Nil(t, err) +} + +func TestDonorStoreDeleteVoucherWhenExpectOneByPartialSKError(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "an-id", LpaID: "lpa-id"}) + + dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id")), + sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("hey")), + SK: dynamo.ShareSortKey(dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id"))), + }, dynamo.NotFoundError{}) + dynamoClient. + ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.VoucherKey(""), + voucherdata.Provided{ + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.VoucherKey("voucher-id"), + }, expectedError) + + donorStore := &Store{dynamoClient: dynamoClient} + + err := donorStore.DeleteVoucher(ctx, &donordata.Provided{ + PK: dynamo.LpaKey("lpa-id"), + Voucher: donordata.Voucher{FirstNames: "a"}, + VoucherInvitedAt: testNow, + WantVoucher: form.Yes, + }) + assert.Equal(t, expectedError, err) +} + func TestDonorFailVoucher(t *testing.T) { ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "an-id", LpaID: "lpa-id"}) dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOneBySK(ctx, dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id")), + sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("hey")), + SK: dynamo.ShareSortKey(dynamo.VoucherShareSortKey(dynamo.LpaKey("lpa-id"))), + }, dynamo.NotFoundError{}) + dynamoClient. + ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.VoucherKey(""), + voucherdata.Provided{ + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.VoucherKey("voucher-id"), + }, nil) dynamoClient.EXPECT(). WriteTransaction(ctx, &dynamo.Transaction{ Deletes: []dynamo.Keys{ - {PK: dynamo.LpaKey("lpa-id"), SK: dynamo.VoucherKey("a-voucher")}, + {PK: dynamo.LpaKey("lpa-id"), SK: dynamo.VoucherKey("voucher-id")}, {PK: dynamo.LpaKey("lpa-id"), SK: dynamo.ReservedKey(dynamo.VoucherKey)}, }, Puts: []any{ @@ -1133,6 +1245,6 @@ func TestDonorFailVoucher(t *testing.T) { WantVoucher: form.Yes, VoucherInvitedAt: testNow, DetailsVerifiedByVoucher: true, - }, dynamo.VoucherKey("a-voucher")) + }) assert.Equal(t, expectedError, err) } diff --git a/internal/voucher/voucherpage/mock_DonorStore_test.go b/internal/voucher/voucherpage/mock_DonorStore_test.go index e9eb619f3b..c4830ab88f 100644 --- a/internal/voucher/voucherpage/mock_DonorStore_test.go +++ b/internal/voucher/voucherpage/mock_DonorStore_test.go @@ -6,8 +6,6 @@ import ( context "context" donordata "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" - dynamo "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" - mock "github.com/stretchr/testify/mock" ) @@ -24,17 +22,17 @@ func (_m *mockDonorStore) EXPECT() *mockDonorStore_Expecter { return &mockDonorStore_Expecter{mock: &_m.Mock} } -// FailVoucher provides a mock function with given fields: ctx, provided, voucherKey -func (_m *mockDonorStore) FailVoucher(ctx context.Context, provided *donordata.Provided, voucherKey dynamo.VoucherKeyType) error { - ret := _m.Called(ctx, provided, voucherKey) +// FailVoucher provides a mock function with given fields: ctx, provided +func (_m *mockDonorStore) FailVoucher(ctx context.Context, provided *donordata.Provided) error { + ret := _m.Called(ctx, provided) if len(ret) == 0 { panic("no return value specified for FailVoucher") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *donordata.Provided, dynamo.VoucherKeyType) error); ok { - r0 = rf(ctx, provided, voucherKey) + if rf, ok := ret.Get(0).(func(context.Context, *donordata.Provided) error); ok { + r0 = rf(ctx, provided) } else { r0 = ret.Error(0) } @@ -50,14 +48,13 @@ type mockDonorStore_FailVoucher_Call struct { // FailVoucher is a helper method to define mock.On call // - ctx context.Context // - provided *donordata.Provided -// - voucherKey dynamo.VoucherKeyType -func (_e *mockDonorStore_Expecter) FailVoucher(ctx interface{}, provided interface{}, voucherKey interface{}) *mockDonorStore_FailVoucher_Call { - return &mockDonorStore_FailVoucher_Call{Call: _e.mock.On("FailVoucher", ctx, provided, voucherKey)} +func (_e *mockDonorStore_Expecter) FailVoucher(ctx interface{}, provided interface{}) *mockDonorStore_FailVoucher_Call { + return &mockDonorStore_FailVoucher_Call{Call: _e.mock.On("FailVoucher", ctx, provided)} } -func (_c *mockDonorStore_FailVoucher_Call) Run(run func(ctx context.Context, provided *donordata.Provided, voucherKey dynamo.VoucherKeyType)) *mockDonorStore_FailVoucher_Call { +func (_c *mockDonorStore_FailVoucher_Call) Run(run func(ctx context.Context, provided *donordata.Provided)) *mockDonorStore_FailVoucher_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*donordata.Provided), args[2].(dynamo.VoucherKeyType)) + run(args[0].(context.Context), args[1].(*donordata.Provided)) }) return _c } @@ -67,7 +64,7 @@ func (_c *mockDonorStore_FailVoucher_Call) Return(_a0 error) *mockDonorStore_Fai return _c } -func (_c *mockDonorStore_FailVoucher_Call) RunAndReturn(run func(context.Context, *donordata.Provided, dynamo.VoucherKeyType) error) *mockDonorStore_FailVoucher_Call { +func (_c *mockDonorStore_FailVoucher_Call) RunAndReturn(run func(context.Context, *donordata.Provided) error) *mockDonorStore_FailVoucher_Call { _c.Call.Return(run) return _c } diff --git a/internal/voucher/voucherpage/register.go b/internal/voucher/voucherpage/register.go index ca8d1a1c8a..cd89e2cc64 100644 --- a/internal/voucher/voucherpage/register.go +++ b/internal/voucher/voucherpage/register.go @@ -12,7 +12,6 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/dashboard/dashboarddata" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" - "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/identity" "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" @@ -48,7 +47,7 @@ type LpaStoreClient interface { type DonorStore interface { GetAny(ctx context.Context) (*donordata.Provided, error) Put(ctx context.Context, donor *donordata.Provided) error - FailVoucher(ctx context.Context, provided *donordata.Provided, voucherKey dynamo.VoucherKeyType) error + FailVoucher(ctx context.Context, provided *donordata.Provided) error } type NotifyClient interface { diff --git a/internal/voucher/voucherpage/vouch_failer.go b/internal/voucher/voucherpage/vouch_failer.go index 5853477e72..67445ef9e2 100644 --- a/internal/voucher/voucherpage/vouch_failer.go +++ b/internal/voucher/voucherpage/vouch_failer.go @@ -29,7 +29,7 @@ func makeVouchFailer(donorStore DonorStore, notifyClient NotifyClient, appPublic return fmt.Errorf("could not send email: %w", err) } - if err := donorStore.FailVoucher(ctx, donor, provided.SK); err != nil { + if err := donorStore.FailVoucher(ctx, donor); err != nil { return fmt.Errorf("could not fail vouch: %w", err) } diff --git a/internal/voucher/voucherpage/vouch_failer_test.go b/internal/voucher/voucherpage/vouch_failer_test.go index f1aa747221..97095375c8 100644 --- a/internal/voucher/voucherpage/vouch_failer_test.go +++ b/internal/voucher/voucherpage/vouch_failer_test.go @@ -38,7 +38,7 @@ func TestVouchFailer(t *testing.T) { GetAny(ctx). Return(donor, nil) donorStore.EXPECT(). - FailVoucher(ctx, donor, provided.SK). + FailVoucher(ctx, donor). Return(nil) notifyClient := newMockNotifyClient(t) @@ -91,7 +91,7 @@ func TestVouchFailerWhenDonorStorePutErrors(t *testing.T) { GetAny(mock.Anything). Return(&donordata.Provided{}, nil) donorStore.EXPECT(). - FailVoucher(mock.Anything, mock.Anything, mock.Anything). + FailVoucher(mock.Anything, mock.Anything). Return(expectedError) notifyClient := newMockNotifyClient(t) diff --git a/scripts/pre-commit/prevent_unsafe_chains.go b/scripts/pre-commit/prevent_unsafe_chains.go new file mode 100644 index 0000000000..2355dbb6d4 --- /dev/null +++ b/scripts/pre-commit/prevent_unsafe_chains.go @@ -0,0 +1,72 @@ +package main + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "strings" +) + +func getChangedFiles() ([]string, error) { + cmd := exec.Command("git", "diff", "--cached", "--name-only", "--diff-filter=ACM", "*.cy.js") + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("error getting changed files: %v", err) + } + + files := strings.Fields(string(output)) + return files, nil +} + +func checkCypressUnsafeChaining(filePath string) error { + content, err := os.ReadFile(filePath) + if err != nil { + return fmt.Errorf("error reading file %s: %v", filePath, err) + } + + unsafeChainRegex := regexp.MustCompile(`\.(clear|click|check|type|focus)\(.*\)\.\w+`) + safeChainRegex := regexp.MustCompile(`\.(clear|click|check|type|focus)\(.*\)\.(then|and)`) + + matches := unsafeChainRegex.FindAll(content, -1) + for _, match := range matches { + if !safeChainRegex.Match(match) { + return errors.New("unsafe chain detected") + } + } + + return nil +} + +func main() { + changedFiles, err := getChangedFiles() + if err != nil { + log.Fatal(err) + } + + errFiles := "" + for _, file := range changedFiles { + err := checkCypressUnsafeChaining(file) + if err != nil { + errFiles += file + "\n" + } + } + + if errFiles != "" { + fmt.Fprintln(os.Stderr, fmt.Sprintf(`Unsafe chained command found in: + +%s +It is unsafe to chain further commands after clear(), click(), check(), type() or focus(), + +Bad: +cy.get('#selector').clear().type('value') + +Good: +cy.get('#selector').clear(); +cy.get('#selector').type('value')`, errFiles)) + os.Exit(1) + } + os.Exit(0) +}