From e34df89ab9e9cfb3298f48ee6bb265841c87433c Mon Sep 17 00:00:00 2001 From: Joshua Hawxwell Date: Thu, 25 Apr 2024 15:17:32 +0100 Subject: [PATCH] MLPAB-1777 Add PERFECT update type and status (#180) --- docs/openapi/openapi.yaml | 1 + internal/shared/lpa.go | 1 + lambda/update/perfect.go | 47 ++++++++++++ lambda/update/perfect_test.go | 126 +++++++++++++++++++++++++++++++++ lambda/update/register.go | 26 +------ lambda/update/register_test.go | 95 ++----------------------- lambda/update/validate.go | 2 + 7 files changed, 185 insertions(+), 113 deletions(-) create mode 100644 lambda/update/perfect.go create mode 100644 lambda/update/perfect_test.go diff --git a/docs/openapi/openapi.yaml b/docs/openapi/openapi.yaml index de17c5d7..3ee16218 100644 --- a/docs/openapi/openapi.yaml +++ b/docs/openapi/openapi.yaml @@ -265,6 +265,7 @@ components: - CERTIFICATE_PROVIDER_SIGN - ATTORNEY_SIGN - TRUST_CORPORATION_SIGN + - PERFECT - REGISTER changes: type: array diff --git a/internal/shared/lpa.go b/internal/shared/lpa.go index a81cc331..659d2a50 100644 --- a/internal/shared/lpa.go +++ b/internal/shared/lpa.go @@ -47,5 +47,6 @@ type LpaStatus string const ( LpaStatusProcessing = LpaStatus("processing") + LpaStatusPerfect = LpaStatus("perfect") LpaStatusRegistered = LpaStatus("registered") ) diff --git a/lambda/update/perfect.go b/lambda/update/perfect.go new file mode 100644 index 00000000..f78b3287 --- /dev/null +++ b/lambda/update/perfect.go @@ -0,0 +1,47 @@ +package main + +import ( + "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" +) + +type Perfect struct{} + +func (r Perfect) Apply(lpa *shared.Lpa) []shared.FieldError { + if lpa.Status != shared.LpaStatusProcessing { + return []shared.FieldError{{Source: "/type", Detail: "status must be processing to make perfect"}} + } + + if lpa.SignedAt.IsZero() { + return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed"}} + } + + if lpa.CertificateProvider.SignedAt == nil || lpa.CertificateProvider.SignedAt.IsZero() { + return []shared.FieldError{{Source: "/type", Detail: "lpa must have a certificate"}} + } + + for _, a := range lpa.Attorneys { + if a.SignedAt == nil || a.SignedAt.IsZero() { + return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by attorneys"}} + } + } + + for _, t := range lpa.TrustCorporations { + for _, s := range t.Signatories { + if s.SignedAt.IsZero() { + return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by trust corporations"}} + } + } + } + + lpa.Status = shared.LpaStatusPerfect + + return nil +} + +func validatePerfect(changes []shared.Change) (Perfect, []shared.FieldError) { + if len(changes) > 0 { + return Perfect{}, []shared.FieldError{{Source: "/changes", Detail: "expected empty"}} + } + + return Perfect{}, nil +} diff --git a/lambda/update/perfect_test.go b/lambda/update/perfect_test.go new file mode 100644 index 00000000..e6801279 --- /dev/null +++ b/lambda/update/perfect_test.go @@ -0,0 +1,126 @@ +package main + +import ( + "testing" + "time" + + "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" + "github.com/stretchr/testify/assert" +) + +func TestPerfectApply(t *testing.T) { + now := time.Now() + + lpa := &shared.Lpa{ + Status: shared.LpaStatusProcessing, + LpaInit: shared.LpaInit{ + SignedAt: now, + CertificateProvider: shared.CertificateProvider{ + SignedAt: &now, + }, + Attorneys: []shared.Attorney{{ + SignedAt: &now, + }}, + TrustCorporations: []shared.TrustCorporation{{ + Signatories: []shared.Signatory{{ + SignedAt: now, + }}, + }}, + }, + } + + errors := Perfect{}.Apply(lpa) + assert.Nil(t, errors) + assert.Equal(t, shared.LpaStatusPerfect, lpa.Status) +} + +func TestPerfectApplyWhenUnsigned(t *testing.T) { + now := time.Now() + + testcases := map[string]struct { + lpa *shared.Lpa + errors []shared.FieldError + }{ + "lpa": { + lpa: &shared.Lpa{ + Status: shared.LpaStatusProcessing, + LpaInit: shared.LpaInit{ + CertificateProvider: shared.CertificateProvider{SignedAt: &now}, + Attorneys: []shared.Attorney{{SignedAt: &now}}, + TrustCorporations: []shared.TrustCorporation{{ + Signatories: []shared.Signatory{{SignedAt: now}}, + }}, + }, + }, + errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed"}}, + }, + "certificate provider": { + lpa: &shared.Lpa{ + Status: shared.LpaStatusProcessing, + LpaInit: shared.LpaInit{ + SignedAt: now, + CertificateProvider: shared.CertificateProvider{}, + Attorneys: []shared.Attorney{{SignedAt: &now}}, + TrustCorporations: []shared.TrustCorporation{{ + Signatories: []shared.Signatory{{SignedAt: now}}, + }}, + }, + }, + errors: []shared.FieldError{{Source: "/type", Detail: "lpa must have a certificate"}}, + }, + "attorney": { + lpa: &shared.Lpa{ + Status: shared.LpaStatusProcessing, + LpaInit: shared.LpaInit{ + SignedAt: now, + CertificateProvider: shared.CertificateProvider{SignedAt: &now}, + Attorneys: []shared.Attorney{{SignedAt: &now}, {}}, + TrustCorporations: []shared.TrustCorporation{{ + Signatories: []shared.Signatory{{SignedAt: now}}, + }}, + }, + }, + errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by attorneys"}}, + }, + "trust corporation": { + lpa: &shared.Lpa{ + Status: shared.LpaStatusProcessing, + LpaInit: shared.LpaInit{ + SignedAt: now, + CertificateProvider: shared.CertificateProvider{SignedAt: &now}, + Attorneys: []shared.Attorney{{SignedAt: &now}}, + TrustCorporations: []shared.TrustCorporation{{ + Signatories: []shared.Signatory{{SignedAt: now}, {}}, + }}, + }, + }, + errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by trust corporations"}}, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + errors := Perfect{}.Apply(tc.lpa) + assert.Equal(t, tc.errors, errors) + }) + } +} + +func TestRegisterApplyWhenNotProcessing(t *testing.T) { + for _, status := range []shared.LpaStatus{shared.LpaStatusPerfect, shared.LpaStatusRegistered} { + t.Run(string(status), func(t *testing.T) { + errors := Perfect{}.Apply(&shared.Lpa{Status: status}) + assert.Equal(t, []shared.FieldError{{Source: "/type", Detail: "status must be processing to make perfect"}}, errors) + }) + } +} + +func TestValidatePerfect(t *testing.T) { + _, errors := validatePerfect(nil) + assert.Nil(t, errors) +} + +func TestValidatePerfectWhenChanges(t *testing.T) { + _, errors := validatePerfect([]shared.Change{{}}) + assert.Equal(t, []shared.FieldError{{Source: "/changes", Detail: "expected empty"}}, errors) +} diff --git a/lambda/update/register.go b/lambda/update/register.go index de4ee571..8922f6b4 100644 --- a/lambda/update/register.go +++ b/lambda/update/register.go @@ -9,30 +9,8 @@ import ( type Register struct{} func (r Register) Apply(lpa *shared.Lpa) []shared.FieldError { - if lpa.RegistrationDate != nil && !lpa.RegistrationDate.IsZero() { - return []shared.FieldError{{Source: "/type", Detail: "lpa already registered"}} - } - - if lpa.SignedAt.IsZero() { - return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed"}} - } - - if lpa.CertificateProvider.SignedAt == nil || lpa.CertificateProvider.SignedAt.IsZero() { - return []shared.FieldError{{Source: "/type", Detail: "lpa must have a certificate"}} - } - - for _, a := range lpa.Attorneys { - if a.SignedAt == nil || a.SignedAt.IsZero() { - return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by attorneys"}} - } - } - - for _, t := range lpa.TrustCorporations { - for _, s := range t.Signatories { - if s.SignedAt.IsZero() { - return []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by trust corporations"}} - } - } + if lpa.Status != shared.LpaStatusPerfect { + return []shared.FieldError{{Source: "/type", Detail: "status must be perfect to register"}} } now := time.Now().UTC() diff --git a/lambda/update/register_test.go b/lambda/update/register_test.go index 89ec1f1f..ff0e0f75 100644 --- a/lambda/update/register_test.go +++ b/lambda/update/register_test.go @@ -12,20 +12,7 @@ func TestRegisterApply(t *testing.T) { now := time.Now() lpa := &shared.Lpa{ - LpaInit: shared.LpaInit{ - SignedAt: now, - CertificateProvider: shared.CertificateProvider{ - SignedAt: &now, - }, - Attorneys: []shared.Attorney{{ - SignedAt: &now, - }}, - TrustCorporations: []shared.TrustCorporation{{ - Signatories: []shared.Signatory{{ - SignedAt: now, - }}, - }}, - }, + Status: shared.LpaStatusPerfect, } errors := Register{}.Apply(lpa) @@ -34,85 +21,15 @@ func TestRegisterApply(t *testing.T) { assert.Equal(t, shared.LpaStatusRegistered, lpa.Status) } -func TestRegisterApplyWhenUnsigned(t *testing.T) { - now := time.Now() - - testcases := map[string]struct { - lpa *shared.Lpa - errors []shared.FieldError - }{ - "lpa": { - lpa: &shared.Lpa{ - LpaInit: shared.LpaInit{ - CertificateProvider: shared.CertificateProvider{SignedAt: &now}, - Attorneys: []shared.Attorney{{SignedAt: &now}}, - TrustCorporations: []shared.TrustCorporation{{ - Signatories: []shared.Signatory{{SignedAt: now}}, - }}, - }, - }, - errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed"}}, - }, - "certificate provider": { - lpa: &shared.Lpa{ - LpaInit: shared.LpaInit{ - SignedAt: now, - CertificateProvider: shared.CertificateProvider{}, - Attorneys: []shared.Attorney{{SignedAt: &now}}, - TrustCorporations: []shared.TrustCorporation{{ - Signatories: []shared.Signatory{{SignedAt: now}}, - }}, - }, - }, - errors: []shared.FieldError{{Source: "/type", Detail: "lpa must have a certificate"}}, - }, - "attorney": { - lpa: &shared.Lpa{ - LpaInit: shared.LpaInit{ - SignedAt: now, - CertificateProvider: shared.CertificateProvider{SignedAt: &now}, - Attorneys: []shared.Attorney{{SignedAt: &now}, {}}, - TrustCorporations: []shared.TrustCorporation{{ - Signatories: []shared.Signatory{{SignedAt: now}}, - }}, - }, - }, - errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by attorneys"}}, - }, - "trust corporation": { - lpa: &shared.Lpa{ - LpaInit: shared.LpaInit{ - SignedAt: now, - CertificateProvider: shared.CertificateProvider{SignedAt: &now}, - Attorneys: []shared.Attorney{{SignedAt: &now}}, - TrustCorporations: []shared.TrustCorporation{{ - Signatories: []shared.Signatory{{SignedAt: now}, {}}, - }}, - }, - }, - errors: []shared.FieldError{{Source: "/type", Detail: "lpa must be signed by trust corporations"}}, - }, - } - - for name, tc := range testcases { - t.Run(name, func(t *testing.T) { - errors := Register{}.Apply(tc.lpa) - assert.Equal(t, tc.errors, errors) +func TestRegisterApplyWhenNotPerfect(t *testing.T) { + for _, status := range []shared.LpaStatus{shared.LpaStatusProcessing, shared.LpaStatusRegistered} { + t.Run(string(status), func(t *testing.T) { + errors := Register{}.Apply(&shared.Lpa{Status: status}) + assert.Equal(t, []shared.FieldError{{Source: "/type", Detail: "status must be perfect to register"}}, errors) }) } } -func TestRegisterApplyWhenAlreadyRegistered(t *testing.T) { - now := time.Now() - - lpa := &shared.Lpa{ - RegistrationDate: &now, - } - - errors := Register{}.Apply(lpa) - assert.Equal(t, []shared.FieldError{{Source: "/type", Detail: "lpa already registered"}}, errors) -} - func TestValidateRegister(t *testing.T) { _, errors := validateRegister(nil) assert.Nil(t, errors) diff --git a/lambda/update/validate.go b/lambda/update/validate.go index b0c45f73..00578348 100644 --- a/lambda/update/validate.go +++ b/lambda/update/validate.go @@ -16,6 +16,8 @@ func validateUpdate(update shared.Update, lpa *shared.Lpa) (Applyable, []shared. return validateAttorneySign(update.Changes) case "TRUST_CORPORATION_SIGN": return validateTrustCorporationSign(update.Changes) + case "PERFECT": + return validatePerfect(update.Changes) case "REGISTER": return validateRegister(update.Changes) default: