package infrastructure

import (
	"bytes"
	"context"
	"database/sql"
	"testing"
	"time"

	"code.justin.tv/eventbus/controlplane/infrastructure/mocks"
	infraMocks "code.justin.tv/eventbus/controlplane/infrastructure/mocks"
	"code.justin.tv/eventbus/controlplane/infrastructure/routing"
	"code.justin.tv/eventbus/controlplane/infrastructure/rpc"
	"code.justin.tv/eventbus/controlplane/internal/db"
	dbMocks "code.justin.tv/eventbus/controlplane/internal/db/mocks"
	"code.justin.tv/eventbus/controlplane/internal/db/postgres"
	"code.justin.tv/eventbus/controlplane/internal/environment"
	"code.justin.tv/eventbus/controlplane/internal/ldap"
	twirperrMocks "code.justin.tv/eventbus/controlplane/internal/twirperr/mocks"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"github.com/twitchtv/twirp"
)

func newDBMock() *dbMocks.DB {
	dbMock := &dbMocks.DB{}

	// avoid race/memory overlap conditions from reusing the same addresses of event streams
	createdStreams := func() []*db.EventStream {
		return []*db.EventStream{
			{
				ID:          1,
				Environment: environment.Development,
				EventTypeID: 1,
			},
			{
				ID:          2,
				Environment: environment.Staging,
				EventTypeID: 1,
			},
			{
				ID:          3,
				Environment: environment.Production,
				EventTypeID: 1,
			},
		}
	}
	dbMock.On("EventTypeAndStreamsCreate", mock.Anything, mock.Anything, mock.Anything).Return(createdStreams(), nil)
	dbMock.On("EventTypeUpdate", mock.Anything, mock.Anything, mock.Anything).Return(1, nil)
	dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, environment.Development).Return(createdStreams()[0], nil)
	dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, environment.Staging).Return(createdStreams()[1], nil)
	dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, environment.Production).Return(createdStreams()[2], nil)
	dbMock.
		On("EventStreamAcquireLease", mock.Anything, mock.Anything, mock.Anything).
		Return(&postgres.PostgresAWSLease{}, context.Background(), nil)
	dbMock.
		On("EventStreamReleaseLease", mock.Anything).
		Return(nil)

	dbMock.
		On("EventStreamUpdateInfra", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
		Return(
			func(ctx context.Context, lease db.AWSLease, id int, eventStreamInfraUpdate *db.EventStreamInfraUpdate) int {
				return id
			},
			nil,
		)

	dbMock.On("EventStreamsByEventTypeID", mock.Anything, mock.Anything).Return(createdStreams(), nil)

	return dbMock
}

