github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/tenantfetchersvc/resync/tenant_manager_test.go (about)

     1  package resync_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	domaintenant "github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
     8  	"github.com/kyma-incubator/compass/components/director/internal/model"
     9  	"github.com/kyma-incubator/compass/components/director/internal/tenantfetchersvc/resync"
    10  	"github.com/kyma-incubator/compass/components/director/internal/tenantfetchersvc/resync/automock"
    11  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    12  	"github.com/kyma-incubator/compass/components/director/pkg/tenant"
    13  	"github.com/pkg/errors"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/mock"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  const (
    20  	centralRegion = "central"
    21  	region        = "europe-east"
    22  	provider      = "external-service"
    23  )
    24  
    25  var (
    26  	testLicenseType = "LICENSETYPE"
    27  )
    28  
    29  func TestTenantManager_TenantsToCreate(t *testing.T) {
    30  	const (
    31  		centralRegion = "central"
    32  		unknownRegion = "europe-east"
    33  		provider      = "external-service"
    34  		timestamp     = "1234567899987"
    35  	)
    36  
    37  	ctx := context.TODO()
    38  
    39  	// GIVEN
    40  	tenantConverter := domaintenant.NewConverter()
    41  
    42  	jobConfig := configForTenantType(tenant.Account)
    43  
    44  	busTenant1 := fixBusinessTenantMappingInput("1", provider, "subdomain-1", "", "", tenant.Account, &testLicenseType)
    45  	busTenant2 := fixBusinessTenantMappingInput("2", provider, "subdomain-2", "", "", tenant.Account, nil)
    46  
    47  	event1 := fixEvent(t, "GlobalAccount", busTenant1.ExternalTenant, eventFieldsFromTenant(tenant.Account, jobConfig.APIConfig.TenantFieldMapping, busTenant1))
    48  	event2 := fixEvent(t, "GlobalAccount", busTenant2.ExternalTenant, eventFieldsFromTenant(tenant.Account, jobConfig.APIConfig.TenantFieldMapping, busTenant2))
    49  
    50  	pageOneQueryParams := resync.QueryParams{
    51  		"pageSize":  "1",
    52  		"pageNum":   "1",
    53  		"timestamp": timestamp,
    54  	}
    55  
    56  	testCases := []struct {
    57  		name              string
    58  		jobConfigFn       func() resync.JobConfig
    59  		region            string
    60  		directorClientFn  func() *automock.DirectorGraphQLClient
    61  		universalClientFn func(resync.JobConfig) *automock.EventAPIClient
    62  		regionalClientsFn func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient)
    63  		expectedTenants   []model.BusinessTenantMappingInput
    64  		expectedErrMsg    string
    65  	}{
    66  		{
    67  			name: "Success when only one page is returned from created tenants events",
    68  			jobConfigFn: func() resync.JobConfig {
    69  				return configForTenantType(tenant.Account)
    70  			},
    71  			region:           centralRegion,
    72  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
    73  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
    74  				client := &automock.EventAPIClient{}
    75  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
    76  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
    77  
    78  				return client
    79  			},
    80  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
    81  				return nil, nil
    82  			},
    83  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
    84  		},
    85  		{
    86  			name: "Success when two pages are returned from created tenants events",
    87  			jobConfigFn: func() resync.JobConfig {
    88  				return configForTenantType(tenant.Account)
    89  			},
    90  			region:           centralRegion,
    91  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
    92  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
    93  				pageTwoQueryParams := resync.QueryParams{
    94  					"pageSize":  "1",
    95  					"pageNum":   "2",
    96  					"timestamp": timestamp,
    97  				}
    98  
    99  				client := &automock.EventAPIClient{}
   100  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   101  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageTwoQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event2), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   102  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   103  				return client
   104  			},
   105  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   106  				return nil, nil
   107  			},
   108  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1, busTenant2},
   109  		},
   110  		{
   111  			name: "Success when only one page is returned from updated tenants events",
   112  			jobConfigFn: func() resync.JobConfig {
   113  				return configForTenantType(tenant.Account)
   114  			},
   115  			region:           centralRegion,
   116  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   117  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   118  				client := &automock.EventAPIClient{}
   119  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   120  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   121  				return client
   122  			},
   123  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   124  				return nil, nil
   125  			},
   126  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   127  		},
   128  		{
   129  			name: "Success when events for both create and update are returned for the same tenant",
   130  			jobConfigFn: func() resync.JobConfig {
   131  				return configForTenantType(tenant.Account)
   132  			},
   133  			region:           centralRegion,
   134  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   135  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   136  				client := &automock.EventAPIClient{}
   137  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   138  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   139  				return client
   140  			},
   141  			regionalClientsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   142  				return nil, nil
   143  			},
   144  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   145  		},
   146  		{
   147  			name: "Success when events for both create and update are returned for different tenants",
   148  			jobConfigFn: func() resync.JobConfig {
   149  				return configForTenantType(tenant.Account)
   150  			},
   151  			region:           centralRegion,
   152  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   153  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   154  				client := &automock.EventAPIClient{}
   155  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   156  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event2), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   157  				return client
   158  			},
   159  			regionalClientsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   160  				return nil, nil
   161  			},
   162  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1, busTenant2},
   163  		},
   164  		{
   165  			name: "Success when regional client is available",
   166  			jobConfigFn: func() resync.JobConfig {
   167  				cfg := configForTenantType(tenant.Account)
   168  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   169  				reg := cfg.APIConfig
   170  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   171  				return cfg
   172  			},
   173  			region:           centralRegion,
   174  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   175  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   176  				client := &automock.EventAPIClient{}
   177  				queryParams := resync.QueryParams{
   178  					"pageSize":  "1",
   179  					"pageNum":   "1",
   180  					"timestamp": timestamp,
   181  					"region":    centralRegion,
   182  				}
   183  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   184  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, queryParams).Return(nil, nil).Once()
   185  				return client
   186  			},
   187  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   188  				client := &automock.EventAPIClient{}
   189  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   190  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   191  
   192  				details := map[string]resync.EventAPIClient{
   193  					centralRegion: client,
   194  				}
   195  
   196  				return details, []*automock.EventAPIClient{client}
   197  			},
   198  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   199  		},
   200  		{
   201  			name: "Success when regional and universal clients return the same tenant",
   202  			jobConfigFn: func() resync.JobConfig {
   203  				cfg := configForTenantType(tenant.Account)
   204  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   205  				reg := cfg.APIConfig
   206  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   207  				return cfg
   208  			},
   209  			region:           centralRegion,
   210  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   211  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   212  				client := &automock.EventAPIClient{}
   213  				queryParams := resync.QueryParams{
   214  					"pageSize":  "1",
   215  					"pageNum":   "1",
   216  					"timestamp": timestamp,
   217  					"region":    centralRegion,
   218  				}
   219  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   220  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, queryParams).Return(nil, nil).Once()
   221  				return client
   222  			},
   223  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   224  				client := &automock.EventAPIClient{}
   225  				busTenantWithoutSubdomain := busTenant1
   226  				busTenantWithoutSubdomain.Subdomain = ""
   227  				event := fixEvent(t, "GlobalAccount", busTenant1.ExternalTenant, eventFieldsFromTenant(tenant.Account, cfg.RegionalAPIConfigs[centralRegion].TenantFieldMapping, busTenant1))
   228  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   229  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   230  
   231  				details := map[string]resync.EventAPIClient{
   232  					centralRegion: client,
   233  				}
   234  
   235  				return details, []*automock.EventAPIClient{client}
   236  			},
   237  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   238  		},
   239  		{
   240  			name: "Fail when fetching created tenants events returns an error",
   241  			jobConfigFn: func() resync.JobConfig {
   242  				return configForTenantType(tenant.Account)
   243  			},
   244  			region:           centralRegion,
   245  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   246  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   247  				client := &automock.EventAPIClient{}
   248  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(nil, errors.New("failed to get created")).Once()
   249  				return client
   250  			},
   251  			regionalClientsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   252  				return nil, nil
   253  			},
   254  			expectedErrMsg: "while fetching created tenants",
   255  		},
   256  		{
   257  			name: "Fail when fetching updated tenants events returns an error",
   258  			jobConfigFn: func() resync.JobConfig {
   259  				return configForTenantType(tenant.Account)
   260  			},
   261  			region:           centralRegion,
   262  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   263  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   264  				client := &automock.EventAPIClient{}
   265  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   266  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedAccountType, pageOneQueryParams).Return(nil, errors.New("failed to get updated")).Once()
   267  				return client
   268  			},
   269  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   270  				return nil, nil
   271  			},
   272  			expectedErrMsg: "while fetching updated tenants",
   273  		},
   274  		{
   275  			name: "Fail when API client returns an error while fetching tenants",
   276  			jobConfigFn: func() resync.JobConfig {
   277  				cfg := configForTenantType(tenant.Account)
   278  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   279  				reg := cfg.APIConfig
   280  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   281  				return cfg
   282  			},
   283  			region:           centralRegion,
   284  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   285  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   286  				client := &automock.EventAPIClient{}
   287  				queryParams := resync.QueryParams{
   288  					"pageSize":  "1",
   289  					"pageNum":   "1",
   290  					"timestamp": timestamp,
   291  					"region":    centralRegion,
   292  				}
   293  				client.On("FetchTenantEventsPage", ctx, resync.CreatedAccountType, queryParams).Return(nil, errors.New("error")).Once()
   294  				return client
   295  			},
   296  			regionalClientsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   297  				return nil, nil
   298  			},
   299  			expectedErrMsg: "while fetching created tenants",
   300  		},
   301  	}
   302  
   303  	for _, tc := range testCases {
   304  		t.Run(tc.name, func(t *testing.T) {
   305  			cfg := tc.jobConfigFn()
   306  
   307  			directorClient := tc.directorClientFn()
   308  			universalClient := tc.universalClientFn(cfg)
   309  			regionalDetails, clientMocks := tc.regionalClientsFn(cfg)
   310  
   311  			defer func(t *testing.T) {
   312  				mock.AssertExpectationsForObjects(t, directorClient, universalClient)
   313  				for _, clientMock := range clientMocks {
   314  					clientMock.AssertExpectations(t)
   315  				}
   316  			}(t)
   317  
   318  			manager, err := resync.NewTenantsManager(cfg, directorClient, universalClient, regionalDetails, tenantConverter)
   319  			require.NoError(t, err)
   320  
   321  			res, err := manager.TenantsToCreate(ctx, tc.region, timestamp)
   322  			if len(tc.expectedErrMsg) > 0 {
   323  				require.Error(t, err)
   324  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   325  			} else {
   326  				require.NoError(t, err)
   327  				require.EqualValues(t, tc.expectedTenants, res)
   328  			}
   329  		})
   330  	}
   331  }
   332  
   333  func TestTenantManager_CreateTenants(t *testing.T) {
   334  	const (
   335  		region   = "europe-east"
   336  		provider = "external-service"
   337  
   338  		failedToStoreTenantsErrMsg = "failed to store tenants in Director"
   339  	)
   340  
   341  	ctx := context.TODO()
   342  
   343  	// GIVEN
   344  	tenantConverter := domaintenant.NewConverter()
   345  
   346  	busTenant1 := fixBusinessTenantMappingInput("1", provider, "subdomain-1", region, "", tenant.Account, &testLicenseType)
   347  	busTenant2 := fixBusinessTenantMappingInput("2", provider, "subdomain-2", region, "", tenant.Account, nil)
   348  	busTenants := []model.BusinessTenantMappingInput{busTenant1, busTenant2}
   349  
   350  	testCases := []struct {
   351  		name             string
   352  		jobConfigFn      func() resync.JobConfig
   353  		directorClientFn func() *automock.DirectorGraphQLClient
   354  		expectedErrMsg   string
   355  	}{
   356  		{
   357  			name: "Success when tenants are stored in one chunk",
   358  			jobConfigFn: func() resync.JobConfig {
   359  				return configForTenantType(tenant.Account)
   360  			},
   361  			directorClientFn: func() *automock.DirectorGraphQLClient {
   362  				gqlClient := &automock.DirectorGraphQLClient{}
   363  				gqlClient.On("WriteTenants", mock.Anything, matchArrayWithoutOrderArgument(tenantConverter.MultipleInputToGraphQLInput(busTenants))).Return(nil)
   364  				return gqlClient
   365  			},
   366  		},
   367  		{
   368  			name: "Success when tenants are stored in more than one chunk",
   369  			jobConfigFn: func() resync.JobConfig {
   370  				cfg := configForTenantType(tenant.Account)
   371  				cfg.TenantOperationChunkSize = 1
   372  				return cfg
   373  			},
   374  			directorClientFn: func() *automock.DirectorGraphQLClient {
   375  				gqlClient := &automock.DirectorGraphQLClient{}
   376  				gqlClient.On("WriteTenants", ctx, tenantConverter.MultipleInputToGraphQLInput([]model.BusinessTenantMappingInput{busTenant1})).Return(nil).Once()
   377  				gqlClient.On("WriteTenants", ctx, tenantConverter.MultipleInputToGraphQLInput([]model.BusinessTenantMappingInput{busTenant2})).Return(nil).Once()
   378  				return gqlClient
   379  			},
   380  		},
   381  		{
   382  			name: "Fail when tenant insertion in Director returns an error",
   383  			jobConfigFn: func() resync.JobConfig {
   384  				return configForTenantType(tenant.Account)
   385  			},
   386  			directorClientFn: func() *automock.DirectorGraphQLClient {
   387  				gqlClient := &automock.DirectorGraphQLClient{}
   388  				gqlClient.On("WriteTenants", mock.Anything, matchArrayWithoutOrderArgument(tenantConverter.MultipleInputToGraphQLInput(busTenants))).Return(errors.New(failedToStoreTenantsErrMsg))
   389  				return gqlClient
   390  			},
   391  			expectedErrMsg: failedToStoreTenantsErrMsg,
   392  		},
   393  	}
   394  
   395  	for _, tc := range testCases {
   396  		t.Run(tc.name, func(t *testing.T) {
   397  			jobCfg := tc.jobConfigFn()
   398  			directorClient := tc.directorClientFn()
   399  			universalClient := &automock.EventAPIClient{}
   400  
   401  			manager, err := resync.NewTenantsManager(jobCfg, directorClient, universalClient, map[string]resync.EventAPIClient{}, tenantConverter)
   402  			require.NoError(t, err)
   403  
   404  			err = manager.CreateTenants(ctx, busTenants)
   405  			if len(tc.expectedErrMsg) > 0 {
   406  				require.Error(t, err)
   407  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   408  			} else {
   409  				require.NoError(t, err)
   410  			}
   411  		})
   412  	}
   413  }
   414  
   415  func TestTenantManager_TenantsToDelete(t *testing.T) {
   416  	const (
   417  		centralRegion = "central"
   418  		provider      = "external-service"
   419  		timestamp     = "1234567899987"
   420  	)
   421  
   422  	ctx := context.TODO()
   423  
   424  	// GIVEN
   425  	tenantConverter := domaintenant.NewConverter()
   426  
   427  	jobConfig := configForTenantType(tenant.Account)
   428  
   429  	busTenant1 := fixBusinessTenantMappingInput("1", provider, "subdomain-1", "", "", tenant.Account, &testLicenseType)
   430  	busTenant2 := fixBusinessTenantMappingInput("2", provider, "subdomain-2", "", "", tenant.Account, nil)
   431  
   432  	event1 := fixEvent(t, "GlobalAccount", busTenant1.ExternalTenant, eventFieldsFromTenant(tenant.Account, jobConfig.APIConfig.TenantFieldMapping, busTenant1))
   433  	event2 := fixEvent(t, "GlobalAccount", busTenant2.ExternalTenant, eventFieldsFromTenant(tenant.Account, jobConfig.APIConfig.TenantFieldMapping, busTenant2))
   434  
   435  	pageOneQueryParams := resync.QueryParams{
   436  		"pageSize":  "1",
   437  		"pageNum":   "1",
   438  		"timestamp": timestamp,
   439  	}
   440  
   441  	testCases := []struct {
   442  		name              string
   443  		jobConfigFn       func() resync.JobConfig
   444  		region            string
   445  		directorClientFn  func() *automock.DirectorGraphQLClient
   446  		universalClientFn func(resync.JobConfig) *automock.EventAPIClient
   447  		regionalDetailsFn func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient)
   448  		expectedTenants   []model.BusinessTenantMappingInput
   449  		expectedErrMsg    string
   450  	}{
   451  		{
   452  			name: "Success when only one page is returned from deleted tenants events",
   453  			jobConfigFn: func() resync.JobConfig {
   454  				return configForTenantType(tenant.Account)
   455  			},
   456  			region:           centralRegion,
   457  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   458  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   459  				client := &automock.EventAPIClient{}
   460  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   461  				return client
   462  			},
   463  			regionalDetailsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   464  				return nil, nil
   465  			},
   466  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   467  		},
   468  		{
   469  			name: "Success when two pages are returned from deleted tenants events",
   470  			jobConfigFn: func() resync.JobConfig {
   471  				return configForTenantType(tenant.Account)
   472  			},
   473  			region:           centralRegion,
   474  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   475  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   476  				pageTwoQueryParams := resync.QueryParams{
   477  					"pageSize":  "1",
   478  					"pageNum":   "2",
   479  					"timestamp": timestamp,
   480  				}
   481  
   482  				client := &automock.EventAPIClient{}
   483  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   484  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageTwoQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event2), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   485  
   486  				return client
   487  			},
   488  			regionalDetailsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   489  				return nil, nil
   490  			},
   491  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1, busTenant2},
   492  		},
   493  		{
   494  			name: "Success when regional client is enabled",
   495  			jobConfigFn: func() resync.JobConfig {
   496  				cfg := configForTenantType(tenant.Account)
   497  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   498  				reg := cfg.APIConfig
   499  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   500  				return cfg
   501  			},
   502  			region:           centralRegion,
   503  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   504  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   505  				client := &automock.EventAPIClient{}
   506  				queryParams := resync.QueryParams{
   507  					"pageSize":  "1",
   508  					"pageNum":   "1",
   509  					"timestamp": timestamp,
   510  					"region":    centralRegion,
   511  				}
   512  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   513  				return client
   514  			},
   515  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   516  				client := &automock.EventAPIClient{}
   517  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(nil, nil).Once()
   518  
   519  				details := map[string]resync.EventAPIClient{
   520  					centralRegion: client,
   521  				}
   522  
   523  				return details, []*automock.EventAPIClient{client}
   524  			},
   525  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   526  		},
   527  		{
   528  			name: "Success when regional and universal clients return the same tenant",
   529  			jobConfigFn: func() resync.JobConfig {
   530  				cfg := configForTenantType(tenant.Account)
   531  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   532  				reg := cfg.APIConfig
   533  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   534  				return cfg
   535  			},
   536  			region:           centralRegion,
   537  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   538  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   539  				client := &automock.EventAPIClient{}
   540  				queryParams := resync.QueryParams{
   541  					"pageSize":  "1",
   542  					"pageNum":   "1",
   543  					"timestamp": timestamp,
   544  					"region":    centralRegion,
   545  				}
   546  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   547  				return client
   548  			},
   549  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   550  				client := &automock.EventAPIClient{}
   551  				busTenantWithoutSubdomain := busTenant1
   552  				busTenantWithoutSubdomain.Subdomain = ""
   553  				event := fixEvent(t, "GlobalAccount", busTenant1.ExternalTenant, eventFieldsFromTenant(tenant.Account, cfg.RegionalAPIConfigs[centralRegion].TenantFieldMapping, busTenant1))
   554  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   555  
   556  				details := map[string]resync.EventAPIClient{
   557  					centralRegion: client,
   558  				}
   559  
   560  				return details, []*automock.EventAPIClient{client}
   561  			},
   562  			expectedTenants: []model.BusinessTenantMappingInput{busTenant1},
   563  		},
   564  		{
   565  			name: "Fail when fetching deleted tenants events returns an error",
   566  			jobConfigFn: func() resync.JobConfig {
   567  				return configForTenantType(tenant.Account)
   568  			},
   569  			region:           centralRegion,
   570  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   571  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   572  				client := &automock.EventAPIClient{}
   573  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(nil, errors.New("failed to get deleted")).Once()
   574  				return client
   575  			},
   576  			regionalDetailsFn: func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   577  				return nil, nil
   578  			},
   579  			expectedErrMsg: "while fetching deleted tenants",
   580  		},
   581  		{
   582  			name: "Fail when regional client returns an error while fetching deleted tenants",
   583  			jobConfigFn: func() resync.JobConfig {
   584  				cfg := configForTenantType(tenant.Account)
   585  				cfg.QueryConfig.RegionField = "region" // enables region query parameter
   586  				reg := cfg.APIConfig
   587  				cfg.RegionalAPIConfigs[centralRegion] = &reg
   588  				return cfg
   589  			},
   590  			region:           centralRegion,
   591  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   592  			universalClientFn: func(resync.JobConfig) *automock.EventAPIClient {
   593  				client := &automock.EventAPIClient{}
   594  				queryParams := resync.QueryParams{
   595  					"pageSize":  "1",
   596  					"pageNum":   "1",
   597  					"timestamp": timestamp,
   598  					"region":    centralRegion,
   599  				}
   600  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, queryParams).Return(nil, nil).Once()
   601  				return client
   602  			},
   603  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   604  				client := &automock.EventAPIClient{}
   605  				client.On("FetchTenantEventsPage", ctx, resync.DeletedAccountType, pageOneQueryParams).Return(nil, errors.New("error")).Once()
   606  
   607  				details := map[string]resync.EventAPIClient{
   608  					centralRegion: client,
   609  				}
   610  
   611  				return details, []*automock.EventAPIClient{client}
   612  			},
   613  			expectedErrMsg: "while fetching deleted tenants",
   614  		},
   615  	}
   616  
   617  	for _, tc := range testCases {
   618  		t.Run(tc.name, func(t *testing.T) {
   619  			cfg := tc.jobConfigFn()
   620  
   621  			directorClient := tc.directorClientFn()
   622  			universalClient := tc.universalClientFn(cfg)
   623  			regionalDetails, clientMocks := tc.regionalDetailsFn(cfg)
   624  
   625  			defer func(t *testing.T) {
   626  				mock.AssertExpectationsForObjects(t, directorClient, universalClient)
   627  				for _, clientMock := range clientMocks {
   628  					clientMock.AssertExpectations(t)
   629  				}
   630  			}(t)
   631  
   632  			manager, err := resync.NewTenantsManager(cfg, directorClient, universalClient, regionalDetails, tenantConverter)
   633  			require.NoError(t, err)
   634  
   635  			res, err := manager.TenantsToDelete(ctx, tc.region, timestamp)
   636  			if len(tc.expectedErrMsg) > 0 {
   637  				require.Error(t, err)
   638  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   639  			} else {
   640  				require.NoError(t, err)
   641  				require.EqualValues(t, tc.expectedTenants, res)
   642  			}
   643  		})
   644  	}
   645  }
   646  
   647  func TestTenantManager_DeleteTenants(t *testing.T) {
   648  	const failedToDeleteTenantsErrMsg = "failed to delete tenants in Director"
   649  
   650  	ctx := context.TODO()
   651  
   652  	// GIVEN
   653  	tenantConverter := domaintenant.NewConverter()
   654  
   655  	busTenant1 := fixBusinessTenantMappingInput("1", provider, "subdomain-1", region, "", tenant.Account, &testLicenseType)
   656  	busTenant2 := fixBusinessTenantMappingInput("2", provider, "subdomain-2", region, "", tenant.Account, nil)
   657  	busTenants := []model.BusinessTenantMappingInput{busTenant1, busTenant2}
   658  
   659  	testCases := []struct {
   660  		name             string
   661  		jobConfigFn      func() resync.JobConfig
   662  		directorClientFn func() *automock.DirectorGraphQLClient
   663  		expectedErrMsg   string
   664  	}{
   665  		{
   666  			name: "Success when tenants are deleted in one chunk",
   667  			jobConfigFn: func() resync.JobConfig {
   668  				return configForTenantType(tenant.Account)
   669  			},
   670  			directorClientFn: func() *automock.DirectorGraphQLClient {
   671  				gqlClient := &automock.DirectorGraphQLClient{}
   672  				gqlClient.On("DeleteTenants", ctx, matchArrayWithoutOrderArgument(tenantConverter.MultipleInputToGraphQLInput(busTenants))).Return(nil)
   673  				return gqlClient
   674  			},
   675  		},
   676  		{
   677  			name: "Success when tenants are deleted in more than one chunk",
   678  			jobConfigFn: func() resync.JobConfig {
   679  				cfg := configForTenantType(tenant.Account)
   680  				cfg.TenantOperationChunkSize = 1
   681  				return cfg
   682  			},
   683  			directorClientFn: func() *automock.DirectorGraphQLClient {
   684  				gqlClient := &automock.DirectorGraphQLClient{}
   685  				gqlClient.On("DeleteTenants", ctx, tenantConverter.MultipleInputToGraphQLInput([]model.BusinessTenantMappingInput{busTenant1})).Return(nil).Once()
   686  				gqlClient.On("DeleteTenants", ctx, tenantConverter.MultipleInputToGraphQLInput([]model.BusinessTenantMappingInput{busTenant2})).Return(nil).Once()
   687  				return gqlClient
   688  			},
   689  		},
   690  		{
   691  			name: "Fail when tenant deletion in Director returns an error",
   692  			jobConfigFn: func() resync.JobConfig {
   693  				return configForTenantType(tenant.Account)
   694  			},
   695  			directorClientFn: func() *automock.DirectorGraphQLClient {
   696  				gqlClient := &automock.DirectorGraphQLClient{}
   697  				gqlClient.On("DeleteTenants", ctx, matchArrayWithoutOrderArgument(tenantConverter.MultipleInputToGraphQLInput(busTenants))).Return(errors.New(failedToDeleteTenantsErrMsg))
   698  				return gqlClient
   699  			},
   700  			expectedErrMsg: failedToDeleteTenantsErrMsg,
   701  		},
   702  	}
   703  
   704  	for _, tc := range testCases {
   705  		t.Run(tc.name, func(t *testing.T) {
   706  			jobCfg := tc.jobConfigFn()
   707  			directorClient := tc.directorClientFn()
   708  			universalClient := &automock.EventAPIClient{}
   709  
   710  			manager, err := resync.NewTenantsManager(jobCfg, directorClient, universalClient, map[string]resync.EventAPIClient{}, tenantConverter)
   711  			require.NoError(t, err)
   712  
   713  			err = manager.DeleteTenants(ctx, busTenants)
   714  			if len(tc.expectedErrMsg) > 0 {
   715  				require.Error(t, err)
   716  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   717  			} else {
   718  				require.NoError(t, err)
   719  			}
   720  		})
   721  	}
   722  }
   723  
   724  func TestTenantManager_FetchTenant(t *testing.T) {
   725  	ctx := context.TODO()
   726  
   727  	// GIVEN
   728  	tenantConverter := domaintenant.NewConverter()
   729  
   730  	jobConfig := configForTenantType(tenant.Account)
   731  
   732  	busTenant := fixBusinessTenantMappingInput("1", provider, "subdomain-1", region, "", tenant.Subaccount, &testLicenseType)
   733  	event := fixEvent(t, "Subaccount", busTenant.Parent, eventFieldsFromTenant(tenant.Subaccount, jobConfig.APIConfig.TenantFieldMapping, busTenant))
   734  
   735  	queryParams := resync.QueryParams{
   736  		"pageSize": "1",
   737  		"pageNum":  "1",
   738  		"entityId": busTenant.ExternalTenant,
   739  	}
   740  
   741  	testCases := []struct {
   742  		name              string
   743  		jobConfigFn       func() resync.JobConfig
   744  		region            string
   745  		directorClientFn  func() *automock.DirectorGraphQLClient
   746  		universalClientFn func(resync.JobConfig) *automock.EventAPIClient
   747  		regionalDetailsFn func(resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient)
   748  		expectedTenant    *model.BusinessTenantMappingInput
   749  		expectedErrMsg    string
   750  	}{
   751  		{
   752  			name: "Success when tenant is found in the central region",
   753  			jobConfigFn: func() resync.JobConfig {
   754  				return configWithRegionsForSubaccounts(centralRegion, region)
   755  			},
   756  			region:           centralRegion,
   757  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   758  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   759  				client := &automock.EventAPIClient{}
   760  				queryParams := resync.QueryParams{
   761  					"pageSize": "1",
   762  					"pageNum":  "1",
   763  					"entityId": busTenant.ExternalTenant,
   764  				}
   765  				client.On("FetchTenantEventsPage", ctx, resync.CreatedSubaccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   766  				client.On("FetchTenantEventsPage", ctx, resync.UpdatedSubaccountType, queryParams).Return(nil, nil).Once()
   767  				return client
   768  			},
   769  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   770  				return nil, nil
   771  			},
   772  			expectedTenant: &busTenant,
   773  		},
   774  		{
   775  			name: "Success when tenant is found from regional client",
   776  			jobConfigFn: func() resync.JobConfig {
   777  				return configWithRegionsForSubaccounts(centralRegion, region)
   778  			},
   779  			region:           centralRegion,
   780  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   781  			universalClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   782  				client := &automock.EventAPIClient{}
   783  				client.On("FetchTenantEventsPage", mock.Anything, resync.CreatedSubaccountType, queryParams).Return(nil, nil).Once()
   784  				client.On("FetchTenantEventsPage", mock.Anything, resync.UpdatedSubaccountType, queryParams).Return(nil, nil).Once()
   785  				return client
   786  			},
   787  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   788  				client := &automock.EventAPIClient{}
   789  				client.On("FetchTenantEventsPage", mock.Anything, resync.CreatedSubaccountType, queryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
   790  				client.On("FetchTenantEventsPage", mock.Anything, resync.UpdatedSubaccountType, queryParams).Return(nil, nil).Once()
   791  
   792  				details := map[string]resync.EventAPIClient{
   793  					centralRegion: client,
   794  				}
   795  
   796  				return details, []*automock.EventAPIClient{client}
   797  			},
   798  			expectedTenant: &busTenant,
   799  		},
   800  		{
   801  			name: "[Temporary] Success when tenant is not found",
   802  			jobConfigFn: func() resync.JobConfig {
   803  				return configWithRegionsForSubaccounts(centralRegion, region)
   804  			},
   805  			region:           centralRegion,
   806  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   807  			universalClientFn: func(resync.JobConfig) *automock.EventAPIClient {
   808  				client := &automock.EventAPIClient{}
   809  				client.On("FetchTenantEventsPage", mock.Anything, resync.CreatedSubaccountType, queryParams).Return(nil, nil).Once()
   810  				client.On("FetchTenantEventsPage", mock.Anything, resync.UpdatedSubaccountType, queryParams).Return(nil, nil).Once()
   811  				return client
   812  			},
   813  			regionalDetailsFn: func(cfg resync.JobConfig) (map[string]resync.EventAPIClient, []*automock.EventAPIClient) {
   814  				client := &automock.EventAPIClient{}
   815  				client.On("FetchTenantEventsPage", mock.Anything, resync.CreatedSubaccountType, queryParams).Return(nil, nil).Once()
   816  				client.On("FetchTenantEventsPage", mock.Anything, resync.UpdatedSubaccountType, queryParams).Return(nil, nil).Once()
   817  
   818  				details := map[string]resync.EventAPIClient{
   819  					centralRegion: client,
   820  				}
   821  
   822  				return details, []*automock.EventAPIClient{client}
   823  			},
   824  		},
   825  	}
   826  
   827  	for _, tc := range testCases {
   828  		t.Run(tc.name, func(t *testing.T) {
   829  			cfg := tc.jobConfigFn()
   830  
   831  			directorClient := tc.directorClientFn()
   832  			universalClient := tc.universalClientFn(cfg)
   833  			regionalDetails, clientMocks := tc.regionalDetailsFn(cfg)
   834  
   835  			defer func(t *testing.T) {
   836  				mock.AssertExpectationsForObjects(t, directorClient, universalClient)
   837  				for _, clientMock := range clientMocks {
   838  					clientMock.AssertExpectations(t)
   839  				}
   840  			}(t)
   841  
   842  			manager, err := resync.NewTenantsManager(cfg, directorClient, universalClient, regionalDetails, tenantConverter)
   843  			require.NoError(t, err)
   844  
   845  			actualTenant, err := manager.FetchTenant(ctx, busTenant.ExternalTenant)
   846  			if len(tc.expectedErrMsg) > 0 {
   847  				require.Error(t, err)
   848  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   849  			} else {
   850  				require.NoError(t, err)
   851  				require.Equal(t, tc.expectedTenant, actualTenant)
   852  			}
   853  		})
   854  	}
   855  }
   856  
   857  func configWithRegionsForSubaccounts(regions ...string) resync.JobConfig {
   858  	cfg := configForTenantType(tenant.Subaccount)
   859  	regionalCfgs := make(map[string]*resync.EventsAPIConfig)
   860  	for _, r := range regions {
   861  		regionalCfgs[r] = &resync.EventsAPIConfig{
   862  			RegionName: r,
   863  		}
   864  	}
   865  	cfg.RegionalAPIConfigs = regionalCfgs
   866  	return cfg
   867  }
   868  
   869  func configForTenantType(tenantType tenant.Type) resync.JobConfig {
   870  	centralRegionCfg := &resync.EventsAPIConfig{
   871  		APIEndpointsConfig: resync.APIEndpointsConfig{},
   872  		TenantFieldMapping: resync.TenantFieldMapping{
   873  			EventsField:            "events",
   874  			NameField:              "name",
   875  			IDField:                "id",
   876  			GlobalAccountGUIDField: "globalAccountGUID",
   877  			SubaccountIDField:      "subaccountId",
   878  			CustomerIDField:        "customerId",
   879  			SubdomainField:         "subdomain",
   880  			DetailsField:           "eventData",
   881  			EntityIDField:          "entityId",
   882  			EntityTypeField:        "type",
   883  			RegionField:            "region",
   884  			LicenseTypeField:       "licenseType",
   885  		},
   886  		MovedSubaccountsFieldMapping: resync.MovedSubaccountsFieldMapping{
   887  			SubaccountID: "subaccountId",
   888  			SourceTenant: "source",
   889  			TargetTenant: "target",
   890  		},
   891  		OAuthConfig:   resync.OAuth2Config{},
   892  		ClientTimeout: 0,
   893  		RegionName:    centralRegion,
   894  	}
   895  	return resync.JobConfig{
   896  		EventsConfig: resync.EventsConfig{
   897  			QueryConfig: resync.QueryConfig{
   898  				PageNumField:   "pageNum",
   899  				PageSizeField:  "pageSize",
   900  				TimestampField: "timestamp",
   901  				PageSizeValue:  "1",
   902  				PageStartValue: "1",
   903  				EntityField:    "entityId",
   904  			},
   905  			PagingConfig: resync.PagingConfig{
   906  				TotalPagesField:   "pages",
   907  				TotalResultsField: "total",
   908  			},
   909  			APIConfig: *centralRegionCfg,
   910  			RegionalAPIConfigs: map[string]*resync.EventsAPIConfig{
   911  				centralRegion: centralRegionCfg,
   912  			},
   913  			TenantOperationChunkSize: 500,
   914  			RetryAttempts:            1,
   915  			PageWorkers:              2,
   916  		},
   917  		ResyncConfig:   resync.ResyncConfig{},
   918  		KubeConfig:     resync.KubeConfig{},
   919  		JobName:        "tenant-fetcher",
   920  		TenantProvider: provider,
   921  		TenantType:     tenantType,
   922  	}
   923  }
   924  
   925  func eventFieldsFromTenant(tenantType tenant.Type, tenantFieldMapping resync.TenantFieldMapping, tenantInput model.BusinessTenantMappingInput) map[string]string {
   926  	fields := map[string]string{
   927  		tenantFieldMapping.IDField:        tenantInput.ExternalTenant,
   928  		tenantFieldMapping.NameField:      tenantInput.Name,
   929  		tenantFieldMapping.SubdomainField: tenantInput.Subdomain,
   930  		tenantFieldMapping.RegionField:    tenantInput.Region,
   931  	}
   932  	if tenantInput.LicenseType != nil {
   933  		fields[tenantFieldMapping.LicenseTypeField] = *tenantInput.LicenseType
   934  	}
   935  
   936  	switch tenantType {
   937  	case tenant.Account:
   938  		fields[tenantFieldMapping.EntityTypeField] = "GlobalAccount"
   939  		fields[tenantFieldMapping.GlobalAccountGUIDField] = tenantInput.ExternalTenant
   940  		fields[tenantFieldMapping.CustomerIDField] = tenantInput.Parent
   941  	case tenant.Subaccount:
   942  		fields[tenantFieldMapping.EntityTypeField] = "Subaccount"
   943  		fields[tenantFieldMapping.SubaccountIDField] = tenantInput.ExternalTenant
   944  		fields[tenantFieldMapping.GlobalAccountGUIDField] = tenantInput.Parent
   945  	}
   946  	return fields
   947  }
   948  
   949  func matchArrayWithoutOrderArgument(expected []graphql.BusinessTenantMappingInput) interface{} {
   950  	return mock.MatchedBy(func(actual []graphql.BusinessTenantMappingInput) bool {
   951  		if len(expected) != len(actual) {
   952  			return false
   953  		}
   954  		matched := make([]bool, len(actual))
   955  		for i := 0; i < len(matched); i++ {
   956  			matched[i] = false
   957  		}
   958  		for i := 0; i < len(expected); i++ {
   959  			for j := 0; j < len(actual); j++ {
   960  				if assert.ObjectsAreEqual(expected[i], actual[j]) {
   961  					matched[j] = true
   962  				}
   963  			}
   964  		}
   965  		for i := 0; i < len(matched); i++ {
   966  			if matched[i] {
   967  				continue
   968  			}
   969  			return false
   970  		}
   971  		return true
   972  	})
   973  }