github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/applicationtenancy/directive_test.go (about)

     1  package applicationtenancy_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/pkg/applicationtenancy"
     8  	"github.com/kyma-incubator/compass/components/director/pkg/applicationtenancy/automock"
     9  	schema "github.com/kyma-incubator/compass/components/director/pkg/graphql"
    10  	persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock"
    11  	tenantpkg "github.com/kyma-incubator/compass/components/director/pkg/tenant"
    12  	"github.com/pkg/errors"
    13  	"github.com/stretchr/testify/mock"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest"
    16  
    17  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    18  	"github.com/kyma-incubator/compass/components/director/internal/model"
    19  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  const (
    26  	tenantID       = "381060d3-39ed-4617-a14a-f6fcf52a1e7e"
    27  	newTenantID    = "12345678-1111-4617-0000-f6fcf52a1e7e"
    28  	applicationID  = "637060ad-f30e-4326-a8f0-6dfae63d8dc9"
    29  	parentTenantID = "f2cad1bc-caae-4d5b-8699-649c155a9939"
    30  )
    31  
    32  var (
    33  	testErr                         = errors.New("test-err")
    34  	subaccountTenantModel           = fixBusinessTenantMappingModel(tenantID, tenantpkg.Subaccount, false)
    35  	rgTenantModel                   = fixBusinessTenantMappingModel(tenantID, tenantpkg.ResourceGroup, false)
    36  	accountTenantModel              = fixBusinessTenantMappingModel(tenantID, tenantpkg.Account, true)
    37  	newAccountTenantModel           = fixBusinessTenantMappingModel(newTenantID, tenantpkg.Account, true)
    38  	accountTenantWithoutParentModel = fixBusinessTenantMappingModel(tenantID, tenantpkg.Account, false)
    39  	customerTenantModel             = fixBusinessTenantMappingModel(parentTenantID, tenantpkg.Customer, false)
    40  	tenantAccessWithOwnerTrue       = fixTenantAccessModel(tenantID, applicationID, resource.Application, true)
    41  	tenantAccessWithOwnerFalse      = fixTenantAccessModel(newTenantID, applicationID, resource.Application, false)
    42  	orgTenantsModel                 = []*model.BusinessTenantMapping{fixBusinessTenantMappingModel(tenantID, tenantpkg.Organization, true)}
    43  	applicationsModel               = []*model.Application{fixApplicationModel(applicationID)}
    44  )
    45  
    46  func TestDirective_TestSynchronizeApplicationTenancy(t *testing.T) {
    47  	testCases := []struct {
    48  		Name             string
    49  		TxFn             func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner)
    50  		TenantSvcFn      func() *automock.BusinessTenantMappingService
    51  		ApplicationSvcFn func() *automock.ApplicationService
    52  		GetCtx           func() context.Context
    53  		Resolver         func() func(ctx context.Context) (res interface{}, err error)
    54  		EventType        schema.EventType
    55  		ExpectedError    error
    56  		ExpectedResult   interface{}
    57  	}{
    58  		{
    59  			Name: "NEW_APPLICATION flow: Success",
    60  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
    61  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
    62  				tenantService := &automock.BusinessTenantMappingService{}
    63  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
    64  				tenantService.On("GetCustomerIDParentRecursively", txtest.CtxWithDBMatcher(), tenantID).Return(parentTenantID, nil)
    65  				tenantService.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), parentTenantID).Return(customerTenantModel, nil)
    66  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Account).Return([]*model.BusinessTenantMapping{accountTenantModel}, nil)
    67  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerTrue).Return(nil)
    68  
    69  				return tenantService
    70  			},
    71  			GetCtx:           fixContextWithTenant,
    72  			ApplicationSvcFn: fixEmptyApplicationService,
    73  			Resolver:         fixSuccessGraphQLApplicationResolver,
    74  			EventType:        schema.EventTypeNewApplication,
    75  			ExpectedResult:   mockedGraphQLApplicationNextOutput(),
    76  		},
    77  		{
    78  			Name:             "Should not do anything when resolver fails",
    79  			TxFn:             txtest.NewTransactionContextGenerator(nil).ThatDoesntStartTransaction,
    80  			TenantSvcFn:      fixEmptyTenantService,
    81  			GetCtx:           fixContextWithTenant,
    82  			ApplicationSvcFn: fixEmptyApplicationService,
    83  			Resolver:         fixErrorResolver,
    84  			EventType:        schema.EventTypeNewApplication,
    85  			ExpectedError:    testErr,
    86  		},
    87  		{
    88  			Name:             "Should fail when transaction fails to begin",
    89  			TxFn:             txtest.NewTransactionContextGenerator(testErr).ThatFailsOnBegin,
    90  			TenantSvcFn:      fixEmptyTenantService,
    91  			GetCtx:           fixContextWithTenant,
    92  			ApplicationSvcFn: fixEmptyApplicationService,
    93  			Resolver:         fixSuccessTenantResolver,
    94  			EventType:        schema.EventTypeNewApplication,
    95  			ExpectedError:    testErr,
    96  		},
    97  		{
    98  			Name:        "NEW_APPLICATION flow: Should fail when there is no tenant in the context",
    99  			TxFn:        txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   100  			TenantSvcFn: fixEmptyTenantService,
   101  			GetCtx: func() context.Context {
   102  				return context.TODO()
   103  			},
   104  			ApplicationSvcFn: fixEmptyApplicationService,
   105  			Resolver:         fixSuccessTenantResolver,
   106  			EventType:        schema.EventTypeNewApplication,
   107  			ExpectedError:    errors.New("cannot read tenant from context"),
   108  		},
   109  		{
   110  			Name: "NEW_APPLICATION flow: Should fail when getting tenant by ID",
   111  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   112  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   113  				tenantService := &automock.BusinessTenantMappingService{}
   114  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(nil, testErr)
   115  				return tenantService
   116  			},
   117  			GetCtx:           fixContextWithTenant,
   118  			ApplicationSvcFn: fixEmptyApplicationService,
   119  			Resolver:         fixSuccessTenantResolver,
   120  			EventType:        schema.EventTypeNewApplication,
   121  			ExpectedError:    testErr,
   122  		},
   123  		{
   124  			Name: "NEW_APPLICATION flow: Should not do anything when tenant type is not expected",
   125  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   126  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   127  				tenantService := &automock.BusinessTenantMappingService{}
   128  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(subaccountTenantModel, nil)
   129  				return tenantService
   130  			},
   131  			GetCtx:           fixContextWithTenant,
   132  			ApplicationSvcFn: fixEmptyApplicationService,
   133  			Resolver:         fixSuccessTenantResolver,
   134  			EventType:        schema.EventTypeNewApplication,
   135  			ExpectedResult:   mockedTenantNextOutput(),
   136  		},
   137  		{
   138  			Name: "NEW_APPLICATION flow: Should fail when parsing response to graphQL entity",
   139  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   140  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   141  				tenantService := &automock.BusinessTenantMappingService{}
   142  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
   143  				return tenantService
   144  			},
   145  			GetCtx:           fixContextWithTenant,
   146  			ApplicationSvcFn: fixEmptyApplicationService,
   147  			Resolver:         fixSuccessTenantResolver,
   148  			EventType:        schema.EventTypeNewApplication,
   149  			ExpectedError:    errors.New("Invalid data [reason=An error occurred while casting the response entity: 12345678-1111-4617-0000-f6fcf52a1e7e]"),
   150  		},
   151  		{
   152  			Name: "NEW_APPLICATION flow: Should fail when getting parent customer ID",
   153  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   154  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   155  				tenantService := &automock.BusinessTenantMappingService{}
   156  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
   157  				tenantService.On("GetCustomerIDParentRecursively", txtest.CtxWithDBMatcher(), tenantID).Return("", testErr)
   158  				return tenantService
   159  			},
   160  			GetCtx:           fixContextWithTenant,
   161  			ApplicationSvcFn: fixEmptyApplicationService,
   162  			Resolver:         fixSuccessGraphQLApplicationResolver,
   163  			EventType:        schema.EventTypeNewApplication,
   164  			ExpectedError:    testErr,
   165  		},
   166  		{
   167  			Name: "NEW_APPLICATION flow: Should fail when getting tenant by external ID",
   168  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   169  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   170  				tenantService := &automock.BusinessTenantMappingService{}
   171  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
   172  				tenantService.On("GetCustomerIDParentRecursively", txtest.CtxWithDBMatcher(), tenantID).Return(parentTenantID, nil)
   173  				tenantService.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), parentTenantID).Return(nil, testErr)
   174  				return tenantService
   175  			},
   176  			GetCtx:           fixContextWithTenant,
   177  			ApplicationSvcFn: fixEmptyApplicationService,
   178  			Resolver:         fixSuccessGraphQLApplicationResolver,
   179  			EventType:        schema.EventTypeNewApplication,
   180  			ExpectedError:    testErr,
   181  		},
   182  		{
   183  			Name: "NEW_APPLICATION flow: Should fail when listing tenant by parent and type",
   184  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   185  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   186  				tenantService := &automock.BusinessTenantMappingService{}
   187  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
   188  				tenantService.On("GetCustomerIDParentRecursively", txtest.CtxWithDBMatcher(), tenantID).Return(parentTenantID, nil)
   189  				tenantService.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), parentTenantID).Return(customerTenantModel, nil)
   190  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Account).Return(nil, testErr)
   191  				return tenantService
   192  			},
   193  			GetCtx:           fixContextWithTenant,
   194  			ApplicationSvcFn: fixEmptyApplicationService,
   195  			Resolver:         fixSuccessGraphQLApplicationResolver,
   196  			EventType:        schema.EventTypeNewApplication,
   197  			ExpectedError:    testErr,
   198  		},
   199  		{
   200  			Name: "NEW_APPLICATION flow: Should fail when creating tenant access for Application resources",
   201  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   202  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   203  				tenantService := &automock.BusinessTenantMappingService{}
   204  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), tenantID).Return(rgTenantModel, nil)
   205  				tenantService.On("GetCustomerIDParentRecursively", txtest.CtxWithDBMatcher(), tenantID).Return(parentTenantID, nil)
   206  				tenantService.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), parentTenantID).Return(customerTenantModel, nil)
   207  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Account).Return([]*model.BusinessTenantMapping{accountTenantModel}, nil)
   208  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerTrue).Return(testErr)
   209  				return tenantService
   210  			},
   211  			GetCtx:           fixContextWithTenant,
   212  			ApplicationSvcFn: fixEmptyApplicationService,
   213  			Resolver:         fixSuccessGraphQLApplicationResolver,
   214  			EventType:        schema.EventTypeNewApplication,
   215  			ExpectedError:    testErr,
   216  		},
   217  		{
   218  			Name: "NEW_SINGLE_TENANT flow: Success",
   219  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   220  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   221  				tenantService := &automock.BusinessTenantMappingService{}
   222  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(newAccountTenantModel, nil)
   223  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   224  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerFalse).Return(nil)
   225  				return tenantService
   226  			},
   227  			GetCtx: fixContextWithTenant,
   228  			ApplicationSvcFn: func() *automock.ApplicationService {
   229  				appService := &automock.ApplicationService{}
   230  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(applicationsModel, nil)
   231  				return appService
   232  			},
   233  			Resolver:       fixSuccessTenantResolver,
   234  			EventType:      schema.EventTypeNewSingleTenant,
   235  			ExpectedResult: mockedTenantNextOutput(),
   236  		},
   237  		{
   238  			Name:             "NEW_SINGLE_TENANT flow: Should fail when parsing response to string",
   239  			TxFn:             txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   240  			TenantSvcFn:      fixEmptyTenantService,
   241  			GetCtx:           fixContextWithTenant,
   242  			ApplicationSvcFn: fixEmptyApplicationService,
   243  			Resolver:         fixSuccessGraphQLApplicationResolver,
   244  			EventType:        schema.EventTypeNewSingleTenant,
   245  			ExpectedError:    errors.New("An error occurred while casting the response entity"),
   246  		},
   247  		{
   248  			Name: "NEW_SINGLE_TENANT flow: Should fail when getting tenant by ID",
   249  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   250  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   251  				tenantService := &automock.BusinessTenantMappingService{}
   252  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(nil, testErr)
   253  				return tenantService
   254  			},
   255  			GetCtx:           fixContextWithTenant,
   256  			ApplicationSvcFn: fixEmptyApplicationService,
   257  			Resolver:         fixSuccessTenantResolver,
   258  			EventType:        schema.EventTypeNewSingleTenant,
   259  			ExpectedError:    testErr,
   260  		},
   261  		{
   262  			Name: "NEW_SINGLE_TENANT flow: Should not do anything when new tenant is not Account",
   263  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   264  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   265  				tenantService := &automock.BusinessTenantMappingService{}
   266  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(subaccountTenantModel, nil)
   267  				return tenantService
   268  			},
   269  			GetCtx:           fixContextWithTenant,
   270  			ApplicationSvcFn: fixEmptyApplicationService,
   271  			Resolver:         fixSuccessTenantResolver,
   272  			EventType:        schema.EventTypeNewSingleTenant,
   273  			ExpectedResult:   mockedTenantNextOutput(),
   274  		},
   275  		{
   276  			Name: "NEW_SINGLE_TENANT flow: Should not do anything when new tenant is Account but does not have a parent",
   277  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   278  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   279  				tenantService := &automock.BusinessTenantMappingService{}
   280  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantWithoutParentModel, nil)
   281  				return tenantService
   282  			},
   283  			GetCtx:           fixContextWithTenant,
   284  			ApplicationSvcFn: fixEmptyApplicationService,
   285  			Resolver:         fixSuccessTenantResolver,
   286  			EventType:        schema.EventTypeNewSingleTenant,
   287  			ExpectedResult:   mockedTenantNextOutput(),
   288  		},
   289  		{
   290  			Name: "NEW_SINGLE_TENANT flow: Should fail when listing tenants by parent and type errors",
   291  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   292  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   293  				tenantService := &automock.BusinessTenantMappingService{}
   294  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantModel, nil)
   295  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(nil, testErr)
   296  				return tenantService
   297  			},
   298  			GetCtx:           fixContextWithTenant,
   299  			ApplicationSvcFn: fixEmptyApplicationService,
   300  			Resolver:         fixSuccessTenantResolver,
   301  			EventType:        schema.EventTypeNewSingleTenant,
   302  			ExpectedError:    testErr,
   303  		},
   304  		{
   305  			Name: "NEW_SINGLE_TENANT flow: Should fail when listing applications for org tenant errors",
   306  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   307  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   308  				tenantService := &automock.BusinessTenantMappingService{}
   309  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantModel, nil)
   310  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   311  				return tenantService
   312  			},
   313  			GetCtx: fixContextWithTenant,
   314  			ApplicationSvcFn: func() *automock.ApplicationService {
   315  				appService := &automock.ApplicationService{}
   316  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(nil, testErr)
   317  				return appService
   318  			},
   319  			Resolver:      fixSuccessTenantResolver,
   320  			EventType:     schema.EventTypeNewSingleTenant,
   321  			ExpectedError: testErr,
   322  		},
   323  		{
   324  			Name: "NEW_SINGLE_TENANT flow: Should fail when creating tenant access for application errors",
   325  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   326  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   327  				tenantService := &automock.BusinessTenantMappingService{}
   328  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(newAccountTenantModel, nil)
   329  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   330  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerFalse).Return(testErr)
   331  				return tenantService
   332  			},
   333  			GetCtx: fixContextWithTenant,
   334  			ApplicationSvcFn: func() *automock.ApplicationService {
   335  				appService := &automock.ApplicationService{}
   336  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(applicationsModel, nil)
   337  				return appService
   338  			},
   339  			Resolver:      fixSuccessTenantResolver,
   340  			EventType:     schema.EventTypeNewSingleTenant,
   341  			ExpectedError: testErr,
   342  		},
   343  		{
   344  			Name: "NEW_MULTIPLE_TENANTS flow: Success",
   345  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   346  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   347  				tenantService := &automock.BusinessTenantMappingService{}
   348  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(newAccountTenantModel, nil)
   349  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   350  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerFalse).Return(nil)
   351  				return tenantService
   352  			},
   353  			GetCtx: fixContextWithTenant,
   354  			ApplicationSvcFn: func() *automock.ApplicationService {
   355  				appService := &automock.ApplicationService{}
   356  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(applicationsModel, nil)
   357  				return appService
   358  			},
   359  			Resolver:       fixSuccessMultipleTenantResolver,
   360  			EventType:      schema.EventTypeNewMultipleTenants,
   361  			ExpectedResult: mockedMultipleTenantsNextOutput(),
   362  		},
   363  		{
   364  			Name:             "NEW_MULTIPLE_TENANTS flow: Should fail when parsing response to string array",
   365  			TxFn:             txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   366  			TenantSvcFn:      fixEmptyTenantService,
   367  			GetCtx:           fixContextWithTenant,
   368  			ApplicationSvcFn: fixEmptyApplicationService,
   369  			Resolver:         fixSuccessGraphQLApplicationResolver,
   370  			EventType:        schema.EventTypeNewMultipleTenants,
   371  			ExpectedError:    errors.New("An error occurred while casting the response entity"),
   372  		},
   373  		{
   374  			Name: "NEW_MULTIPLE_TENANTS flow: Should fail getting tenant by ID errors",
   375  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   376  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   377  				tenantService := &automock.BusinessTenantMappingService{}
   378  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(nil, testErr)
   379  				return tenantService
   380  			},
   381  			GetCtx:           fixContextWithTenant,
   382  			ApplicationSvcFn: fixEmptyApplicationService,
   383  			Resolver:         fixSuccessMultipleTenantResolver,
   384  			EventType:        schema.EventTypeNewMultipleTenants,
   385  			ExpectedError:    testErr,
   386  		},
   387  		{
   388  			Name: "NEW_MULTIPLE_TENANTS flow: Should not do anything when tenant type is not Account",
   389  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   390  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   391  				tenantService := &automock.BusinessTenantMappingService{}
   392  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(rgTenantModel, nil)
   393  				return tenantService
   394  			},
   395  			GetCtx:           fixContextWithTenant,
   396  			ApplicationSvcFn: fixEmptyApplicationService,
   397  			Resolver:         fixSuccessMultipleTenantResolver,
   398  			EventType:        schema.EventTypeNewMultipleTenants,
   399  			ExpectedResult:   mockedMultipleTenantsNextOutput(),
   400  		},
   401  		{
   402  			Name: "NEW_MULTIPLE_TENANTS flow: Should not do anything when tenant does not have parent",
   403  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatSucceeds,
   404  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   405  				tenantService := &automock.BusinessTenantMappingService{}
   406  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantWithoutParentModel, nil)
   407  				return tenantService
   408  			},
   409  			GetCtx:           fixContextWithTenant,
   410  			ApplicationSvcFn: fixEmptyApplicationService,
   411  			Resolver:         fixSuccessMultipleTenantResolver,
   412  			EventType:        schema.EventTypeNewMultipleTenants,
   413  			ExpectedResult:   mockedMultipleTenantsNextOutput(),
   414  		},
   415  		{
   416  			Name: "NEW_MULTIPLE_TENANTS flow: Should fail when listing tenants by parent and type errors",
   417  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   418  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   419  				tenantService := &automock.BusinessTenantMappingService{}
   420  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantModel, nil)
   421  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(nil, testErr)
   422  				return tenantService
   423  			},
   424  			GetCtx:           fixContextWithTenant,
   425  			ApplicationSvcFn: fixEmptyApplicationService,
   426  			Resolver:         fixSuccessMultipleTenantResolver,
   427  			EventType:        schema.EventTypeNewMultipleTenants,
   428  			ExpectedError:    testErr,
   429  		},
   430  		{
   431  			Name: "NEW_MULTIPLE_TENANTS flow: Should fail when listing applications for org tenant errors",
   432  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   433  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   434  				tenantService := &automock.BusinessTenantMappingService{}
   435  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(accountTenantModel, nil)
   436  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   437  				return tenantService
   438  			},
   439  			GetCtx: fixContextWithTenant,
   440  			ApplicationSvcFn: func() *automock.ApplicationService {
   441  				appService := &automock.ApplicationService{}
   442  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(nil, testErr)
   443  				return appService
   444  			},
   445  			Resolver:      fixSuccessMultipleTenantResolver,
   446  			EventType:     schema.EventTypeNewMultipleTenants,
   447  			ExpectedError: testErr,
   448  		},
   449  		{
   450  			Name: "NEW_MULTIPLE_TENANTS flow: Should fail when creating tenant access for application errors",
   451  			TxFn: txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit,
   452  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   453  				tenantService := &automock.BusinessTenantMappingService{}
   454  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(newAccountTenantModel, nil)
   455  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   456  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerFalse).Return(testErr)
   457  				return tenantService
   458  			},
   459  			GetCtx: fixContextWithTenant,
   460  			ApplicationSvcFn: func() *automock.ApplicationService {
   461  				appService := &automock.ApplicationService{}
   462  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(applicationsModel, nil)
   463  				return appService
   464  			},
   465  			Resolver:      fixSuccessMultipleTenantResolver,
   466  			EventType:     schema.EventTypeNewMultipleTenants,
   467  			ExpectedError: testErr,
   468  		},
   469  		{
   470  			Name: "Should fail when committing transaction",
   471  			TxFn: txtest.NewTransactionContextGenerator(testErr).ThatFailsOnCommit,
   472  			TenantSvcFn: func() *automock.BusinessTenantMappingService {
   473  				tenantService := &automock.BusinessTenantMappingService{}
   474  				tenantService.On("GetTenantByID", txtest.CtxWithDBMatcher(), newTenantID).Return(newAccountTenantModel, nil)
   475  				tenantService.On("ListByParentAndType", txtest.CtxWithDBMatcher(), parentTenantID, tenantpkg.Organization).Return(orgTenantsModel, nil)
   476  				tenantService.On("CreateTenantAccessForResource", txtest.CtxWithDBMatcher(), tenantAccessWithOwnerFalse).Return(nil)
   477  				return tenantService
   478  			},
   479  			GetCtx: fixContextWithTenant,
   480  			ApplicationSvcFn: func() *automock.ApplicationService {
   481  				appService := &automock.ApplicationService{}
   482  				appService.On("ListAll", txtest.CtxWithDBMatcher()).Return(applicationsModel, nil)
   483  				return appService
   484  			},
   485  			Resolver:      fixSuccessMultipleTenantResolver,
   486  			EventType:     schema.EventTypeNewMultipleTenants,
   487  			ExpectedError: testErr,
   488  		},
   489  	}
   490  
   491  	for _, testCase := range testCases {
   492  		t.Run(testCase.Name, func(t *testing.T) {
   493  			tenantSvc := testCase.TenantSvcFn()
   494  			appSvc := testCase.ApplicationSvcFn()
   495  			persist, transact := testCase.TxFn()
   496  
   497  			directive := applicationtenancy.NewDirective(transact, tenantSvc, appSvc)
   498  
   499  			// WHEN
   500  			res, err := directive.SynchronizeApplicationTenancy(testCase.GetCtx(), nil, testCase.Resolver(), testCase.EventType)
   501  
   502  			// THEN
   503  			if testCase.ExpectedError != nil {
   504  				require.Error(t, err)
   505  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   506  			} else {
   507  				assert.NoError(t, err)
   508  				assert.Equal(t, testCase.ExpectedResult, res)
   509  			}
   510  
   511  			mock.AssertExpectationsForObjects(t, persist, transact, tenantSvc, appSvc)
   512  		})
   513  	}
   514  }
   515  
   516  type dummyResolver struct {
   517  	called bool
   518  }
   519  
   520  func (d *dummyResolver) SuccessTenantResolve(_ context.Context) (res interface{}, err error) {
   521  	d.called = true
   522  	return mockedTenantNextOutput(), nil
   523  }
   524  
   525  func (d *dummyResolver) SuccessMultipleTenantResolve(_ context.Context) (res interface{}, err error) {
   526  	d.called = true
   527  	return mockedMultipleTenantsNextOutput(), nil
   528  }
   529  
   530  func (d *dummyResolver) ErrorResolve(_ context.Context) (res interface{}, err error) {
   531  	d.called = true
   532  	return nil, testErr
   533  }
   534  
   535  func (d *dummyResolver) SuccessGraphQLApplicationResolve(_ context.Context) (res interface{}, err error) {
   536  	d.called = true
   537  	return mockedGraphQLApplicationNextOutput(), nil
   538  }
   539  
   540  func mockedGraphQLApplicationNextOutput() *schema.Application {
   541  	return &schema.Application{
   542  		BaseEntity: &schema.BaseEntity{
   543  			ID:    applicationID,
   544  			Ready: true,
   545  		},
   546  	}
   547  }
   548  
   549  func mockedTenantNextOutput() string {
   550  	return newTenantID
   551  }
   552  
   553  func mockedMultipleTenantsNextOutput() []string {
   554  	return []string{newTenantID}
   555  }
   556  
   557  func fixSuccessGraphQLApplicationResolver() func(_ context.Context) (res interface{}, err error) {
   558  	r := &dummyResolver{}
   559  	return r.SuccessGraphQLApplicationResolve
   560  }
   561  
   562  func fixSuccessTenantResolver() func(_ context.Context) (res interface{}, err error) {
   563  	r := &dummyResolver{}
   564  	return r.SuccessTenantResolve
   565  }
   566  
   567  func fixSuccessMultipleTenantResolver() func(_ context.Context) (res interface{}, err error) {
   568  	r := &dummyResolver{}
   569  	return r.SuccessMultipleTenantResolve
   570  }
   571  
   572  func fixErrorResolver() func(_ context.Context) (res interface{}, err error) {
   573  	r := &dummyResolver{}
   574  	return r.ErrorResolve
   575  }
   576  
   577  func fixBusinessTenantMappingModel(id string, tntType tenantpkg.Type, hasParent bool) *model.BusinessTenantMapping {
   578  	parent := ""
   579  	if hasParent {
   580  		parent = parentTenantID
   581  	}
   582  	return &model.BusinessTenantMapping{
   583  		ID:             id,
   584  		ExternalTenant: id,
   585  		Type:           tntType,
   586  		Parent:         parent,
   587  	}
   588  }
   589  
   590  func fixApplicationModel(id string) *model.Application {
   591  	return &model.Application{
   592  		Name: "test-app",
   593  		BaseEntity: &model.BaseEntity{
   594  			ID:    id,
   595  			Ready: true,
   596  		},
   597  	}
   598  }
   599  
   600  func fixEmptyApplicationService() *automock.ApplicationService {
   601  	return &automock.ApplicationService{}
   602  }
   603  
   604  func fixEmptyTenantService() *automock.BusinessTenantMappingService {
   605  	return &automock.BusinessTenantMappingService{}
   606  }
   607  
   608  func fixContextWithTenant() context.Context {
   609  	return context.WithValue(context.TODO(), tenant.TenantContextKey, tenant.TenantCtx{InternalID: tenantID})
   610  }
   611  
   612  func fixTenantAccessModel(tenantID, resourceID string, resourceType resource.Type, owner bool) *model.TenantAccess {
   613  	return &model.TenantAccess{
   614  		InternalTenantID: tenantID,
   615  		ResourceType:     resourceType,
   616  		ResourceID:       resourceID,
   617  		Owner:            owner,
   618  	}
   619  }