func s3Mock() *infraMocks.S3Iface {
	m := &infraMocks.S3Iface{}
	notFound := awserr.New("NotFound", "Thing", errors.New("Not Found"))
	m.On("GetObjectWithContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, notFound)
	m.On("PutObjectWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&s3.PutObjectOutput{}, nil)
	return m
}

type AllServiceMocks struct {
	DB                          *dbMocks.DB
	S3                          *infraMocks.S3Iface
	AuthorizedFieldGrantManager *infraMocks.AuthorizedFieldGrantManager
	SNSManager                  *infraMocks.SNSManager
}

func serviceWithDBAndActionMocks(allMocks *AllServiceMocks) (*InfrastructureService, *AllServiceMocks) {
	if allMocks == nil {
		allMocks = &AllServiceMocks{}
	}

	allMocks.DB = newDBMock()
	allMocks.AuthorizedFieldGrantManager = &infraMocks.AuthorizedFieldGrantManager{}
	if allMocks.S3 == nil {
		allMocks.S3 = s3Mock()
	}
	allMocks.SNSManager = &infraMocks.SNSManager{}

	s := &InfrastructureService{
		DB:         allMocks.DB,
		SNSManager: allMocks.SNSManager,
		RouteConfigActions: &routing.ConfigActions{
			S3: allMocks.S3,
		},
	}
	return s, allMocks
}

func serviceWithDBMock() (*InfrastructureService, *dbMocks.DB) {
	s, mocks := serviceWithDBAndActionMocks(nil)
	return s, mocks.DB
}

func TestInfrastructureService(t *testing.T) {

	noAuthCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()

	ctx := ldap.WithGroups(noAuthCtx, []string{})
	ctx = ldap.WithUser(ctx, updaterLambdaLDAPUser)

	t.Run("RegisterEventTypes", func(t *testing.T) {
		t.Run("Authz Required", func(t *testing.T) {
			service, _ := serviceWithDBMock()
			resp, err := service.RegisterEventDefinitions(noAuthCtx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:   "UserCreate",
						DisplayName: "UserCreate",
					},
				},
			})
			require.Nil(t, resp)
			require.Error(t, err)
			assert.Contains(t, err.Error(), "not_found")
		})

		t.Run("Simple Create", func(t *testing.T) {
			service, m := serviceWithDBAndActionMocks(nil)
			m.DB.On("EventTypeByName", mock.Anything, "UserCreate").Return(nil, twirperrMocks.NewDBErrorFor("EventTypeNotFound"))
			m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
			resp, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:   "UserCreate",
						DisplayName: "UserCreate",
					},
				},
			})
			require.NoError(t, err)
			m.DB.AssertNumberOfCalls(t, "EventTypeAndStreamsCreate", 1)
			assert.Len(t, resp.Results, 1) // one result per event type
			assert.True(t, resp.Results[0].Created)
		})

		t.Run("S3 Get Failure", func(t *testing.T) {
			s3Mock := &infraMocks.S3Iface{}
			s3Mock.On("GetObjectWithContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("S3 get random failure"))
			service, m := serviceWithDBAndActionMocks(&AllServiceMocks{S3: s3Mock})
			m.DB.On("EventTypeByName", mock.Anything, "UserCreate").Return(nil, db.ErrResourceNotFound)
			m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
			resp, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:   "UserCreate",
						DisplayName: "UserCreate",
					},
				},
			})
			require.Error(t, err)
			require.Nil(t, resp)
		})

		t.Run("S3 Put Failure", func(t *testing.T) {
			s3Mock := &infraMocks.S3Iface{}
			s3Mock.On("GetObjectWithContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&s3.GetObjectOutput{
				Body: emptyReadCloser{&bytes.Buffer{}},
			}, nil)
			s3Mock.On("PutObjectWithContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("put failure"))
			service, m := serviceWithDBAndActionMocks(&AllServiceMocks{S3: s3Mock})
			m.DB.On("EventTypeByName", mock.Anything, "UserCreate").Return(nil, db.ErrResourceNotFound)
			m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
			resp, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:   "UserCreate",
						DisplayName: "UserCreate",
					},
				},
			})
			require.Error(t, err)
			require.Nil(t, resp)
		})

		t.Run("Update record", func(t *testing.T) {
			service, m := serviceWithDBAndActionMocks(nil)
			eventType := &db.EventType{
				Name:        "UserDelete",
				Description: "Old Description",
			}
			m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
			m.DB.On("EventTypeByName", mock.Anything, "UserDelete").Return(eventType, nil)
			resp, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:   "UserDelete",
						Description: "New Description!",
					},
				},
			})
			require.NoError(t, err)
			assert.Len(t, resp.Results, 1)
			m.DB.AssertNumberOfCalls(t, "EventTypeAndStreamCreate", 0)
			result := resp.Results[0]
			assert.False(t, result.Created)
			assert.Equal(t, []string{"updated description"}, result.Messages)
		})

		t.Run("Deprecate", func(t *testing.T) {
			service, m := serviceWithDBAndActionMocks(nil)
			eventType := &db.EventType{
				Name: "DeprecatedEvent",
			}
			m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
			m.DB.On("EventTypeByName", mock.Anything, "DeprecatedEvent").Return(eventType, nil)
			resp, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
				EventDefinitions: []*rpc.EventDefinition{
					{
						EventType:  "DeprecatedEvent",
						Deprecated: true,
					},
				},
			})
			require.NoError(t, err)
			assert.Len(t, resp.Results, 1)
			m.DB.AssertNumberOfCalls(t, "EventTypeAndStreamCreate", 0)
			result := resp.Results[0]
			assert.False(t, result.Created)
			assert.Equal(t, []string{"updated deprecated"}, result.Messages)
		})

		t.Run("With AuthorizedFields", func(t *testing.T) {
			authorizedFields := []*rpc.AuthorizedField{
				{
					MessageName: "Foobar",
					FieldName:   "Garply",
				},
				{
					MessageName: "Baz",
					FieldName:   "Womp",
				},
				{
					MessageName: "Classified",
					FieldName:   "TopSecretData",
				},
			}
			t.Run("On create", func(t *testing.T) {
				service, m := serviceWithDBAndActionMocks(nil)
				m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
				dbMock := m.DB
				dbMock.On("EventTypeByName", mock.Anything, "UserCreate").Return(nil, twirperrMocks.NewDBErrorFor("EventTypeNotFound"))
				dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldNotFound"))
				dbMock.On("AuthorizedFieldCreate", mock.Anything, mock.Anything).Return(1, nil)
				_, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
					EventDefinitions: []*rpc.EventDefinition{
						{
							EventType:        "UserCreate",
							AuthorizedFields: authorizedFields,
						},
					},
				})
				assert.NoError(t, err)
				dbMock.AssertNumberOfCalls(t, "AuthorizedFieldByAuthContext", 9)
				dbMock.AssertNumberOfCalls(t, "AuthorizedFieldCreate", 9)
			})
			t.Run("On update", func(t *testing.T) {
				t.Run("All new authorized fields", func(t *testing.T) {
					service, m := serviceWithDBAndActionMocks(nil)
					m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
					dbMock := m.DB
					dbMock.Calls = nil
					eventType := &db.EventType{
						Name: "UserCreate",
					}
					dbMock.On("EventTypeByName", mock.Anything, eventType.Name).Return(eventType, nil)
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldNotFound"))
					dbMock.On("AuthorizedFieldCreate", mock.Anything, mock.Anything).Return(1, nil)
					_, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
						EventDefinitions: []*rpc.EventDefinition{
							{
								EventType:        eventType.Name,
								AuthorizedFields: authorizedFields,
							},
						},
					})
					assert.NoError(t, err)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldByAuthContext", 9)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldCreate", 9)
				})

				t.Run("Partial update new authorized field", func(t *testing.T) {
					service, m := serviceWithDBAndActionMocks(nil)
					m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
					dbMock := m.DB
					existingAuthField := &db.AuthorizedField{}
					eventType := &db.EventType{
						Name: "UserCreate",
					}
					dbMock.On("EventTypeByName", mock.Anything, eventType.Name).Return(eventType, nil)
					// Mock methods to indicate that 2 authorized fields already exist and the third does not (yet)
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, authorizedFields[0].MessageName, authorizedFields[0].FieldName).Return(existingAuthField, nil)
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, authorizedFields[1].MessageName, authorizedFields[1].FieldName).Return(existingAuthField, nil)
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, authorizedFields[2].MessageName, authorizedFields[2].FieldName).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldNotFound"))
					dbMock.On("AuthorizedFieldCreate", mock.Anything, mock.Anything).Return(1, nil)
					_, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
						EventDefinitions: []*rpc.EventDefinition{
							{
								EventType:        eventType.Name,
								AuthorizedFields: authorizedFields,
							},
						},
					})
					// Expect same number of lookups, but only 1/3 the calls to create
					assert.NoError(t, err)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldByAuthContext", 9)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldCreate", 3)
				})

				// This is a bit unrealistic since we don't have any environment churn at all (production, staging, development),
				// but it serves to show that the feature is working as intended across all authorized field lookup/iteration dimensions
				t.Run("Partial update new environments", func(t *testing.T) {
					service, m := serviceWithDBAndActionMocks(nil)
					m.SNSManager.On("CreateTopic", mock.Anything, mock.Anything, mock.Anything).Return("sns:abc", nil)
					dbMock := m.DB
					existingAuthField := &db.AuthorizedField{}
					eventType := &db.EventType{
						Name: "UserCreate",
					}
					dbMock.On("EventTypeByName", mock.Anything, eventType.Name).Return(eventType, nil)
					// Mock methods to indicate that authorized fields only exist for 1 of the 3 standard environments
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, 1, mock.Anything, mock.Anything).Return(existingAuthField, nil)
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, 2, mock.Anything, mock.Anything).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldNotFound"))
					dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, 3, mock.Anything, mock.Anything).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldNotFound"))
					dbMock.On("AuthorizedFieldCreate", mock.Anything, mock.Anything).Return(1, nil)
					_, err := service.RegisterEventDefinitions(ctx, &rpc.RegisterEventDefinitionsReq{
						EventDefinitions: []*rpc.EventDefinition{
							{
								EventType:        eventType.Name,
								AuthorizedFields: authorizedFields,
							},
						},
					})
					// Two of the three environments need authorized fields created
					assert.NoError(t, err)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldByAuthContext", 9)
					dbMock.AssertNumberOfCalls(t, "AuthorizedFieldCreate", 6)
				})
			})
		})
	})

	t.Run("List Publications Iam Role", func(t *testing.T) {
		ctx = ldap.WithGroups(noAuthCtx, []string{"team-eventbus"})
		m := &dbMocks.DB{}
		service := &InfrastructureService{
			DB: m,
		}
		m.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(&db.EventStream{
			ID:          1,
			Environment: "production",
			EventTypeID: 1,
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
		}, nil)
		m.On("EventTypeByName", mock.Anything, mock.Anything).Return(&db.EventType{
			ID:         1,
			Deprecated: false,
			Name:       "TestEvent",
		}, nil)
		m.On("PublicationsByEventStreamID", mock.Anything, mock.Anything).Return([]*db.Publication{
			{
				ID:            111,
				EventStreamID: 1,
				IAMRoleID: sql.NullInt64{
					Valid: true,
					Int64: 123,
				},
			},
		}, nil)
		m.On("IAMRoleByID", mock.Anything, mock.Anything).Return(&db.IAMRole{
			ID:        123,
			ARN:       "arn:aws:iam::123456789012:role/test",
			ServiceID: 7,
		}, nil)
		m.On("ServiceByID", mock.Anything, mock.Anything).Return(&db.Service{
			ID:               7,
			ServiceCatalogID: "17",
			Name:             "TestService",
		}, nil)

		resp, err := service.ListPublicationsByEventStream(ctx, &rpc.ListPublicationsByEventStreamReq{
			EventType:   "TestEvent",
			Environment: "production",
		})

		assert.NoError(t, err)
		assert.Equal(t, 1, len(resp.Publications))
		assert.Equal(t, false, resp.Publications[0].EventDeprecated)
		assert.Equal(t, "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic", resp.Publications[0].SnsTopicArn)
		assert.Equal(t, "", resp.Publications[0].EventDescription)
		assert.Equal(t, "TestEvent", resp.Publications[0].EventType)
		assert.Equal(t, "production", resp.Publications[0].Environment)
		assert.Equal(t, "arn:aws:iam::123456789012:role/test", resp.Publications[0].PublisherIamRole)
		assert.Equal(t, "TestService", resp.Publications[0].PublisherServiceName)
		assert.Equal(t, "17", resp.Publications[0].PublisherServiceCatalogId)
	})

	t.Run("List Publications Account", func(t *testing.T) {
		ctx = ldap.WithGroups(noAuthCtx, []string{"team-eventbus"})
		m := &dbMocks.DB{}
		service := &InfrastructureService{
			DB: m,
		}
		m.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(&db.EventStream{
			ID:          1,
			Environment: "production",
			EventTypeID: 1,
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
		}, nil)
		m.On("EventTypeByName", mock.Anything, mock.Anything).Return(&db.EventType{
			ID:         1,
			Deprecated: false,
			Name:       "TestEvent",
		}, nil)
		m.On("PublicationsByEventStreamID", mock.Anything, mock.Anything).Return([]*db.Publication{
			{
				ID:            111,
				EventStreamID: 1,
				IAMRoleID: sql.NullInt64{
					Valid: false,
					Int64: 0,
				},
				AccountID: sql.NullInt64{
					Valid: true,
					Int64: 1,
				},
			},
		}, nil)
		m.On("AccountByID", mock.Anything, mock.Anything).Return(&db.Account{
			ID:                   1,
			AWSAccountID:         "123456789012",
			CloudformationStatus: "OK",
			Label:                "test-acct",
			ServiceID:            7,
		}, nil)
		m.On("ServiceByID", mock.Anything, mock.Anything).Return(&db.Service{
			ID:               7,
			ServiceCatalogID: "17",
			Name:             "TestService",
		}, nil)

		resp, err := service.ListPublicationsByEventStream(ctx, &rpc.ListPublicationsByEventStreamReq{
			EventType:   "TestEvent",
			Environment: "production",
		})

		assert.NoError(t, err)
		assert.Equal(t, 1, len(resp.Publications))
		assert.Equal(t, false, resp.Publications[0].EventDeprecated)
		assert.Equal(t, "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic", resp.Publications[0].SnsTopicArn)
		assert.Equal(t, "", resp.Publications[0].EventDescription)
		assert.Equal(t, "TestEvent", resp.Publications[0].EventType)
		assert.Equal(t, "production", resp.Publications[0].Environment)
		assert.Equal(t, "missing", resp.Publications[0].PublisherIamRole)
		assert.Equal(t, "TestService", resp.Publications[0].PublisherServiceName)
		assert.Equal(t, "17", resp.Publications[0].PublisherServiceCatalogId)
	})

	t.Run("Delete Publication for IAM role", func(t *testing.T) {
		m := &dbMocks.DB{}
		k := &mocks.AuthorizedFieldGrantManager{}
		service := &InfrastructureService{
			DB:                          m,
			AuthorizedFieldGrantManager: k,
		}

		m.On("ServiceByID", mock.Anything, 1).Return(&db.Service{
			ID:   1,
			Name: "HeHeHaHa",
		}, nil)
		m.On("IAMRoleByARN", mock.Anything, "arn:aws:iam::123456789012:role/hehe").Return(&db.IAMRole{
			ID:        1,
			ServiceID: 1,
			ARN:       "arn:aws:iam::123456789012:role/hehe",
		}, nil)
		m.On("EventStreamByNameAndEnvironment", mock.Anything, "TestEvent", "production").Return(&db.EventStream{
			ID:          1,
			EventTypeID: 1,
			Environment: "production",
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
			EventType: db.EventType{
				ID:         1,
				Name:       "TestEvent",
				Deprecated: true,
			},
		}, nil)
		m.On("PublicationsByEventStreamID", mock.Anything, mock.Anything).Return([]*db.Publication{
			{
				EventStreamID: 1,
				IAMRoleID:     sql.NullInt64{Valid: true, Int64: 2},
			},
			{
				EventStreamID: 7,
				IAMRoleID:     sql.NullInt64{Valid: true, Int64: 1},
			},
		}, nil)
		m.On("PublicationDelete", mock.Anything, mock.Anything).Return(nil)
		m.On("AuthorizedFieldPublisherGrant", mock.Anything, 1, 1).Return(&db.AuthorizedFieldPublisherGrant{
			ID:            1,
			IAMRoleID:     1,
			EventStreamID: 1,
			KMSGrantID:    "deadbeef",
		}, nil)
		m.On("AuthorizedFieldPublisherGrantDeleteByID", mock.Anything, 1).Return(nil)
		k.On("FindByGrantID", mock.Anything, "deadbeef").Return(&kms.GrantListEntry{
			GrantId: aws.String("deadbeef"),
		}, nil)
		k.On("Revoke", mock.Anything, "deadbeef").Return(nil)
		resp, err := service.DeleteIAMRolePublication(ctx, &rpc.DeleteIAMRolePublicationReq{EventType: "TestEvent", Environment: "production", IamRoleArn: "arn:aws:iam::123456789012:role/hehe"})
		require.NoError(t, err)
		require.NotNil(t, resp)
	})

	t.Run("Delete Publication Non Deprecated Event", func(t *testing.T) {
		ctx := ldap.WithGroups(context.Background(), []string{"team-not-admin"})
		m := &dbMocks.DB{}
		service := &InfrastructureService{
			DB: m,
		}

		m.On("ServiceByID", mock.Anything, 1).Return(&db.Service{
			ID:   1,
			Name: "HeHeHaHa",
			IAMRoles: []*db.IAMRole{
				{
					ID:  1,
					ARN: "arn:aws:iam::123456789012:role/hehe",
				},
				{
					ID:  2,
					ARN: "arn:aws:iam::123456789012:role/haha",
				},
			},
		}, nil)
		m.On("EventStreamByNameAndEnvironment", mock.Anything, "TestEvent", "production").Return(&db.EventStream{
			ID:          1,
			EventTypeID: 1,
			Environment: "production",
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
			EventType: db.EventType{
				ID:         1,
				Name:       "TestEvent",
				Deprecated: false,
			},
		}, nil)
		resp, err := service.DeleteIAMRolePublication(ctx, &rpc.DeleteIAMRolePublicationReq{EventType: "TestEvent", Environment: "production", IamRoleArn: "doesnt-matter"})
		require.Error(t, err)
		assert.Nil(t, resp)
		assert.Equal(t, twirp.FailedPrecondition, err.(twirp.Error).Code())
	})

	t.Run("Delete Publication Missing Publisher Grant in DB", func(t *testing.T) {
		m := &dbMocks.DB{}
		service := &InfrastructureService{
			DB: m,
		}

		m.On("ServiceByID", mock.Anything, mock.Anything).Return(&db.Service{
			ID:   1,
			Name: "HeHeHaHa",
		}, nil)
		m.On("IAMRoleByARN", mock.Anything, "arn:aws:iam::123456789012:role/hehe").Return(&db.IAMRole{

			ID:  1,
			ARN: "arn:aws:iam::123456789012:role/hehe",
		}, nil)
		m.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(&db.EventStream{
			ID:          1,
			EventTypeID: 1,
			Environment: "production",
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
			EventType: db.EventType{
				ID:         1,
				Name:       "TestEvent",
				Deprecated: true,
			},
		}, nil)
		m.On("PublicationsByEventStreamID", mock.Anything, mock.Anything).Return([]*db.Publication{
			{
				ID:            1,
				EventStreamID: 1,
				IAMRoleID:     sql.NullInt64{Valid: true, Int64: 1},
			},
		}, nil)
		m.On("AuthorizedFieldPublisherGrant", mock.Anything, 1, 1).Return(nil, twirperrMocks.NewDBErrorFor("AuthorizedFieldPublisherGrantNotFound"))
		m.On("PublicationDelete", mock.Anything, 1).Return(nil)
		resp, err := service.DeleteIAMRolePublication(ctx, &rpc.DeleteIAMRolePublicationReq{EventType: "TestEvent", Environment: "production", IamRoleArn: "arn:aws:iam::123456789012:role/hehe"})
		require.NoError(t, err)
		require.NotNil(t, resp)
	})

	t.Run("Delete Publication Missing Publisher Grant in KMS", func(t *testing.T) {
		m := &dbMocks.DB{}
		k := &mocks.AuthorizedFieldGrantManager{}
		service := &InfrastructureService{
			DB:                          m,
			AuthorizedFieldGrantManager: k,
		}

		m.On("ServiceByID", mock.Anything, mock.Anything).Return(&db.Service{
			ID:   1,
			Name: "HeHeHaHa",
		}, nil)
		m.On("IAMRoleByARN", mock.Anything, "arn:aws:iam::123456789012:role/hehe").Return(&db.IAMRole{

			ID:  1,
			ARN: "arn:aws:iam::123456789012:role/hehe",
		}, nil)
		m.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(&db.EventStream{
			ID:          1,
			EventTypeID: 1,
			Environment: "production",
			SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent-Topic"},
			EventType: db.EventType{
				ID:         1,
				Name:       "TestEvent",
				Deprecated: true,
			},
		}, nil)
		m.On("PublicationsByEventStreamID", mock.Anything, mock.Anything).Return([]*db.Publication{
			{
				ID:            1,
				EventStreamID: 1,
				IAMRoleID:     sql.NullInt64{Valid: true, Int64: 1},
			},
		}, nil)
		m.On("AuthorizedFieldPublisherGrant", mock.Anything, 1, 1).Return(&db.AuthorizedFieldPublisherGrant{
			ID:            1,
			IAMRoleID:     1,
			EventStreamID: 1,
			KMSGrantID:    "doesntexist",
		}, nil)
		m.On("AuthorizedFieldPublisherGrantDeleteByID", mock.Anything, 1).Return(nil)
		k.On("FindByGrantID", mock.Anything, "doesntexist").Return(nil, nil)
		m.On("PublicationDelete", mock.Anything, 1).Return(nil)
		resp, err := service.DeleteIAMRolePublication(ctx, &rpc.DeleteIAMRolePublicationReq{EventType: "TestEvent", Environment: "production", IamRoleArn: "arn:aws:iam::123456789012:role/hehe"})
		require.NoError(t, err)
		require.NotNil(t, resp)
	})
}

