diff --git a/internal/app/app.go b/internal/app/app.go index ea2c262950..95bb18370b 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -164,6 +164,7 @@ func App( shareCodeStore, dashboardStore, errorHandler, + lpaStoreResolvingService, ) supporterpage.Register( diff --git a/internal/dynamo/keys.go b/internal/dynamo/keys.go index 9b1499100e..8d3e7557ae 100644 --- a/internal/dynamo/keys.go +++ b/internal/dynamo/keys.go @@ -19,6 +19,7 @@ const ( memberPrefix = "MEMBER" memberInvitePrefix = "MEMBERINVITE" memberIDPrefix = "MEMBERID" + voucherPrefix = "VOUCHER" metadataPrefix = "METADATA" donorSharePrefix = "DONORSHARE" donorInvitePrefix = "DONORINVITE" @@ -68,6 +69,8 @@ func readKey(s string) (any, error) { return MetadataKeyType(s), nil case donorInvitePrefix: return DonorInviteKeyType(s), nil + case voucherPrefix: + return VoucherKeyType(s), nil default: return nil, errors.New("unknown key prefix") } @@ -194,6 +197,16 @@ func MemberIDKey(memberID string) MemberIDKeyType { return MemberIDKeyType(memberIDPrefix + "#" + memberID) } +type VoucherKeyType string + +func (t VoucherKeyType) SK() string { return string(t) } + +// VoucherKey is used as the SK (with LpaKey as PK) for voucher entered +// information. +func VoucherKey(s string) VoucherKeyType { + return VoucherKeyType(voucherPrefix + "#" + s) +} + type MetadataKeyType string func (t MetadataKeyType) SK() string { return string(t) } diff --git a/internal/dynamo/keys_test.go b/internal/dynamo/keys_test.go index 0c3bb469a2..2f355fc0a9 100644 --- a/internal/dynamo/keys_test.go +++ b/internal/dynamo/keys_test.go @@ -79,6 +79,7 @@ func TestSK(t *testing.T) { "OrganisationKey": {OrganisationKey("S"), "ORGANISATION#S"}, "MetadataKey": {MetadataKey("S"), "METADATA#S"}, "DonorInviteKey": {DonorInviteKey(OrganisationKey("org-id"), LpaKey("lpa-id")), "DONORINVITE#org-id#lpa-id"}, + "VoucherKey": {VoucherKey("S"), "VOUCHER#S"}, } for name, tc := range testcases { diff --git a/internal/templatefn/fn.go b/internal/templatefn/fn.go index 0a1febc14c..2ad6547c50 100644 --- a/internal/templatefn/fn.go +++ b/internal/templatefn/fn.go @@ -18,6 +18,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore" "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher" ) // Globals contains values that are used in templates and do not change as the @@ -87,6 +88,7 @@ func All(globals *Globals) map[string]any { "checkboxEq": checkboxEq, "lpaDecisions": lpaDecisions, "summaryRow": summaryRow, + "voucherCanGoTo": voucher.CanGoTo, } } diff --git a/internal/voucher/mock_DynamoClient_test.go b/internal/voucher/mock_DynamoClient_test.go new file mode 100644 index 0000000000..8927e29c6f --- /dev/null +++ b/internal/voucher/mock_DynamoClient_test.go @@ -0,0 +1,133 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package voucher + +import ( + context "context" + + dynamo "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" + mock "github.com/stretchr/testify/mock" +) + +// mockDynamoClient is an autogenerated mock type for the DynamoClient type +type mockDynamoClient struct { + mock.Mock +} + +type mockDynamoClient_Expecter struct { + mock *mock.Mock +} + +func (_m *mockDynamoClient) EXPECT() *mockDynamoClient_Expecter { + return &mockDynamoClient_Expecter{mock: &_m.Mock} +} + +// One provides a mock function with given fields: ctx, pk, sk, v +func (_m *mockDynamoClient) One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error { + ret := _m.Called(ctx, pk, sk, v) + + if len(ret) == 0 { + panic("no return value specified for One") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { + r0 = rf(ctx, pk, sk, v) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockDynamoClient_One_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'One' +type mockDynamoClient_One_Call struct { + *mock.Call +} + +// One is a helper method to define mock.On call +// - ctx context.Context +// - pk dynamo.PK +// - sk dynamo.SK +// - v interface{} +func (_e *mockDynamoClient_Expecter) One(ctx interface{}, pk interface{}, sk interface{}, v interface{}) *mockDynamoClient_One_Call { + return &mockDynamoClient_One_Call{Call: _e.mock.On("One", ctx, pk, sk, v)} +} + +func (_c *mockDynamoClient_One_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{})) *mockDynamoClient_One_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) + }) + return _c +} + +func (_c *mockDynamoClient_One_Call) Return(_a0 error) *mockDynamoClient_One_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockDynamoClient_One_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockDynamoClient_One_Call { + _c.Call.Return(run) + return _c +} + +// WriteTransaction provides a mock function with given fields: ctx, transaction +func (_m *mockDynamoClient) WriteTransaction(ctx context.Context, transaction *dynamo.Transaction) error { + ret := _m.Called(ctx, transaction) + + if len(ret) == 0 { + panic("no return value specified for WriteTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *dynamo.Transaction) error); ok { + r0 = rf(ctx, transaction) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockDynamoClient_WriteTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteTransaction' +type mockDynamoClient_WriteTransaction_Call struct { + *mock.Call +} + +// WriteTransaction is a helper method to define mock.On call +// - ctx context.Context +// - transaction *dynamo.Transaction +func (_e *mockDynamoClient_Expecter) WriteTransaction(ctx interface{}, transaction interface{}) *mockDynamoClient_WriteTransaction_Call { + return &mockDynamoClient_WriteTransaction_Call{Call: _e.mock.On("WriteTransaction", ctx, transaction)} +} + +func (_c *mockDynamoClient_WriteTransaction_Call) Run(run func(ctx context.Context, transaction *dynamo.Transaction)) *mockDynamoClient_WriteTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*dynamo.Transaction)) + }) + return _c +} + +func (_c *mockDynamoClient_WriteTransaction_Call) Return(_a0 error) *mockDynamoClient_WriteTransaction_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockDynamoClient_WriteTransaction_Call) RunAndReturn(run func(context.Context, *dynamo.Transaction) error) *mockDynamoClient_WriteTransaction_Call { + _c.Call.Return(run) + return _c +} + +// newMockDynamoClient creates a new instance of mockDynamoClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockDynamoClient(t interface { + mock.TestingT + Cleanup(func()) +}) *mockDynamoClient { + mock := &mockDynamoClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/voucher/path.go b/internal/voucher/path.go index f2d56cf548..1038d4e569 100644 --- a/internal/voucher/path.go +++ b/internal/voucher/path.go @@ -2,12 +2,18 @@ package voucher import ( "net/http" + "strings" "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) const ( - PathTaskList = Path("/task-list") + PathTaskList = Path("/task-list") + PathConfirmYourName = Path("/confirm-your-name") + PathVerifyDonorDetails = Path("/verify-donor-details") + PathConfirmYourIdentity = Path("/confirm-your-identity") + PathSignTheDeclaration = Path("/sign-the-declaration") ) type Path string @@ -29,3 +35,36 @@ func (p Path) Redirect(w http.ResponseWriter, r *http.Request, appData appcontex http.Redirect(w, r, appData.Lang.URL(rurl), http.StatusFound) return nil } + +func (p Path) canVisit(provided *voucherdata.Provided) bool { + switch p { + case PathVerifyDonorDetails: + return provided.Tasks.ConfirmYourName.Completed() + + case PathConfirmYourIdentity: + return provided.Tasks.ConfirmYourName.Completed() && + provided.Tasks.VerifyDonorDetails.Completed() + + case PathSignTheDeclaration: + return provided.Tasks.ConfirmYourName.Completed() && + provided.Tasks.VerifyDonorDetails.Completed() && + provided.Tasks.ConfirmYourIdentity.Completed() + + default: + return true + } +} + +func CanGoTo(provided *voucherdata.Provided, url string) bool { + path, _, _ := strings.Cut(url, "?") + if path == "" { + return false + } + + if strings.HasPrefix(path, "/voucher/") { + _, voucherPath, _ := strings.Cut(strings.TrimPrefix(path, "/voucher/"), "/") + return Path("/" + voucherPath).canVisit(provided) + } + + return true +} diff --git a/internal/voucher/path_test.go b/internal/voucher/path_test.go index 32d0131a3d..07794eef22 100644 --- a/internal/voucher/path_test.go +++ b/internal/voucher/path_test.go @@ -7,6 +7,8 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" + "github.com/ministryofjustice/opg-modernising-lpa/internal/task" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" "github.com/stretchr/testify/assert" ) @@ -43,3 +45,85 @@ func TestPathRedirectWhenFrom(t *testing.T) { assert.Equal(t, http.StatusFound, resp.StatusCode) assert.Equal(t, "/x", resp.Header.Get("Location")) } + +func TestCanGoTo(t *testing.T) { + testcases := map[string]struct { + provided *voucherdata.Provided + url string + expected bool + }{ + "empty path": { + provided: &voucherdata.Provided{}, + url: "", + expected: false, + }, + "unexpected path": { + provided: &voucherdata.Provided{}, + url: "/whatever", + expected: true, + }, + "unrestricted path": { + provided: &voucherdata.Provided{}, + url: PathTaskList.Format("123"), + expected: true, + }, + "verify donor details": { + provided: &voucherdata.Provided{}, + url: PathVerifyDonorDetails.Format("123"), + expected: false, + }, + "verify donor details when previous task completed": { + provided: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ConfirmYourName: task.StateCompleted}, + }, + url: PathVerifyDonorDetails.Format("123"), + expected: true, + }, + "confirm your identity": { + provided: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + }, + }, + url: PathConfirmYourIdentity.Format("123"), + expected: false, + }, + "confirm your identity when previous task completed": { + provided: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + VerifyDonorDetails: task.StateCompleted, + }, + }, + url: PathConfirmYourIdentity.Format("123"), + expected: true, + }, + "sign the declaration": { + provided: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + VerifyDonorDetails: task.StateCompleted, + }, + }, + url: PathSignTheDeclaration.Format("123"), + expected: false, + }, + "sign the declaration when previous task completed": { + provided: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + VerifyDonorDetails: task.StateCompleted, + ConfirmYourIdentity: task.StateCompleted, + }, + }, + url: PathSignTheDeclaration.Format("123"), + expected: true, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.expected, CanGoTo(tc.provided, tc.url)) + }) + } +} diff --git a/internal/voucher/store.go b/internal/voucher/store.go index 342f8467cc..bf845513aa 100644 --- a/internal/voucher/store.go +++ b/internal/voucher/store.go @@ -2,18 +2,79 @@ package voucher import ( "context" + "errors" + "time" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dashboard/dashboarddata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) +type DynamoClient interface { + One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error + WriteTransaction(ctx context.Context, transaction *dynamo.Transaction) error +} + type Store struct { - dynamoClient any + dynamoClient DynamoClient + now func() time.Time +} + +func NewStore(dynamoClient DynamoClient) *Store { + return &Store{dynamoClient: dynamoClient, now: time.Now} } -func NewStore(dynamoClient any) *Store { - return &Store{dynamoClient: dynamoClient} +func (s *Store) Create(ctx context.Context, shareCode sharecodedata.Link, email string) (*voucherdata.Provided, error) { + data, err := appcontext.SessionFromContext(ctx) + if err != nil { + return nil, err + } + + if data.LpaID == "" || data.SessionID == "" { + return nil, errors.New("voucher.Store.Create requires LpaID and SessionID") + } + + provided := &voucherdata.Provided{ + PK: dynamo.LpaKey(data.LpaID), + SK: dynamo.VoucherKey(data.SessionID), + LpaID: data.LpaID, + UpdatedAt: s.now(), + Email: email, + } + + transaction := dynamo.NewTransaction(). + Create(provided). + Create(dashboarddata.LpaLink{ + PK: dynamo.LpaKey(data.LpaID), + SK: dynamo.SubKey(data.SessionID), + DonorKey: shareCode.LpaOwnerKey, + ActorType: actor.TypeVoucher, + UpdatedAt: s.now(), + }). + Delete(dynamo.Keys{PK: shareCode.PK, SK: shareCode.SK}) + + if err := s.dynamoClient.WriteTransaction(ctx, transaction); err != nil { + return nil, err + } + + return provided, err } -func (s *Store) Create(ctx context.Context, shareCode sharecodedata.Link, email string) (any, error) { - return nil, nil +func (s *Store) Get(ctx context.Context) (*voucherdata.Provided, error) { + data, err := appcontext.SessionFromContext(ctx) + if err != nil { + return nil, err + } + + if data.LpaID == "" || data.SessionID == "" { + return nil, errors.New("voucher.Store.Get requires LpaID and SessionID") + } + + var provided voucherdata.Provided + err = s.dynamoClient.One(ctx, dynamo.LpaKey(data.LpaID), dynamo.VoucherKey(data.SessionID), &provided) + + return &provided, err } diff --git a/internal/voucher/store_test.go b/internal/voucher/store_test.go index 8d5a474721..cc5aae8283 100644 --- a/internal/voucher/store_test.go +++ b/internal/voucher/store_test.go @@ -2,23 +2,193 @@ package voucher import ( "context" + "encoding/json" + "errors" "testing" + "time" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid" + "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dashboard/dashboarddata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) -var ctx = context.WithValue(context.Background(), "a", "b") +var ( + ctx = context.WithValue(context.Background(), "a", "b") + expectedError = errors.New("expected") +) + +func (m *mockDynamoClient) ExpectOne(ctx, pk, sk, data interface{}, err error) { + m. + On("One", ctx, pk, sk, mock.Anything). + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { + b, _ := json.Marshal(data) + json.Unmarshal(b, v) + return err + }). + Once() +} func TestNewStore(t *testing.T) { - s := NewStore("a") + dynamoClient := newMockDynamoClient(t) - assert.Equal(t, "a", s.dynamoClient) + store := NewStore(dynamoClient) + assert.Equal(t, dynamoClient, store.dynamoClient) } -func TestStoreCreate(t *testing.T) { - store := Store{} - result, err := store.Create(ctx, sharecodedata.Link{}, "") +func TestVoucherStoreCreate(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{LpaID: "123", SessionID: "456"}) + now := time.Now() + uid := actoruid.New() + details := &voucherdata.Provided{ + PK: dynamo.LpaKey("123"), + SK: dynamo.VoucherKey("456"), + LpaID: "123", + UpdatedAt: now, + Email: "a@example.com", + } + + shareCode := sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("123")), + SK: dynamo.ShareSortKey(dynamo.MetadataKey("123")), + ActorUID: uid, + UpdatedAt: now, + LpaOwnerKey: dynamo.LpaOwnerKey(dynamo.DonorKey("donor")), + } + + expectedTransaction := &dynamo.Transaction{ + Creates: []any{ + details, + dashboarddata.LpaLink{ + PK: dynamo.LpaKey("123"), + SK: dynamo.SubKey("456"), + DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("donor")), + ActorType: actor.TypeVoucher, + UpdatedAt: now, + }, + }, + Deletes: []dynamo.Keys{ + { + PK: dynamo.ShareKey(dynamo.VoucherShareKey("123")), + SK: dynamo.ShareSortKey(dynamo.MetadataKey("123")), + }, + }, + } + + dynamoClient := newMockDynamoClient(t) + dynamoClient.EXPECT(). + WriteTransaction(ctx, expectedTransaction). + Return(nil) + + store := Store{dynamoClient: dynamoClient, now: func() time.Time { return now }} + + provided, err := store.Create(ctx, shareCode, "a@example.com") assert.Nil(t, err) - assert.Nil(t, result) + assert.Equal(t, details, provided) +} + +func TestVoucherStoreCreateWhenSessionMissing(t *testing.T) { + ctx := context.Background() + + store := &Store{dynamoClient: nil, now: nil} + + _, err := store.Create(ctx, sharecodedata.Link{}, "") + assert.Equal(t, appcontext.SessionMissingError{}, err) +} + +func TestVoucherStoreCreateWhenSessionMissingRequiredData(t *testing.T) { + testcases := map[string]*appcontext.Session{ + "LpaID": {SessionID: "456"}, + "SessionID": {LpaID: "123"}, + } + + for name, sessionData := range testcases { + t.Run(name, func(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), sessionData) + + store := &Store{} + + _, err := store.Create(ctx, sharecodedata.Link{}, "") + assert.NotNil(t, err) + }) + } +} + +func TestVoucherStoreCreateWhenWriteTransactionError(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{LpaID: "123", SessionID: "456"}) + now := time.Now() + + dynamoClient := newMockDynamoClient(t) + dynamoClient.EXPECT(). + WriteTransaction(mock.Anything, mock.Anything). + Return(expectedError) + + store := &Store{dynamoClient: dynamoClient, now: func() time.Time { return now }} + + _, err := store.Create(ctx, sharecodedata.Link{ + PK: dynamo.ShareKey(dynamo.VoucherShareKey("123")), + SK: dynamo.ShareSortKey(dynamo.MetadataKey("123")), + }, "") + assert.Equal(t, expectedError, err) +} + +func TestVoucherStoreGet(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{LpaID: "123", SessionID: "456"}) + + dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.VoucherKey("456"), + &voucherdata.Provided{LpaID: "123"}, nil) + + store := &Store{dynamoClient: dynamoClient, now: nil} + + provided, err := store.Get(ctx) + assert.Nil(t, err) + assert.Equal(t, &voucherdata.Provided{LpaID: "123"}, provided) +} + +func TestVoucherStoreGetWhenSessionMissing(t *testing.T) { + ctx := context.Background() + + store := &Store{dynamoClient: nil, now: nil} + + _, err := store.Get(ctx) + assert.Equal(t, appcontext.SessionMissingError{}, err) +} + +func TestVoucherStoreGetMissingLpaIDInSession(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "456"}) + + store := &Store{} + + _, err := store.Get(ctx) + assert.Equal(t, errors.New("voucher.Store.Get requires LpaID and SessionID"), err) +} + +func TestVoucherStoreGetMissingSessionIDInSession(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{LpaID: "123"}) + + store := &Store{} + + _, err := store.Get(ctx) + assert.Equal(t, errors.New("voucher.Store.Get requires LpaID and SessionID"), err) +} + +func TestVoucherStoreGetOnError(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{LpaID: "123", SessionID: "456"}) + + dynamoClient := newMockDynamoClient(t) + dynamoClient. + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.VoucherKey("456"), + &voucherdata.Provided{LpaID: "123"}, expectedError) + + store := &Store{dynamoClient: dynamoClient, now: nil} + + _, err := store.Get(ctx) + assert.Equal(t, expectedError, err) } diff --git a/internal/voucher/voucherdata/provided.go b/internal/voucher/voucherdata/provided.go new file mode 100644 index 0000000000..59193e7df3 --- /dev/null +++ b/internal/voucher/voucherdata/provided.go @@ -0,0 +1,29 @@ +package voucherdata + +import ( + "time" + + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" + "github.com/ministryofjustice/opg-modernising-lpa/internal/task" +) + +// Provided contains the information a voucher has given +type Provided struct { + PK dynamo.LpaKeyType + SK dynamo.VoucherKeyType + // LpaID is for the LPA the voucher is provided a vouch for + LpaID string + // UpdatedAt is the time that this data was last updated + UpdatedAt time.Time + // Tasks shows the state of the actions the voucher will do + Tasks Tasks + // Email is the email address of the voucher + Email string +} + +type Tasks struct { + ConfirmYourName task.State + VerifyDonorDetails task.State + ConfirmYourIdentity task.State + SignTheDeclaration task.State +} diff --git a/internal/voucher/voucherpage/mock_Handler_test.go b/internal/voucher/voucherpage/mock_Handler_test.go index b167bc6ec6..11ec27962f 100644 --- a/internal/voucher/voucherpage/mock_Handler_test.go +++ b/internal/voucher/voucherpage/mock_Handler_test.go @@ -8,6 +8,8 @@ import ( appcontext "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" mock "github.com/stretchr/testify/mock" + + voucherdata "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) // mockHandler is an autogenerated mock type for the Handler type @@ -23,17 +25,17 @@ func (_m *mockHandler) EXPECT() *mockHandler_Expecter { return &mockHandler_Expecter{mock: &_m.Mock} } -// Execute provides a mock function with given fields: data, w, r -func (_m *mockHandler) Execute(data appcontext.Data, w http.ResponseWriter, r *http.Request) error { - ret := _m.Called(data, w, r) +// Execute provides a mock function with given fields: data, w, r, provided +func (_m *mockHandler) Execute(data appcontext.Data, w http.ResponseWriter, r *http.Request, provided *voucherdata.Provided) error { + ret := _m.Called(data, w, r, provided) if len(ret) == 0 { panic("no return value specified for Execute") } var r0 error - if rf, ok := ret.Get(0).(func(appcontext.Data, http.ResponseWriter, *http.Request) error); ok { - r0 = rf(data, w, r) + if rf, ok := ret.Get(0).(func(appcontext.Data, http.ResponseWriter, *http.Request, *voucherdata.Provided) error); ok { + r0 = rf(data, w, r, provided) } else { r0 = ret.Error(0) } @@ -50,13 +52,14 @@ type mockHandler_Execute_Call struct { // - data appcontext.Data // - w http.ResponseWriter // - r *http.Request -func (_e *mockHandler_Expecter) Execute(data interface{}, w interface{}, r interface{}) *mockHandler_Execute_Call { - return &mockHandler_Execute_Call{Call: _e.mock.On("Execute", data, w, r)} +// - provided *voucherdata.Provided +func (_e *mockHandler_Expecter) Execute(data interface{}, w interface{}, r interface{}, provided interface{}) *mockHandler_Execute_Call { + return &mockHandler_Execute_Call{Call: _e.mock.On("Execute", data, w, r, provided)} } -func (_c *mockHandler_Execute_Call) Run(run func(data appcontext.Data, w http.ResponseWriter, r *http.Request)) *mockHandler_Execute_Call { +func (_c *mockHandler_Execute_Call) Run(run func(data appcontext.Data, w http.ResponseWriter, r *http.Request, provided *voucherdata.Provided)) *mockHandler_Execute_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(appcontext.Data), args[1].(http.ResponseWriter), args[2].(*http.Request)) + run(args[0].(appcontext.Data), args[1].(http.ResponseWriter), args[2].(*http.Request), args[3].(*voucherdata.Provided)) }) return _c } @@ -66,7 +69,7 @@ func (_c *mockHandler_Execute_Call) Return(_a0 error) *mockHandler_Execute_Call return _c } -func (_c *mockHandler_Execute_Call) RunAndReturn(run func(appcontext.Data, http.ResponseWriter, *http.Request) error) *mockHandler_Execute_Call { +func (_c *mockHandler_Execute_Call) RunAndReturn(run func(appcontext.Data, http.ResponseWriter, *http.Request, *voucherdata.Provided) error) *mockHandler_Execute_Call { _c.Call.Return(run) return _c } diff --git a/internal/voucher/voucherpage/mock_Localizer_test.go b/internal/voucher/voucherpage/mock_Localizer_test.go new file mode 100644 index 0000000000..a84f87392e --- /dev/null +++ b/internal/voucher/voucherpage/mock_Localizer_test.go @@ -0,0 +1,534 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package voucherpage + +import ( + date "github.com/ministryofjustice/opg-modernising-lpa/internal/date" + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// mockLocalizer is an autogenerated mock type for the Localizer type +type mockLocalizer struct { + mock.Mock +} + +type mockLocalizer_Expecter struct { + mock *mock.Mock +} + +func (_m *mockLocalizer) EXPECT() *mockLocalizer_Expecter { + return &mockLocalizer_Expecter{mock: &_m.Mock} +} + +// Concat provides a mock function with given fields: list, joiner +func (_m *mockLocalizer) Concat(list []string, joiner string) string { + ret := _m.Called(list, joiner) + + if len(ret) == 0 { + panic("no return value specified for Concat") + } + + var r0 string + if rf, ok := ret.Get(0).(func([]string, string) string); ok { + r0 = rf(list, joiner) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_Concat_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Concat' +type mockLocalizer_Concat_Call struct { + *mock.Call +} + +// Concat is a helper method to define mock.On call +// - list []string +// - joiner string +func (_e *mockLocalizer_Expecter) Concat(list interface{}, joiner interface{}) *mockLocalizer_Concat_Call { + return &mockLocalizer_Concat_Call{Call: _e.mock.On("Concat", list, joiner)} +} + +func (_c *mockLocalizer_Concat_Call) Run(run func(list []string, joiner string)) *mockLocalizer_Concat_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]string), args[1].(string)) + }) + return _c +} + +func (_c *mockLocalizer_Concat_Call) Return(_a0 string) *mockLocalizer_Concat_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_Concat_Call) RunAndReturn(run func([]string, string) string) *mockLocalizer_Concat_Call { + _c.Call.Return(run) + return _c +} + +// Count provides a mock function with given fields: messageID, count +func (_m *mockLocalizer) Count(messageID string, count int) string { + ret := _m.Called(messageID, count) + + if len(ret) == 0 { + panic("no return value specified for Count") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string, int) string); ok { + r0 = rf(messageID, count) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_Count_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Count' +type mockLocalizer_Count_Call struct { + *mock.Call +} + +// Count is a helper method to define mock.On call +// - messageID string +// - count int +func (_e *mockLocalizer_Expecter) Count(messageID interface{}, count interface{}) *mockLocalizer_Count_Call { + return &mockLocalizer_Count_Call{Call: _e.mock.On("Count", messageID, count)} +} + +func (_c *mockLocalizer_Count_Call) Run(run func(messageID string, count int)) *mockLocalizer_Count_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(int)) + }) + return _c +} + +func (_c *mockLocalizer_Count_Call) Return(_a0 string) *mockLocalizer_Count_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_Count_Call) RunAndReturn(run func(string, int) string) *mockLocalizer_Count_Call { + _c.Call.Return(run) + return _c +} + +// Format provides a mock function with given fields: messageID, data +func (_m *mockLocalizer) Format(messageID string, data map[string]interface{}) string { + ret := _m.Called(messageID, data) + + if len(ret) == 0 { + panic("no return value specified for Format") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string, map[string]interface{}) string); ok { + r0 = rf(messageID, data) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_Format_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Format' +type mockLocalizer_Format_Call struct { + *mock.Call +} + +// Format is a helper method to define mock.On call +// - messageID string +// - data map[string]interface{} +func (_e *mockLocalizer_Expecter) Format(messageID interface{}, data interface{}) *mockLocalizer_Format_Call { + return &mockLocalizer_Format_Call{Call: _e.mock.On("Format", messageID, data)} +} + +func (_c *mockLocalizer_Format_Call) Run(run func(messageID string, data map[string]interface{})) *mockLocalizer_Format_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(map[string]interface{})) + }) + return _c +} + +func (_c *mockLocalizer_Format_Call) Return(_a0 string) *mockLocalizer_Format_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_Format_Call) RunAndReturn(run func(string, map[string]interface{}) string) *mockLocalizer_Format_Call { + _c.Call.Return(run) + return _c +} + +// FormatCount provides a mock function with given fields: messageID, count, data +func (_m *mockLocalizer) FormatCount(messageID string, count int, data map[string]interface{}) string { + ret := _m.Called(messageID, count, data) + + if len(ret) == 0 { + panic("no return value specified for FormatCount") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string, int, map[string]interface{}) string); ok { + r0 = rf(messageID, count, data) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_FormatCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatCount' +type mockLocalizer_FormatCount_Call struct { + *mock.Call +} + +// FormatCount is a helper method to define mock.On call +// - messageID string +// - count int +// - data map[string]interface{} +func (_e *mockLocalizer_Expecter) FormatCount(messageID interface{}, count interface{}, data interface{}) *mockLocalizer_FormatCount_Call { + return &mockLocalizer_FormatCount_Call{Call: _e.mock.On("FormatCount", messageID, count, data)} +} + +func (_c *mockLocalizer_FormatCount_Call) Run(run func(messageID string, count int, data map[string]interface{})) *mockLocalizer_FormatCount_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(int), args[2].(map[string]interface{})) + }) + return _c +} + +func (_c *mockLocalizer_FormatCount_Call) Return(_a0 string) *mockLocalizer_FormatCount_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_FormatCount_Call) RunAndReturn(run func(string, int, map[string]interface{}) string) *mockLocalizer_FormatCount_Call { + _c.Call.Return(run) + return _c +} + +// FormatDate provides a mock function with given fields: t +func (_m *mockLocalizer) FormatDate(t date.TimeOrDate) string { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for FormatDate") + } + + var r0 string + if rf, ok := ret.Get(0).(func(date.TimeOrDate) string); ok { + r0 = rf(t) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_FormatDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatDate' +type mockLocalizer_FormatDate_Call struct { + *mock.Call +} + +// FormatDate is a helper method to define mock.On call +// - t date.TimeOrDate +func (_e *mockLocalizer_Expecter) FormatDate(t interface{}) *mockLocalizer_FormatDate_Call { + return &mockLocalizer_FormatDate_Call{Call: _e.mock.On("FormatDate", t)} +} + +func (_c *mockLocalizer_FormatDate_Call) Run(run func(t date.TimeOrDate)) *mockLocalizer_FormatDate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(date.TimeOrDate)) + }) + return _c +} + +func (_c *mockLocalizer_FormatDate_Call) Return(_a0 string) *mockLocalizer_FormatDate_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_FormatDate_Call) RunAndReturn(run func(date.TimeOrDate) string) *mockLocalizer_FormatDate_Call { + _c.Call.Return(run) + return _c +} + +// FormatDateTime provides a mock function with given fields: t +func (_m *mockLocalizer) FormatDateTime(t time.Time) string { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for FormatDateTime") + } + + var r0 string + if rf, ok := ret.Get(0).(func(time.Time) string); ok { + r0 = rf(t) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_FormatDateTime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatDateTime' +type mockLocalizer_FormatDateTime_Call struct { + *mock.Call +} + +// FormatDateTime is a helper method to define mock.On call +// - t time.Time +func (_e *mockLocalizer_Expecter) FormatDateTime(t interface{}) *mockLocalizer_FormatDateTime_Call { + return &mockLocalizer_FormatDateTime_Call{Call: _e.mock.On("FormatDateTime", t)} +} + +func (_c *mockLocalizer_FormatDateTime_Call) Run(run func(t time.Time)) *mockLocalizer_FormatDateTime_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *mockLocalizer_FormatDateTime_Call) Return(_a0 string) *mockLocalizer_FormatDateTime_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_FormatDateTime_Call) RunAndReturn(run func(time.Time) string) *mockLocalizer_FormatDateTime_Call { + _c.Call.Return(run) + return _c +} + +// FormatTime provides a mock function with given fields: t +func (_m *mockLocalizer) FormatTime(t time.Time) string { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for FormatTime") + } + + var r0 string + if rf, ok := ret.Get(0).(func(time.Time) string); ok { + r0 = rf(t) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_FormatTime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatTime' +type mockLocalizer_FormatTime_Call struct { + *mock.Call +} + +// FormatTime is a helper method to define mock.On call +// - t time.Time +func (_e *mockLocalizer_Expecter) FormatTime(t interface{}) *mockLocalizer_FormatTime_Call { + return &mockLocalizer_FormatTime_Call{Call: _e.mock.On("FormatTime", t)} +} + +func (_c *mockLocalizer_FormatTime_Call) Run(run func(t time.Time)) *mockLocalizer_FormatTime_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *mockLocalizer_FormatTime_Call) Return(_a0 string) *mockLocalizer_FormatTime_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_FormatTime_Call) RunAndReturn(run func(time.Time) string) *mockLocalizer_FormatTime_Call { + _c.Call.Return(run) + return _c +} + +// Possessive provides a mock function with given fields: s +func (_m *mockLocalizer) Possessive(s string) string { + ret := _m.Called(s) + + if len(ret) == 0 { + panic("no return value specified for Possessive") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(s) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_Possessive_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Possessive' +type mockLocalizer_Possessive_Call struct { + *mock.Call +} + +// Possessive is a helper method to define mock.On call +// - s string +func (_e *mockLocalizer_Expecter) Possessive(s interface{}) *mockLocalizer_Possessive_Call { + return &mockLocalizer_Possessive_Call{Call: _e.mock.On("Possessive", s)} +} + +func (_c *mockLocalizer_Possessive_Call) Run(run func(s string)) *mockLocalizer_Possessive_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockLocalizer_Possessive_Call) Return(_a0 string) *mockLocalizer_Possessive_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_Possessive_Call) RunAndReturn(run func(string) string) *mockLocalizer_Possessive_Call { + _c.Call.Return(run) + return _c +} + +// SetShowTranslationKeys provides a mock function with given fields: s +func (_m *mockLocalizer) SetShowTranslationKeys(s bool) { + _m.Called(s) +} + +// mockLocalizer_SetShowTranslationKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetShowTranslationKeys' +type mockLocalizer_SetShowTranslationKeys_Call struct { + *mock.Call +} + +// SetShowTranslationKeys is a helper method to define mock.On call +// - s bool +func (_e *mockLocalizer_Expecter) SetShowTranslationKeys(s interface{}) *mockLocalizer_SetShowTranslationKeys_Call { + return &mockLocalizer_SetShowTranslationKeys_Call{Call: _e.mock.On("SetShowTranslationKeys", s)} +} + +func (_c *mockLocalizer_SetShowTranslationKeys_Call) Run(run func(s bool)) *mockLocalizer_SetShowTranslationKeys_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *mockLocalizer_SetShowTranslationKeys_Call) Return() *mockLocalizer_SetShowTranslationKeys_Call { + _c.Call.Return() + return _c +} + +func (_c *mockLocalizer_SetShowTranslationKeys_Call) RunAndReturn(run func(bool)) *mockLocalizer_SetShowTranslationKeys_Call { + _c.Call.Return(run) + return _c +} + +// ShowTranslationKeys provides a mock function with given fields: +func (_m *mockLocalizer) ShowTranslationKeys() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ShowTranslationKeys") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// mockLocalizer_ShowTranslationKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowTranslationKeys' +type mockLocalizer_ShowTranslationKeys_Call struct { + *mock.Call +} + +// ShowTranslationKeys is a helper method to define mock.On call +func (_e *mockLocalizer_Expecter) ShowTranslationKeys() *mockLocalizer_ShowTranslationKeys_Call { + return &mockLocalizer_ShowTranslationKeys_Call{Call: _e.mock.On("ShowTranslationKeys")} +} + +func (_c *mockLocalizer_ShowTranslationKeys_Call) Run(run func()) *mockLocalizer_ShowTranslationKeys_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockLocalizer_ShowTranslationKeys_Call) Return(_a0 bool) *mockLocalizer_ShowTranslationKeys_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_ShowTranslationKeys_Call) RunAndReturn(run func() bool) *mockLocalizer_ShowTranslationKeys_Call { + _c.Call.Return(run) + return _c +} + +// T provides a mock function with given fields: messageID +func (_m *mockLocalizer) T(messageID string) string { + ret := _m.Called(messageID) + + if len(ret) == 0 { + panic("no return value specified for T") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(messageID) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockLocalizer_T_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'T' +type mockLocalizer_T_Call struct { + *mock.Call +} + +// T is a helper method to define mock.On call +// - messageID string +func (_e *mockLocalizer_Expecter) T(messageID interface{}) *mockLocalizer_T_Call { + return &mockLocalizer_T_Call{Call: _e.mock.On("T", messageID)} +} + +func (_c *mockLocalizer_T_Call) Run(run func(messageID string)) *mockLocalizer_T_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockLocalizer_T_Call) Return(_a0 string) *mockLocalizer_T_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockLocalizer_T_Call) RunAndReturn(run func(string) string) *mockLocalizer_T_Call { + _c.Call.Return(run) + return _c +} + +// newMockLocalizer creates a new instance of mockLocalizer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockLocalizer(t interface { + mock.TestingT + Cleanup(func()) +}) *mockLocalizer { + mock := &mockLocalizer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/voucher/voucherpage/mock_LpaStoreResolvingService_test.go b/internal/voucher/voucherpage/mock_LpaStoreResolvingService_test.go new file mode 100644 index 0000000000..54cafe9fa9 --- /dev/null +++ b/internal/voucher/voucherpage/mock_LpaStoreResolvingService_test.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package voucherpage + +import ( + context "context" + + lpadata "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" + mock "github.com/stretchr/testify/mock" +) + +// mockLpaStoreResolvingService is an autogenerated mock type for the LpaStoreResolvingService type +type mockLpaStoreResolvingService struct { + mock.Mock +} + +type mockLpaStoreResolvingService_Expecter struct { + mock *mock.Mock +} + +func (_m *mockLpaStoreResolvingService) EXPECT() *mockLpaStoreResolvingService_Expecter { + return &mockLpaStoreResolvingService_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function with given fields: ctx +func (_m *mockLpaStoreResolvingService) Get(ctx context.Context) (*lpadata.Lpa, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *lpadata.Lpa + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*lpadata.Lpa, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *lpadata.Lpa); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*lpadata.Lpa) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockLpaStoreResolvingService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type mockLpaStoreResolvingService_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockLpaStoreResolvingService_Expecter) Get(ctx interface{}) *mockLpaStoreResolvingService_Get_Call { + return &mockLpaStoreResolvingService_Get_Call{Call: _e.mock.On("Get", ctx)} +} + +func (_c *mockLpaStoreResolvingService_Get_Call) Run(run func(ctx context.Context)) *mockLpaStoreResolvingService_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockLpaStoreResolvingService_Get_Call) Return(_a0 *lpadata.Lpa, _a1 error) *mockLpaStoreResolvingService_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockLpaStoreResolvingService_Get_Call) RunAndReturn(run func(context.Context) (*lpadata.Lpa, error)) *mockLpaStoreResolvingService_Get_Call { + _c.Call.Return(run) + return _c +} + +// newMockLpaStoreResolvingService creates a new instance of mockLpaStoreResolvingService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockLpaStoreResolvingService(t interface { + mock.TestingT + Cleanup(func()) +}) *mockLpaStoreResolvingService { + mock := &mockLpaStoreResolvingService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/voucher/voucherpage/mock_VoucherStore_test.go b/internal/voucher/voucherpage/mock_VoucherStore_test.go index 7fce2af7b1..03a9535032 100644 --- a/internal/voucher/voucherpage/mock_VoucherStore_test.go +++ b/internal/voucher/voucherpage/mock_VoucherStore_test.go @@ -6,6 +6,7 @@ import ( context "context" sharecodedata "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" + voucherdata "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" mock "github.com/stretchr/testify/mock" ) @@ -23,23 +24,23 @@ func (_m *mockVoucherStore) EXPECT() *mockVoucherStore_Expecter { } // Create provides a mock function with given fields: ctx, shareCode, email -func (_m *mockVoucherStore) Create(ctx context.Context, shareCode sharecodedata.Link, email string) (interface{}, error) { +func (_m *mockVoucherStore) Create(ctx context.Context, shareCode sharecodedata.Link, email string) (*voucherdata.Provided, error) { ret := _m.Called(ctx, shareCode, email) if len(ret) == 0 { panic("no return value specified for Create") } - var r0 interface{} + var r0 *voucherdata.Provided var r1 error - if rf, ok := ret.Get(0).(func(context.Context, sharecodedata.Link, string) (interface{}, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, sharecodedata.Link, string) (*voucherdata.Provided, error)); ok { return rf(ctx, shareCode, email) } - if rf, ok := ret.Get(0).(func(context.Context, sharecodedata.Link, string) interface{}); ok { + if rf, ok := ret.Get(0).(func(context.Context, sharecodedata.Link, string) *voucherdata.Provided); ok { r0 = rf(ctx, shareCode, email) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(interface{}) + r0 = ret.Get(0).(*voucherdata.Provided) } } @@ -72,12 +73,70 @@ func (_c *mockVoucherStore_Create_Call) Run(run func(ctx context.Context, shareC return _c } -func (_c *mockVoucherStore_Create_Call) Return(_a0 interface{}, _a1 error) *mockVoucherStore_Create_Call { +func (_c *mockVoucherStore_Create_Call) Return(_a0 *voucherdata.Provided, _a1 error) *mockVoucherStore_Create_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockVoucherStore_Create_Call) RunAndReturn(run func(context.Context, sharecodedata.Link, string) (interface{}, error)) *mockVoucherStore_Create_Call { +func (_c *mockVoucherStore_Create_Call) RunAndReturn(run func(context.Context, sharecodedata.Link, string) (*voucherdata.Provided, error)) *mockVoucherStore_Create_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function with given fields: ctx +func (_m *mockVoucherStore) Get(ctx context.Context) (*voucherdata.Provided, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *voucherdata.Provided + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*voucherdata.Provided, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *voucherdata.Provided); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*voucherdata.Provided) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockVoucherStore_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type mockVoucherStore_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockVoucherStore_Expecter) Get(ctx interface{}) *mockVoucherStore_Get_Call { + return &mockVoucherStore_Get_Call{Call: _e.mock.On("Get", ctx)} +} + +func (_c *mockVoucherStore_Get_Call) Run(run func(ctx context.Context)) *mockVoucherStore_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockVoucherStore_Get_Call) Return(_a0 *voucherdata.Provided, _a1 error) *mockVoucherStore_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockVoucherStore_Get_Call) RunAndReturn(run func(context.Context) (*voucherdata.Provided, error)) *mockVoucherStore_Get_Call { _c.Call.Return(run) return _c } diff --git a/internal/voucher/voucherpage/mock_test.go b/internal/voucher/voucherpage/mock_test.go index dd8007d5a1..c4cf2bafc6 100644 --- a/internal/voucher/voucherpage/mock_test.go +++ b/internal/voucher/voucherpage/mock_test.go @@ -8,5 +8,5 @@ import ( var ( expectedError = errors.New("err") - testAppData = appcontext.Data{} + testAppData = appcontext.Data{LpaID: "lpa-id"} ) diff --git a/internal/voucher/voucherpage/register.go b/internal/voucher/voucherpage/register.go index 4d1bf23e48..b45f38764c 100644 --- a/internal/voucher/voucherpage/register.go +++ b/internal/voucher/voucherpage/register.go @@ -9,20 +9,30 @@ import ( "github.com/ministryofjustice/opg-go-common/template" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" + "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" "github.com/ministryofjustice/opg-modernising-lpa/internal/onelogin" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/random" "github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" "github.com/ministryofjustice/opg-modernising-lpa/internal/sharecode/sharecodedata" "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) -type Handler func(data appcontext.Data, w http.ResponseWriter, r *http.Request) error +type Handler func(data appcontext.Data, w http.ResponseWriter, r *http.Request, provided *voucherdata.Provided) error type Template func(io.Writer, interface{}) error type ErrorHandler func(http.ResponseWriter, *http.Request, error) +type Localizer interface { + page.Localizer +} + +type LpaStoreResolvingService interface { + Get(ctx context.Context) (*lpadata.Lpa, error) +} + type Logger interface { InfoContext(ctx context.Context, msg string, args ...any) WarnContext(ctx context.Context, msg string, args ...any) @@ -51,7 +61,8 @@ type ShareCodeStore interface { } type VoucherStore interface { - Create(ctx context.Context, shareCode sharecodedata.Link, email string) (any, error) + Create(ctx context.Context, shareCode sharecodedata.Link, email string) (*voucherdata.Provided, error) + Get(ctx context.Context) (*voucherdata.Provided, error) } type DashboardStore interface { @@ -69,6 +80,7 @@ func Register( shareCodeStore ShareCodeStore, dashboardStore DashboardStore, errorHandler page.ErrorHandler, + lpaStoreResolvingService LpaStoreResolvingService, ) { handleRoot := makeHandle(rootMux, sessionStore, errorHandler) @@ -79,10 +91,10 @@ func Register( handleRoot(page.PathVoucherEnterReferenceNumber, RequireSession, EnterReferenceNumber(tmpls.Get("enter_reference_number.gohtml"), shareCodeStore, sessionStore, voucherStore)) - handleVoucher := makeVoucherHandle(rootMux, sessionStore, errorHandler) + handleVoucher := makeVoucherHandle(rootMux, sessionStore, errorHandler, voucherStore) handleVoucher(voucher.PathTaskList, None, - TaskList(tmpls.Get("task_list.gohtml"))) + TaskList(tmpls.Get("task_list.gohtml"), lpaStoreResolvingService)) } type handleOpt byte @@ -120,7 +132,7 @@ func makeHandle(mux *http.ServeMux, store SessionStore, errorHandler page.ErrorH } } -func makeVoucherHandle(mux *http.ServeMux, store SessionStore, errorHandler page.ErrorHandler) func(voucher.Path, handleOpt, Handler) { +func makeVoucherHandle(mux *http.ServeMux, store SessionStore, errorHandler page.ErrorHandler, voucherStore VoucherStore) func(voucher.Path, handleOpt, Handler) { return func(path voucher.Path, opt handleOpt, h Handler) { mux.HandleFunc(path.String(), func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -147,7 +159,18 @@ func makeVoucherHandle(mux *http.ServeMux, store SessionStore, errorHandler page ctx = appcontext.ContextWithSession(ctx, &appcontext.Session{SessionID: appData.SessionID, LpaID: appData.LpaID}) } - if err := h(appData, w, r.WithContext(appcontext.ContextWithData(ctx, appData))); err != nil { + provided, err := voucherStore.Get(ctx) + if err != nil { + errorHandler(w, r, err) + return + } + + if !voucher.CanGoTo(provided, r.URL.String()) { + voucher.PathTaskList.Redirect(w, r, appData, provided.LpaID) + return + } + + if err := h(appData, w, r.WithContext(appcontext.ContextWithData(ctx, appData)), provided); err != nil { errorHandler(w, r, err) } }) diff --git a/internal/voucher/voucherpage/register_test.go b/internal/voucher/voucherpage/register_test.go index 6bc38bb366..bf34bf79e3 100644 --- a/internal/voucher/voucherpage/register_test.go +++ b/internal/voucher/voucherpage/register_test.go @@ -10,12 +10,15 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestRegister(t *testing.T) { mux := http.NewServeMux() - Register(mux, &mockLogger{}, template.Templates{}, &mockSessionStore{}, &mockVoucherStore{}, &mockOneLoginClient{}, &mockShareCodeStore{}, &mockDashboardStore{}, nil) + Register(mux, &mockLogger{}, template.Templates{}, &mockSessionStore{}, &mockVoucherStore{}, &mockOneLoginClient{}, &mockShareCodeStore{}, &mockDashboardStore{}, nil, &mockLpaStoreResolvingService{}) assert.Implements(t, (*http.Handler)(nil), mux) } @@ -144,18 +147,25 @@ func TestMakeHandleNoSessionRequired(t *testing.T) { func TestMakeVoucherHandleExistingSession(t *testing.T) { ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "ignored-session-id"}) w := httptest.NewRecorder() - r, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/voucher/lpa-id/path?a=b", nil) + r, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/voucher/lpa-id/task-list?a=b", nil) sessionStore := newMockSessionStore(t) sessionStore.EXPECT(). Login(r). Return(&sesh.LoginSession{Sub: "random"}, nil) + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Get(mock.Anything). + Return(&voucherdata.Provided{LpaID: "lpa-id"}, nil) + mux := http.NewServeMux() - handle := makeVoucherHandle(mux, sessionStore, nil) - handle("/path", CanGoBack, func(appData appcontext.Data, hw http.ResponseWriter, hr *http.Request) error { + handle := makeVoucherHandle(mux, sessionStore, nil, voucherStore) + handle(voucher.PathTaskList, CanGoBack, func(appData appcontext.Data, hw http.ResponseWriter, hr *http.Request, provided *voucherdata.Provided) error { + assert.Equal(t, &voucherdata.Provided{LpaID: "lpa-id"}, provided) + assert.Equal(t, appcontext.Data{ - Page: "/voucher/lpa-id/path", + Page: "/voucher/lpa-id/task-list", CanGoBack: true, SessionID: "cmFuZG9t", LpaID: "lpa-id", @@ -175,6 +185,58 @@ func TestMakeVoucherHandleExistingSession(t *testing.T) { assert.Equal(t, http.StatusTeapot, resp.StatusCode) } +func TestMakeVoucherHandleWhenCannotGoToURL(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "ignored-session-id"}) + w := httptest.NewRecorder() + r, _ := http.NewRequestWithContext(ctx, http.MethodGet, voucher.PathSignTheDeclaration.Format("lpa-id"), nil) + + sessionStore := newMockSessionStore(t) + sessionStore.EXPECT(). + Login(r). + Return(&sesh.LoginSession{Sub: "random"}, nil) + + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Get(mock.Anything). + Return(&voucherdata.Provided{LpaID: "lpa-id"}, nil) + + mux := http.NewServeMux() + handle := makeVoucherHandle(mux, sessionStore, nil, voucherStore) + handle(voucher.PathSignTheDeclaration, CanGoBack, nil) + + mux.ServeHTTP(w, r) + resp := w.Result() + + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, voucher.PathTaskList.Format("lpa-id"), resp.Header.Get("Location")) +} + +func TestMakeVoucherHandleWhenVoucherStoreErrors(t *testing.T) { + ctx := appcontext.ContextWithSession(context.Background(), &appcontext.Session{SessionID: "ignored-session-id"}) + w := httptest.NewRecorder() + r, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/voucher/lpa-id/task-list?a=b", nil) + + sessionStore := newMockSessionStore(t) + sessionStore.EXPECT(). + Login(r). + Return(&sesh.LoginSession{Sub: "random"}, nil) + + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Get(mock.Anything). + Return(nil, expectedError) + + errorHandler := newMockErrorHandler(t) + errorHandler.EXPECT(). + Execute(w, r, expectedError) + + mux := http.NewServeMux() + handle := makeVoucherHandle(mux, sessionStore, errorHandler.Execute, voucherStore) + handle(voucher.PathTaskList, CanGoBack, nil) + + mux.ServeHTTP(w, r) +} + func TestMakeVoucherHandleErrors(t *testing.T) { w := httptest.NewRecorder() r, _ := http.NewRequest(http.MethodGet, "/voucher/id/path", nil) @@ -184,13 +246,18 @@ func TestMakeVoucherHandleErrors(t *testing.T) { Login(r). Return(&sesh.LoginSession{Sub: "random"}, nil) + voucherStore := newMockVoucherStore(t) + voucherStore.EXPECT(). + Get(mock.Anything). + Return(&voucherdata.Provided{LpaID: "lpa-id"}, nil) + errorHandler := newMockErrorHandler(t) errorHandler.EXPECT(). Execute(w, r, expectedError) mux := http.NewServeMux() - handle := makeVoucherHandle(mux, sessionStore, errorHandler.Execute) - handle("/path", None, func(_ appcontext.Data, _ http.ResponseWriter, _ *http.Request) error { + handle := makeVoucherHandle(mux, sessionStore, errorHandler.Execute, voucherStore) + handle("/path", None, func(_ appcontext.Data, _ http.ResponseWriter, _ *http.Request, _ *voucherdata.Provided) error { return expectedError }) @@ -207,8 +274,8 @@ func TestMakeVoucherHandleSessionError(t *testing.T) { Return(nil, expectedError) mux := http.NewServeMux() - handle := makeVoucherHandle(mux, sessionStore, nil) - handle("/path", RequireSession, func(_ appcontext.Data, _ http.ResponseWriter, _ *http.Request) error { + handle := makeVoucherHandle(mux, sessionStore, nil, nil) + handle("/path", RequireSession, func(_ appcontext.Data, _ http.ResponseWriter, _ *http.Request, _ *voucherdata.Provided) error { return nil }) diff --git a/internal/voucher/voucherpage/task_list.go b/internal/voucher/voucherpage/task_list.go index 0fdd4d84c6..8c69e39c7d 100644 --- a/internal/voucher/voucherpage/task_list.go +++ b/internal/voucher/voucherpage/task_list.go @@ -5,16 +5,61 @@ import ( "github.com/ministryofjustice/opg-go-common/template" "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" + "github.com/ministryofjustice/opg-modernising-lpa/internal/task" "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" ) type taskListData struct { - App appcontext.Data - Errors validation.List + App appcontext.Data + Errors validation.List + Voucher *voucherdata.Provided + Items []taskListItem } -func TaskList(tmpl template.Template) Handler { - return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request) error { - return tmpl(w, &taskListData{App: appData}) +type taskListItem struct { + Name string + Path string + State task.State +} + +func TaskList(tmpl template.Template, lpaStoreResolvingService LpaStoreResolvingService) Handler { + return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *voucherdata.Provided) error { + lpa, err := lpaStoreResolvingService.Get(r.Context()) + if err != nil { + return err + } + + items := []taskListItem{ + { + Name: "confirmYourName", + Path: voucher.PathConfirmYourName.Format(appData.LpaID), + State: provided.Tasks.ConfirmYourName, + }, + { + Name: appData.Localizer.Format("verifyDonorDetails", map[string]any{ + "DonorFullNamePossessive": appData.Localizer.Possessive(lpa.Donor.FullName()), + }), + Path: voucher.PathVerifyDonorDetails.Format(appData.LpaID), + State: provided.Tasks.VerifyDonorDetails, + }, + { + Name: "confirmYourIdentity", + Path: voucher.PathConfirmYourIdentity.Format(appData.LpaID), + State: provided.Tasks.ConfirmYourIdentity, + }, + { + Name: "signTheDeclaration", + Path: voucher.PathSignTheDeclaration.Format(appData.LpaID), + State: provided.Tasks.SignTheDeclaration, + }, + } + + return tmpl(w, &taskListData{ + App: appData, + Voucher: provided, + Items: items, + }) } } diff --git a/internal/voucher/voucherpage/task_list_test.go b/internal/voucher/voucherpage/task_list_test.go new file mode 100644 index 0000000000..85e094d854 --- /dev/null +++ b/internal/voucher/voucherpage/task_list_test.go @@ -0,0 +1,143 @@ +package voucherpage + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/task" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher" + "github.com/ministryofjustice/opg-modernising-lpa/internal/voucher/voucherdata" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetTaskList(t *testing.T) { + testCases := map[string]struct { + lpa *lpadata.Lpa + voucher *voucherdata.Provided + expected func([]taskListItem) []taskListItem + }{ + "empty": { + lpa: &lpadata.Lpa{ + LpaID: "lpa-id", + Donor: lpadata.Donor{FirstNames: "John", LastName: "Smith"}, + }, + voucher: &voucherdata.Provided{}, + expected: func(items []taskListItem) []taskListItem { + return items + }, + }, + "completed": { + lpa: &lpadata.Lpa{ + LpaID: "lpa-id", + Donor: lpadata.Donor{FirstNames: "John", LastName: "Smith"}, + }, + voucher: &voucherdata.Provided{ + Tasks: voucherdata.Tasks{ + ConfirmYourName: task.StateCompleted, + VerifyDonorDetails: task.StateCompleted, + ConfirmYourIdentity: task.StateCompleted, + SignTheDeclaration: task.StateCompleted, + }, + }, + expected: func(items []taskListItem) []taskListItem { + items[0].State = task.StateCompleted + items[1].State = task.StateCompleted + items[2].State = task.StateCompleted + items[3].State = task.StateCompleted + return items + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) + + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(tc.lpa, nil) + + localizer := newMockLocalizer(t) + localizer.EXPECT(). + Possessive("John Smith"). + Return("John Smith's") + localizer.EXPECT(). + Format("verifyDonorDetails", map[string]any{"DonorFullNamePossessive": "John Smith's"}). + Return("verifyJohnSmithsDetails") + + appData := testAppData + appData.Localizer = localizer + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, &taskListData{ + App: appData, + Voucher: tc.voucher, + Items: tc.expected([]taskListItem{ + {Name: "confirmYourName", Path: voucher.PathConfirmYourName.Format("lpa-id")}, + {Name: "verifyJohnSmithsDetails", Path: voucher.PathVerifyDonorDetails.Format("lpa-id")}, + {Name: "confirmYourIdentity", Path: voucher.PathConfirmYourIdentity.Format("lpa-id")}, + {Name: "signTheDeclaration", Path: voucher.PathSignTheDeclaration.Format("lpa-id")}, + }), + }). + Return(nil) + + err := TaskList(template.Execute, lpaStoreResolvingService)(appData, w, r, tc.voucher) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + } +} + +func TestGetTaskListWhenLpaStoreResolvingServiceErrors(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) + + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(&lpadata.Lpa{}, expectedError) + + err := TaskList(nil, lpaStoreResolvingService)(testAppData, w, r, nil) + + assert.Equal(t, expectedError, err) +} + +func TestGetTaskListWhenTemplateErrors(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) + + lpaStoreResolvingService := newMockLpaStoreResolvingService(t) + lpaStoreResolvingService.EXPECT(). + Get(r.Context()). + Return(&lpadata.Lpa{LpaID: "lpa-id"}, nil) + + localizer := newMockLocalizer(t) + localizer.EXPECT(). + Possessive(mock.Anything). + Return("oi") + localizer.EXPECT(). + Format(mock.Anything, mock.Anything). + Return("hey") + + appData := testAppData + appData.Localizer = localizer + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, mock.Anything). + Return(expectedError) + + err := TaskList(template.Execute, lpaStoreResolvingService)(appData, w, r, &voucherdata.Provided{}) + resp := w.Result() + + assert.Equal(t, expectedError, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/web/template/voucher/task_list.gohtml b/web/template/voucher/task_list.gohtml index 1fa8f053aa..6900d0d454 100644 --- a/web/template/voucher/task_list.gohtml +++ b/web/template/voucher/task_list.gohtml @@ -1,13 +1,40 @@ {{ template "page" . }} -{{ define "pageTitle" }}{{ tr .App "taskList" }}{{ end }} +{{ define "pageTitle" }}{{ tr .App "yourTaskList" }}{{ end }} {{ define "main" }} -
-
-

{{ tr .App "taskList" }}

+
+
+ {{ tr .App "vouchForSomeonesIdentity" }} +

{{ tr .App "yourTaskList" }}

-

TODO

+
    + {{ range .Items }} + {{ $hasLink := voucherCanGoTo $.Voucher .Path }} + + + {{ end }} +
+
-
{{ end }}