From 6a769b6dab13fc405af999fbfa68dd19bdf14c14 Mon Sep 17 00:00:00 2001
From: Joshua Hawxwell <m@hawx.me>
Date: Tue, 26 Mar 2024 13:36:24 +0000
Subject: [PATCH] Handle certificate-provider-submission-completed event

---
 .../cloud_watch_event_handler.go              |  38 ++++
 .../cloud_watch_event_handler_test.go         | 168 ++++++++++++++++++
 cmd/event-received/factory.go                 |   1 +
 .../mock_shareCodeSender_test.go              |  48 +++++
 4 files changed, 255 insertions(+)

diff --git a/cmd/event-received/cloud_watch_event_handler.go b/cmd/event-received/cloud_watch_event_handler.go
index ae98f70700..5e7665882e 100644
--- a/cmd/event-received/cloud_watch_event_handler.go
+++ b/cmd/event-received/cloud_watch_event_handler.go
@@ -86,6 +86,9 @@ func (h *cloudWatchEventHandler) Handle(ctx context.Context, cloudWatchEvent eve
 
 		return handleDonorSubmissionCompleted(ctx, h.dynamoClient, cloudWatchEvent, shareCodeSender, appData, lpaStoreClient)
 
+	case "certificate-provider-submission-completed":
+		return handleCertificateProviderSubmissionCompleted(ctx, cloudWatchEvent, h.factory)
+
 	default:
 		return fmt.Errorf("unknown cloudwatch event")
 	}