func TestDeleteAuthorizedFieldSubscriberGrant(t *testing.T) {
	ctx := ldap.WithGroups(context.Background(), []string{"team-eventbus"})
	iamRoleARNSub := "arn:aws:iam::123456789012:role/test-sub"

	iamRoleSub := &db.IAMRole{
		ID:  2,
		ARN: iamRoleARNSub,
	}

	eventStream := &db.EventStream{
		ID:          1,
		Environment: "production",
		SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent"},
		EventType: db.EventType{
			ID:   1,
			Name: "TestEvent",
		},
	}

	authedField := &db.AuthorizedField{
		ID:            1,
		EventStreamID: 1,
		FieldName:     "Field",
		MessageName:   "Message",
	}

	subGrant := &db.AuthorizedFieldSubscriberGrant{
		ID:                1,
		IAMRoleID:         2,
		KMSGrantID:        "kms-sub-grant-id",
		AuthorizedFieldID: 1,
	}

	t.Run("Delete Subscriber Grant", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("IAMRoleByARN", mock.Anything, mock.Anything).Return(iamRoleSub, nil)
		dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(eventStream, nil)
		dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(authedField, nil)
		dbMock.On("AuthorizedFieldSubscriberGrant", mock.Anything, mock.Anything, mock.Anything).Return(subGrant, nil)
		dbMock.On("AuthorizedFieldSubscriberGrantDeleteByID", mock.Anything, mock.Anything).Return(nil)
		k := &mocks.AuthorizedFieldGrantManager{}
		k.On("FindByGrantID", mock.Anything, "kms-sub-grant-id").Return(&kms.GrantListEntry{
			GrantId: aws.String("kms-sub-grant-id"),
		}, nil)
		k.On("Revoke", mock.Anything, "kms-sub-grant-id").Return(nil)
		service := &InfrastructureService{
			DB:                          dbMock,
			AuthorizedFieldGrantManager: k,
		}
		resp, err := service.DeleteAuthorizedFieldSubscriberGrant(ctx, &rpc.DeleteAuthorizedFieldSubscriberGrantReq{
			Grant: &rpc.AuthorizedFieldSubscriberGrant{
				IamRole:     iamRoleARNSub,
				EventType:   "TestEvent",
				Environment: "production",
				AuthorizedField: &rpc.AuthorizedField{
					FieldName:   "Field",
					MessageName: "Message",
				},
			},
		})
		require.NoError(t, err)
		assert.True(t, resp.Deleted)
	})

	t.Run("Delete Subscriber Grant Without KMS Grant Infra", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("IAMRoleByARN", mock.Anything, mock.Anything).Return(iamRoleSub, nil)
		dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(eventStream, nil)
		dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(authedField, nil)
		dbMock.On("AuthorizedFieldSubscriberGrant", mock.Anything, mock.Anything, mock.Anything).Return(subGrant, nil)
		dbMock.On("AuthorizedFieldSubscriberGrantDeleteByID", mock.Anything, mock.Anything).Return(nil)
		k := &mocks.AuthorizedFieldGrantManager{}
		k.On("FindByGrantID", mock.Anything, "kms-sub-grant-id").Return(nil, nil)
		authFieldManagerMock := &infraMocks.AuthorizedFieldGrantManager{}
		authFieldManagerMock.On("FindByGrantID", mock.Anything, mock.Anything).Return(nil, nil)

		service := &InfrastructureService{
			DB:                          dbMock,
			AuthorizedFieldGrantManager: authFieldManagerMock,
		}
		resp, err := service.DeleteAuthorizedFieldSubscriberGrant(ctx, &rpc.DeleteAuthorizedFieldSubscriberGrantReq{
			Grant: &rpc.AuthorizedFieldSubscriberGrant{
				IamRole: iamRoleARNSub,
				AuthorizedField: &rpc.AuthorizedField{
					MessageName: "Message",
					FieldName:   "Field",
				},
				EventType:   "TestEvent",
				Environment: "production",
			},
		})
		require.NoError(t, err)
		assert.True(t, resp.Deleted)
	})
}

func TestDeleteAuthorizedField(t *testing.T) {
	ctx := ldap.WithGroups(context.Background(), []string{"team-eventbus"})

	eventStream := &db.EventStream{
		ID:          1,
		Environment: "production",
		SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent"},
		EventType: db.EventType{
			ID:         1,
			Name:       "TestEvent",
			Deprecated: true,
		},
	}

	eventStreamBad := &db.EventStream{
		ID:          2,
		Environment: "production",
		EventType: db.EventType{
			ID:         2,
			Name:       "TestEvent2",
			Deprecated: false,
		},
	}

	authedField := &db.AuthorizedField{
		ID:            1,
		EventStreamID: 1,
		FieldName:     "Field",
		MessageName:   "Message",
	}

	t.Run("Delete Authed Field Deprecated Event", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(eventStream, nil)
		dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(authedField, nil)
		dbMock.On("AuthorizedFieldDeleteByID", mock.Anything, mock.Anything).Return(nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteAuthorizedField(ctx, &rpc.DeleteAuthorizedFieldReq{
			EventType:   "TestEvent",
			Environment: "production",
			AuthorizedField: &rpc.AuthorizedField{
				FieldName:   authedField.FieldName,
				MessageName: authedField.MessageName,
			},
		})
		assert.NoError(t, err)
		assert.True(t, resp.Deleted)
	})

	t.Run("Delete Authed Field Non Deprecated Event", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(eventStreamBad, nil)
		dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(authedField, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteAuthorizedField(ctx, &rpc.DeleteAuthorizedFieldReq{
			EventType:   "TestEvent",
			Environment: "production",
			AuthorizedField: &rpc.AuthorizedField{
				FieldName:   authedField.FieldName,
				MessageName: authedField.MessageName,
			},
		})
		assert.Error(t, err)
		assert.Nil(t, resp)
	})
}