@@ -217,8 +220,43 @@ func handleDonorSubmissionCompleted(ctx context.Context, client dynamodbClient,
 
 	if donor.CertificateProvider.CarryOutBy.IsOnline() {
 		if err := shareCodeSender.SendCertificateProviderInvite(ctx, appData, donor); err != nil {
+			return fmt.Errorf("failed to send share code to certificate provider: %w", err)
+		}
+	}
+
+	return nil
+}
+
+func handleCertificateProviderSubmissionCompleted(ctx context.Context, event events.CloudWatchEvent, factory factory) error {
+	var v uidEvent
+	if err := json.Unmarshal(event.Detail, &v); err != nil {
+		return fmt.Errorf("failed to unmarshal detail: %w", err)
+	}
+
+	lpaStoreClient, err := factory.LpaStoreClient()
+	if err != nil {
+		return err
+	}
+
+	donor, err := lpaStoreClient.Lpa(ctx, v.UID)
+	if err != nil {
+		return fmt.Errorf("failed to retrieve lpa: %w", err)
+	}
+
+	if donor.CertificateProvider.CarryOutBy.IsPaper() {
+		shareCodeSender, err := factory.ShareCodeSender(ctx)
+		if err != nil {
 			return err
 		}
+
+		appData, err := factory.AppData()
+		if err != nil {
+			return err
+		}
+
+		if err := shareCodeSender.SendAttorneys(ctx, appData, donor); err != nil {
+			return fmt.Errorf("failed to send share codes to attorneys: %w", err)
+		}
 	}
 
 	return nil
diff --git a/cmd/event-received/cloud_watch_event_handler_test.go b/cmd/event-received/cloud_watch_event_handler_test.go
index 628d3a04df..626bb5c882 100644
--- a/cmd/event-received/cloud_watch_event_handler_test.go
+++ b/cmd/event-received/cloud_watch_event_handler_test.go
@@ -607,5 +607,173 @@ func TestHandleDonorSubmissionCompletedWhenShareCodeSenderError(t *testing.T) {
 		Return(expectedError)
 
 	err := handleDonorSubmissionCompleted(ctx, client, event, shareCodeSender, appData, lpaStoreClient)
+	assert.Equal(t, fmt.Errorf("failed to send share code to certificate provider: %w", expectedError), err)
+}
+
+var certificateProviderSubmissionCompletedEvent = events.CloudWatchEvent{
+	DetailType: "certificate-provider-submission-completed",
+	Detail:     json.RawMessage(`{"uid":"M-1111-2222-3333"}`),
+}
+
+func TestHandleCertificateProviderSubmissionCompleted(t *testing.T) {
+	appData := page.AppData{}
+
+	donor := &actor.DonorProvidedDetails{
+		CertificateProvider: actor.CertificateProvider{
+			CarryOutBy: actor.Paper,
+		},
+	}
+
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(donor, nil)
+
+	shareCodeSender := newMockShareCodeSender(t)
+	shareCodeSender.EXPECT().
+		SendAttorneys(ctx, appData, donor).
+		Return(nil)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+	factory.EXPECT().
+		ShareCodeSender(ctx).
+		Return(shareCodeSender, nil)
+	factory.EXPECT().
+		AppData().
+		Return(appData, nil)
+
+	err := handleCertificateProviderSubmissionCompleted(ctx, certificateProviderSubmissionCompletedEvent, factory)
+	assert.Nil(t, err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenOnline(t *testing.T) {
+	donor := &actor.DonorProvidedDetails{
+		CertificateProvider: actor.CertificateProvider{
+			CarryOutBy: actor.Online,
+		},
+	}
+
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(donor, nil)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
+	assert.Nil(t, err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenLpaStoreFactoryErrors(t *testing.T) {
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(nil, expectedError)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
+	assert.Equal(t, expectedError, err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenLpaStoreErrors(t *testing.T) {
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(nil, expectedError)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
+	assert.Equal(t, fmt.Errorf("failed to retrieve lpa: %w", expectedError), err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenShareCodeSenderErrors(t *testing.T) {
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(&actor.DonorProvidedDetails{
+			CertificateProvider: actor.CertificateProvider{
+				CarryOutBy: actor.Paper,
+			},
+		}, nil)
+
+	shareCodeSender := newMockShareCodeSender(t)
+	shareCodeSender.EXPECT().
+		SendAttorneys(ctx, mock.Anything, mock.Anything).
+		Return(expectedError)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+	factory.EXPECT().
+		ShareCodeSender(ctx).
+		Return(shareCodeSender, nil)
+	factory.EXPECT().
+		AppData().
+		Return(page.AppData{}, nil)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
+	assert.Equal(t, fmt.Errorf("failed to send share codes to attorneys: %w", expectedError), err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenShareCodeSenderFactoryErrors(t *testing.T) {
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(&actor.DonorProvidedDetails{
+			CertificateProvider: actor.CertificateProvider{
+				CarryOutBy: actor.Paper,
+			},
+		}, nil)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+	factory.EXPECT().
+		ShareCodeSender(ctx).
+		Return(nil, expectedError)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
+	assert.Equal(t, expectedError, err)
+}
+
+func TestHandleCertificateProviderSubmissionCompletedWhenAppDataFactoryErrors(t *testing.T) {
+	lpaStoreClient := newMockLpaStoreClient(t)
+	lpaStoreClient.EXPECT().
+		Lpa(ctx, "M-1111-2222-3333").
+		Return(&actor.DonorProvidedDetails{
+			CertificateProvider: actor.CertificateProvider{
+				CarryOutBy: actor.Paper,
+			},
+		}, nil)
+
+	factory := newMockFactory(t)
+	factory.EXPECT().
+		LpaStoreClient().
+		Return(lpaStoreClient, nil)
+	factory.EXPECT().
+		ShareCodeSender(ctx).
+		Return(nil, nil)
+	factory.EXPECT().
+		AppData().
+		Return(page.AppData{}, expectedError)
+
+	handler := &cloudWatchEventHandler{factory: factory}
+	err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent)
 	assert.Equal(t, expectedError, err)
 }
diff --git a/cmd/event-received/factory.go b/cmd/event-received/factory.go
index 6a0c1968aa..d078e3707c 100644
--- a/cmd/event-received/factory.go
+++ b/cmd/event-received/factory.go
@@ -38,6 +38,7 @@ type SecretsClient interface {
 type ShareCodeSender interface {
 	SendCertificateProviderInvite(context.Context, page.AppData, *actor.DonorProvidedDetails) error
 	SendCertificateProviderPrompt(context.Context, page.AppData, *actor.DonorProvidedDetails) error
+	SendAttorneys(context.Context, page.AppData, *actor.DonorProvidedDetails) error
 }
 
 type UidStore interface {
diff --git a/cmd/event-received/mock_shareCodeSender_test.go b/cmd/event-received/mock_shareCodeSender_test.go
index 741f9c3366..412ebe1089 100644
--- a/cmd/event-received/mock_shareCodeSender_test.go
+++ b/cmd/event-received/mock_shareCodeSender_test.go
@@ -25,6 +25,54 @@ func (_m *mockShareCodeSender) EXPECT() *mockShareCodeSender_Expecter {
 	return &mockShareCodeSender_Expecter{mock: &_m.Mock}
 }
 
+// SendAttorneys provides a mock function with given fields: _a0, _a1, _a2
+func (_m *mockShareCodeSender) SendAttorneys(_a0 context.Context, _a1 page.AppData, _a2 *actor.DonorProvidedDetails) error {
+	ret := _m.Called(_a0, _a1, _a2)
+
+	if len(ret) == 0 {
+		panic("no return value specified for SendAttorneys")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, page.AppData, *actor.DonorProvidedDetails) error); ok {
+		r0 = rf(_a0, _a1, _a2)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockShareCodeSender_SendAttorneys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendAttorneys'
+type mockShareCodeSender_SendAttorneys_Call struct {
+	*mock.Call
+}
+
+// SendAttorneys is a helper method to define mock.On call
+//   - _a0 context.Context
+//   - _a1 page.AppData
+//   - _a2 *actor.DonorProvidedDetails
+func (_e *mockShareCodeSender_Expecter) SendAttorneys(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockShareCodeSender_SendAttorneys_Call {
+	return &mockShareCodeSender_SendAttorneys_Call{Call: _e.mock.On("SendAttorneys", _a0, _a1, _a2)}
+}
+
+func (_c *mockShareCodeSender_SendAttorneys_Call) Run(run func(_a0 context.Context, _a1 page.AppData, _a2 *actor.DonorProvidedDetails)) *mockShareCodeSender_SendAttorneys_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(page.AppData), args[2].(*actor.DonorProvidedDetails))
+	})
+	return _c
+}
+
+func (_c *mockShareCodeSender_SendAttorneys_Call) Return(_a0 error) *mockShareCodeSender_SendAttorneys_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockShareCodeSender_SendAttorneys_Call) RunAndReturn(run func(context.Context, page.AppData, *actor.DonorProvidedDetails) error) *mockShareCodeSender_SendAttorneys_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // SendCertificateProviderInvite provides a mock function with given fields: _a0, _a1, _a2
 func (_m *mockShareCodeSender) SendCertificateProviderInvite(_a0 context.Context, _a1 page.AppData, _a2 *actor.DonorProvidedDetails) error {
 	ret := _m.Called(_a0, _a1, _a2)