func TestDeleteEventType(t *testing.T) {
	ctx := ldap.WithGroups(context.Background(), []string{"team-eventbus"})
	eventTypeGoodDelete := &db.EventType{
		ID:         1,
		Name:       "TestEventDeprecated",
		Deprecated: true,
	}
	eventTypeBadDelete := &db.EventType{
		ID:   2,
		Name: "TestEventNotDeprecated",
	}

	t.Run("Test EventType Delete Good", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventTypeByName", mock.Anything, mock.Anything).Return(eventTypeGoodDelete, nil)
		dbMock.On("EventTypeDeleteByName", mock.Anything, mock.Anything).Return(nil)
		dbMock.On("EventStreamsByEventTypeID", mock.Anything, mock.Anything).Return([]*db.EventStream{}, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteEventType(ctx, &rpc.DeleteEventTypeReq{
			EventType: "TestEventDeprecated",
		})
		assert.NoError(t, err)
		assert.True(t, resp.Deleted)
	})

	t.Run("Test EventType Delete Not Deprecated Event", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventTypeByName", mock.Anything, mock.Anything).Return(eventTypeBadDelete, nil)
		dbMock.On("EventStreamsByEventTypeID", mock.Anything, mock.Anything).Return([]*db.EventStream{}, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteEventType(ctx, &rpc.DeleteEventTypeReq{
			EventType: "TestEventNotDeprecated",
		})
		assert.Error(t, err)
		assert.Nil(t, resp)
	})

	t.Run("Test EventType Delete EventStreams Exist", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventTypeByName", mock.Anything, mock.Anything).Return(eventTypeBadDelete, nil)
		dbMock.On("EventStreamsByEventTypeID", mock.Anything, mock.Anything).Return([]*db.EventStream{
			{
				Environment: "development",
				ID:          3,
			},
		}, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteEventType(ctx, &rpc.DeleteEventTypeReq{
			EventType: "TestEventNotDeprecated",
		})
		assert.Error(t, err)
		assert.Nil(t, resp)
	})
}

func TestListSubGrants(t *testing.T) {
	ctx := ldap.WithGroups(context.Background(), []string{"team-eventbus"})
	iamRoleARNSub := "arn:aws:iam::123456789012:role/test-sub"
	iamRoleARNSub2 := "arn:aws:iam::123456789012:role/test-sub-2"

	authedField := &db.AuthorizedField{
		ID:            1,
		EventStreamID: 1,
		FieldName:     "Field",
		MessageName:   "Message",
	}

	eventStream := &db.EventStream{
		ID:          1,
		Environment: "production",
		SNSDetails:  db.SNSDetails{SNSTopicARN: "arn:aws:sns:us-west-2:123456789012:TestEvent"},
		EventType: db.EventType{
			ID:   1,
			Name: "TestEvent",
		},
	}

	subGrants := []*db.AuthorizedFieldSubscriberGrant{
		{
			ID:                1,
			IAMRoleID:         1,
			KMSGrantID:        "kms-sub-grant-id-1",
			AuthorizedFieldID: 1,
		},
		{
			ID:                2,
			IAMRoleID:         2,
			KMSGrantID:        "kms-sub-grant-id-2",
			AuthorizedFieldID: 1,
		},
	}

	iamRole1 := &db.IAMRole{
		ID:  1,
		ARN: iamRoleARNSub,
	}

	iamRole2 := &db.IAMRole{
		ID:  2,
		ARN: iamRoleARNSub2,
	}

	t.Run("Test List Sub Grants Multiple", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventStreamByNameAndEnvironment", mock.Anything, mock.Anything, mock.Anything).Return(eventStream, nil)
		dbMock.On("AuthorizedFieldByAuthContext", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(authedField, nil)
		dbMock.On("AuthorizedFieldSubscriberGrantsByAuthorizedField", mock.Anything, mock.Anything).Return(subGrants, nil)
		dbMock.On("IAMRoleByID", mock.Anything, 1).Return(iamRole1, nil)
		dbMock.On("IAMRoleByID", mock.Anything, 2).Return(iamRole2, nil)

		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.ListAuthorizedFieldSubscriberGrantsByAuthorizedField(ctx, &rpc.ListAuthorizedFieldSubscriberGrantsByAuthorizedFieldReq{
			AuthorizedField: &rpc.AuthorizedField{
				FieldName:   authedField.FieldName,
				MessageName: authedField.MessageName,
			},
			Environment: eventStream.Environment,
			EventType:   eventStream.EventType.Name,
		})

		assert.NoError(t, err)
		assert.Equal(t, 2, len(resp.SubscriberGrants))
		assert.Equal(t, "production", resp.SubscriberGrants[0].Environment)
		assert.Equal(t, "production", resp.SubscriberGrants[1].Environment)
		assert.Equal(t, "Message", resp.SubscriberGrants[0].AuthorizedField.MessageName)
		assert.Equal(t, "Field", resp.SubscriberGrants[1].AuthorizedField.FieldName)
		assert.Equal(t, "kms-sub-grant-id-1", resp.SubscriberGrants[0].KmsGrantId)
		assert.Equal(t, "kms-sub-grant-id-2", resp.SubscriberGrants[1].KmsGrantId)
	})
}

func TestEventStreamDelete(t *testing.T) {
	ctx := ldap.WithGroups(context.Background(), []string{"team-eventbus"})
	eventTypeGoodDelete := &db.EventType{
		ID:         1,
		Name:       "TestEventDeprecated",
		Deprecated: true,
	}
	eventTypeBadDelete := &db.EventType{
		ID:   2,
		Name: "TestEventNotDeprecated",
	}

	t.Run("Test EventStreamDelete Good", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventTypeByName", mock.Anything, mock.Anything).Return(eventTypeGoodDelete, nil)
		dbMock.On("EventStreamDeleteByEventTypeID", mock.Anything, mock.Anything).Return(3, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteEventStreamsForEventType(ctx, &rpc.DeleteEventStreamsForEventTypeReq{
			EventType: "TestEventDeprecated",
		})
		assert.NoError(t, err)
		assert.True(t, resp.Deleted)
		assert.Equal(t, int32(3), resp.NumDeleted)
	})

	t.Run("Test EventStreamDelete Bad", func(t *testing.T) {
		dbMock := &dbMocks.DB{}
		dbMock.On("EventTypeByName", mock.Anything, mock.Anything).Return(eventTypeBadDelete, nil)
		service := &InfrastructureService{
			DB: dbMock,
		}
		resp, err := service.DeleteEventStreamsForEventType(ctx, &rpc.DeleteEventStreamsForEventTypeReq{
			EventType: "TestEventNotDeprecated",
		})
		assert.Error(t, err)
		assert.Nil(t, resp)
	})
}

// func TestPublisherAuthFlow(t *testing.T) {
// 	serviceID := 1
// 	eventType := db.EventType{
// 		ID:           2,
// 		Name:         "test-type",
// 		Description:  "testing",
// 		Schema:       "protobuf schema here",
// 		RepoFilePath: "file/to/repo",
// 	}

// 	eventStream := db.EventStream{
// 		ID:          5,
// 		Environment: "Production",
// 		SNSDetails: db.SNSDetails{
// 			SNSTopicARN: "arn:aws:sns:us-west-2:111111111111:TestTopic",
// 		},
// 		EventTypeID: eventType.ID,
// 		EventType:   eventType,
// 	}

// 	publications := []*db.Publication{
// 		{
// 			ID: 111,
// 			AccountID: sql.NullInt64{
// 				Int64: 123,
// 				Valid: true,
// 			},
// 			IAMRoleID: sql.NullInt64{
// 				Int64: 321,
// 				Valid: true,
// 			},
// 			EventStreamID: eventStream.ID,
// 		},
// 	}

// 	t.Run("Publication Does Not Exist", func(t *testing.T) {
// 		dbMock := &dbMocks.DB{}
// 		accounts := []*db.Account{
// 			{
// 				ID:                   124,
// 				AWSAccountID:         "222222222222",
// 				Label:                "test-prod",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}

// 		dbMock.On("AccountsByServiceID", mock.Anything, serviceID).Return(accounts, nil)
// 		dbMock.On("PublicationsByServiceID", mock.Anything, serviceID).Return(publications, nil)
// 		publication := &db.Publication{
// 			AccountID: sql.NullInt64{
// 				Int64: 124,
// 				Valid: true,
// 			},
// 			IAMRoleID:     sql.NullInt64{Valid: false},
// 			EventStreamID: eventStream.ID,
// 		}
// 		dbMock.On("PublicationCreate", mock.Anything, publication).Return(112, nil)
// 		service := &InfrastructureService{
// 			DB: dbMock,
// 		}
// 		err := service.addPublication(context.Background(), strconv.Itoa(serviceID), &eventStream, []string{"222222222222"})
// 		require.NoError(t, err)
// 		dbMock.AssertNumberOfCalls(t, "PublicationCreate", 1)
// 	})

// 	t.Run("Publication Does Exist", func(t *testing.T) {
// 		dbMock := &dbMocks.DB{}
// 		accounts := []*db.Account{
// 			{
// 				ID:                   123,
// 				AWSAccountID:         "111111111111",
// 				Label:                "test-dev",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}
// 		iamRoles := []*db.IAMRole{
// 			{
// 				ID:                   321,
// 				ARN:                  "arn:aws:iam::111111111111:role/lots-of-oness",
// 				Label:                "test-dev",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}
// 		dbMock.On("IAMRolesByServiceID", mock.Anything, serviceID).Return(iamRoles, nil)
// 		dbMock.On("AccountsByServiceID", mock.Anything, serviceID).Return(accounts, nil)
// 		dbMock.On("PublicationsByServiceID", mock.Anything, serviceID).Return(publications, nil)
// 		service := &InfrastructureService{
// 			DB: dbMock,
// 		}
// 		err := service.addPublication(context.Background(), strconv.Itoa(serviceID), &eventStream, []string{"111111111111"})
// 		require.NoError(t, err)
// 		dbMock.AssertNumberOfCalls(t, "PublicationCreate", 0)
// 	})

// 	t.Run("Publish Mixed Publication (One Exists One Does Not)", func(t *testing.T) {
// 		dbMock := &dbMocks.DB{}
// 		accounts := []*db.Account{
// 			{
// 				ID:                   123,
// 				AWSAccountID:         "111111111111",
// 				Label:                "test-dev",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 			{
// 				ID:                   124,
// 				AWSAccountID:         "222222222222",
// 				Label:                "test-prod",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}
// 		dbMock.On("AccountsByServiceID", mock.Anything, serviceID).Return(accounts, nil)
// 		dbMock.On("PublicationsByServiceID", mock.Anything, serviceID).Return(publications, nil)
// 		publication := &db.Publication{
// 			AccountID: sql.NullInt64{
// 				Int64: int64(124),
// 				Valid: true,
// 			},
// 			EventStreamID: eventStream.ID,
// 		}
// 		dbMock.On("PublicationCreate", mock.Anything, publication).Return(112, nil)
// 		service := &InfrastructureService{
// 			DB: dbMock,
// 		}
// 		err := service.addPublication(context.Background(), strconv.Itoa(serviceID), &eventStream, []string{"111111111111", "222222222222"})
// 		require.NoError(t, err)
// 		dbMock.AssertNumberOfCalls(t, "PublicationCreate", 1)
// 	})

// 	t.Run("Publish Multiple Publications", func(t *testing.T) {
// 		dbMock := &dbMocks.DB{}
// 		accounts := []*db.Account{
// 			{
// 				ID:                   123,
// 				AWSAccountID:         "111111111111",
// 				Label:                "test-dev",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 			{
// 				ID:                   124,
// 				AWSAccountID:         "222222222222",
// 				Label:                "test-prod",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}
// 		dbMock.On("AccountsByServiceID", mock.Anything, serviceID).Return(accounts, nil)
// 		dbMock.On("PublicationsByServiceID", mock.Anything, serviceID).Return([]*db.Publication{}, nil)
// 		publication1 := &db.Publication{
// 			AccountID: sql.NullInt64{
// 				Int64: 123,
// 				Valid: true,
// 			},
// 			EventStreamID: eventStream.ID,
// 		}
// 		dbMock.On("PublicationCreate", mock.Anything, publication1).Return(110, nil)
// 		publication2 := &db.Publication{
// 			AccountID: sql.NullInt64{
// 				Int64: 124,
// 				Valid: true,
// 			},
// 			EventStreamID: eventStream.ID,
// 		}
// 		dbMock.On("PublicationCreate", mock.Anything, publication2).Return(111, nil)
// 		service := &InfrastructureService{
// 			DB: dbMock,
// 		}
// 		err := service.addPublication(context.Background(), strconv.Itoa(serviceID), &eventStream, []string{"111111111111", "222222222222"})
// 		require.NoError(t, err)
// 		dbMock.AssertNumberOfCalls(t, "PublicationCreate", 2)
// 	})

// 	t.Run("Publish when service only has accounts", func(t *testing.T) {
// 		dbMock := &dbMocks.DB{}
// 		accounts := []*db.Account{
// 			{
// 				ID:                   123,
// 				AWSAccountID:         "111111111111",
// 				Label:                "test-dev",
// 				ServiceID:            serviceID,
// 				CloudformationStatus: "OK",
// 			},
// 		}
// 		publication := &db.Publication{
// 			AccountID: sql.NullInt64{
// 				Int64: 123,
// 				Valid: true,
// 			},
// 			EventStreamID: eventStream.ID,
// 		}
// 		dbMock.On("AccountsByServiceID", mock.Anything, serviceID).Return(accounts, nil)
// 		dbMock.On("PublicationsByServiceID", mock.Anything, serviceID).Return(nil, nil)
// 		dbMock.On("PublicationCreate", mock.Anything, publication).Return(111, nil)
// 		service := &InfrastructureService{
// 			DB: dbMock,
// 		}
// 		err := service.addPublication(context.Background(), strconv.Itoa(serviceID), &eventStream, []string{"111111111111"})
// 		require.NoError(t, err)
// 		dbMock.AssertNumberOfCalls(t, "PublicationCreate", 1)
// 	})
// }

// TODO tests for createEventSteam (when SNS topic arn is empty vs exists vs doesnt exist)
// TODO tests for new AllowIAMRolePublish control flow

type emptyReadCloser struct {
	*bytes.Buffer
}

func (emptyReadCloser) Close() error {
	return nil
}
