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

     1  package resync_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  
     8  	domaintenant "github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
     9  	"github.com/kyma-incubator/compass/components/director/internal/labelfilter"
    10  	"github.com/kyma-incubator/compass/components/director/internal/model"
    11  	"github.com/kyma-incubator/compass/components/director/internal/tenantfetchersvc/resync"
    12  	"github.com/kyma-incubator/compass/components/director/internal/tenantfetchersvc/resync/automock"
    13  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    14  	persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock"
    15  	"github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest"
    16  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    17  	"github.com/kyma-incubator/compass/components/director/pkg/tenant"
    18  	"github.com/stretchr/testify/mock"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestTenantMover_TenantsToMove(t *testing.T) {
    23  	const (
    24  		timestamp = "1234567899987"
    25  
    26  		sourceParentTenantID = "d65e725e-f9a2-4a2a-b349-6487ab084ebc"
    27  		targetParentTenantID = "7fb04de7-b397-4b86-98f5-f3af8ac1da0c"
    28  	)
    29  
    30  	ctx := context.TODO()
    31  	testLicenseType := "LICENSETYPE"
    32  	tenantConverter := domaintenant.NewConverter()
    33  	jobConfig := configForTenantType(tenant.Subaccount)
    34  	jobConfig.EventsConfig.QueryConfig.RegionField = "region"
    35  
    36  	movedSubaccount1 := model.MovedSubaccountMappingInput{
    37  		TenantMappingInput: fixBusinessTenantMappingInput("1", provider, "subdomain-1", "", sourceParentTenantID, tenant.Subaccount, &testLicenseType),
    38  		SubaccountID:       "1",
    39  		SourceTenant:       sourceParentTenantID,
    40  		TargetTenant:       targetParentTenantID,
    41  	}
    42  	movedSubaccount2 := model.MovedSubaccountMappingInput{
    43  		TenantMappingInput: fixBusinessTenantMappingInput("2", provider, "subdomain-2", "", sourceParentTenantID, tenant.Subaccount, nil),
    44  		SubaccountID:       "2",
    45  		SourceTenant:       sourceParentTenantID,
    46  		TargetTenant:       targetParentTenantID,
    47  	}
    48  
    49  	event1 := fixEvent(t, "Subaccount", movedSubaccount1.TenantMappingInput.Parent, movedEventFieldsFromTenant(jobConfig.APIConfig.TenantFieldMapping, jobConfig.APIConfig.MovedSubaccountsFieldMapping, movedSubaccount1))
    50  	event2 := fixEvent(t, "Subaccount", movedSubaccount2.TenantMappingInput.Parent, movedEventFieldsFromTenant(jobConfig.APIConfig.TenantFieldMapping, jobConfig.APIConfig.MovedSubaccountsFieldMapping, movedSubaccount2))
    51  
    52  	pageOneQueryParams := resync.QueryParams{
    53  		jobConfig.PageSizeField:                        "1",
    54  		jobConfig.PageNumField:                         "1",
    55  		jobConfig.TimestampField:                       timestamp,
    56  		jobConfig.EventsConfig.QueryConfig.RegionField: region,
    57  	}
    58  
    59  	testCases := []struct {
    60  		name               string
    61  		directorClientFn   func() *automock.DirectorGraphQLClient
    62  		apiClientFn        func(resync.JobConfig) *automock.EventAPIClient
    63  		runtimeSvcFn       func() *automock.RuntimeService
    64  		labelRepoFn        func() *automock.LabelRepo
    65  		tenantStorageSvcFn func() *automock.TenantStorageService
    66  		expectedTenants    []model.MovedSubaccountMappingInput
    67  		expectedErrMsg     string
    68  	}{
    69  		{
    70  			name:             "Success when only one page is returned for moved tenants events",
    71  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
    72  			apiClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
    73  				client := &automock.EventAPIClient{}
    74  				client.On("FetchTenantEventsPage", ctx, resync.MovedSubaccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 1, 1, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
    75  				return client
    76  			},
    77  			expectedTenants: []model.MovedSubaccountMappingInput{movedSubaccount1},
    78  		},
    79  		{
    80  			name:             "Success when two pages are returned for moved tenants events",
    81  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
    82  			apiClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
    83  				pageTwoQueryParams := resync.QueryParams{
    84  					"pageSize":  "1",
    85  					"pageNum":   "2",
    86  					"region":    region,
    87  					"timestamp": timestamp,
    88  				}
    89  
    90  				client := &automock.EventAPIClient{}
    91  				client.On("FetchTenantEventsPage", ctx, resync.MovedSubaccountType, pageOneQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event1), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
    92  				client.On("FetchTenantEventsPage", ctx, resync.MovedSubaccountType, pageTwoQueryParams).Return(fixTenantEventsResponse(eventsToJSONArray(event2), 2, 2, cfg.APIConfig.TenantFieldMapping, cfg.APIConfig.MovedSubaccountsFieldMapping, cfg.TenantProvider), nil).Once()
    93  
    94  				return client
    95  			},
    96  			expectedTenants: []model.MovedSubaccountMappingInput{movedSubaccount1, movedSubaccount2},
    97  		},
    98  		{
    99  			name:             "Fail when fetching moved tenants events returns an error",
   100  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   101  			apiClientFn: func(cfg resync.JobConfig) *automock.EventAPIClient {
   102  				client := &automock.EventAPIClient{}
   103  				client.On("FetchTenantEventsPage", ctx, resync.MovedSubaccountType, pageOneQueryParams).Return(nil, errors.New("failed to get moved")).Once()
   104  				return client
   105  			},
   106  			expectedErrMsg: "while fetching moved tenants",
   107  		},
   108  	}
   109  
   110  	for _, tc := range testCases {
   111  		t.Run(tc.name, func(t *testing.T) {
   112  			directorClient := tc.directorClientFn()
   113  			eventAPIClient := tc.apiClientFn(jobConfig)
   114  			storageSvc := &automock.TenantStorageService{}
   115  			runtimeSvc := &automock.RuntimeService{}
   116  			labelRepo := &automock.LabelRepo{}
   117  			txGen := txtest.NewTransactionContextGenerator(nil)
   118  			persist, transact := txGen.ThatDoesntStartTransaction()
   119  			defer mock.AssertExpectationsForObjects(t, directorClient, eventAPIClient, storageSvc, runtimeSvc, labelRepo, persist, transact)
   120  
   121  			mover := resync.NewSubaccountsMover(jobConfig, transact, directorClient, eventAPIClient, tenantConverter, storageSvc, runtimeSvc, labelRepo)
   122  			res, err := mover.TenantsToMove(ctx, region, timestamp)
   123  			if len(tc.expectedErrMsg) > 0 {
   124  				require.Error(t, err)
   125  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   126  			} else {
   127  				require.NoError(t, err)
   128  				require.EqualValues(t, tc.expectedTenants, res)
   129  			}
   130  		})
   131  	}
   132  }
   133  
   134  func TestTenantMover_MoveTenants(t *testing.T) {
   135  	const (
   136  		provider = "external-service"
   137  
   138  		subaccountExternalTenant = "3daf8389-7f3e-41f7-94d9-75576ac80bee"
   139  		subaccountInternalTenant = "f384fa42-41f2-4490-b9b7-95d8ee03f785"
   140  
   141  		sourceParentTenantID = "d65e725e-f9a2-4a2a-b349-6487ab084ebc"
   142  		targetParentTenantID = "7fb04de7-b397-4b86-98f5-f3af8ac1da0c"
   143  	)
   144  
   145  	ctx := context.TODO()
   146  	txGen := txtest.NewTransactionContextGenerator(errors.New("test err"))
   147  
   148  	// GIVEN
   149  	tenantConverter := domaintenant.NewConverter()
   150  
   151  	var subaccountFromDB *model.BusinessTenantMapping
   152  	init := func() {
   153  		subaccountFromDB = &model.BusinessTenantMapping{
   154  			ID:             subaccountInternalTenant,
   155  			Name:           subaccountExternalTenant,
   156  			ExternalTenant: subaccountExternalTenant,
   157  			Parent:         sourceParentTenantID,
   158  			Type:           tenant.Subaccount,
   159  			Provider:       provider,
   160  		}
   161  	}
   162  
   163  	targetParent := &model.BusinessTenantMapping{
   164  		ID:             "05a2a8ed-66b9-4978-86b9-5167fb43520f",
   165  		ExternalTenant: targetParentTenantID,
   166  	}
   167  	sourceParent := &model.BusinessTenantMapping{
   168  		ID:             "83711a81-4b07-4b21-b1b2-042135800039",
   169  		ExternalTenant: sourceParentTenantID,
   170  	}
   171  
   172  	movedSubaccountInput := graphql.BusinessTenantMappingInput{
   173  		Name:           subaccountExternalTenant,
   174  		ExternalTenant: subaccountExternalTenant,
   175  		Parent:         str.Ptr(targetParent.ID),
   176  		Subdomain:      str.Ptr(""),
   177  		Region:         str.Ptr(""),
   178  		Type:           string(tenant.Subaccount),
   179  		Provider:       provider,
   180  	}
   181  
   182  	movedSubaccount1 := model.MovedSubaccountMappingInput{
   183  		TenantMappingInput: fixBusinessTenantMappingInput(subaccountExternalTenant, provider, "", "", sourceParentTenantID, tenant.Subaccount, nil),
   184  		SubaccountID:       subaccountExternalTenant,
   185  		SourceTenant:       sourceParentTenantID,
   186  		TargetTenant:       targetParentTenantID,
   187  	}
   188  
   189  	ctxWithSubaccountMatcher := mock.MatchedBy(func(ctx context.Context) bool {
   190  		tenantID, err := domaintenant.LoadFromContext(ctx)
   191  		require.NoError(t, err)
   192  		eq := tenantID == subaccountInternalTenant
   193  		return eq
   194  	})
   195  
   196  	subaccountRuntime := &model.Runtime{
   197  		ID: "35ab420d-1c67-4d06-9567-dbba8f95ea95",
   198  	}
   199  
   200  	testCases := []struct {
   201  		name               string
   202  		jobConfigFn        func() resync.JobConfig
   203  		transactionerFn    func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner)
   204  		directorClientFn   func() *automock.DirectorGraphQLClient
   205  		runtimeSvcFn       func() *automock.RuntimeService
   206  		labelRepoFn        func() *automock.LabelRepo
   207  		tenantStorageSvcFn func([]model.MovedSubaccountMappingInput) *automock.TenantStorageService
   208  		tenantsInput       []model.MovedSubaccountMappingInput
   209  		expectedErrMsg     string
   210  	}{
   211  		{
   212  			name: "Success when subaccount is moved to an existing parent tenant",
   213  			jobConfigFn: func() resync.JobConfig {
   214  				return configForTenantType(tenant.Subaccount)
   215  			},
   216  			transactionerFn: txGen.ThatSucceeds,
   217  			directorClientFn: func() *automock.DirectorGraphQLClient {
   218  				client := &automock.DirectorGraphQLClient{}
   219  				client.On("UpdateTenant", txtest.CtxWithDBMatcher(), subaccountInternalTenant, movedSubaccountInput).Return(nil)
   220  				return client
   221  			},
   222  			runtimeSvcFn: func() *automock.RuntimeService {
   223  				svc := &automock.RuntimeService{}
   224  				var emptyFilters []*labelfilter.LabelFilter
   225  
   226  				svc.On("ListByFilters", ctxWithSubaccountMatcher, emptyFilters).Return(nil, nil).Once()
   227  				return svc
   228  			},
   229  			labelRepoFn: func() *automock.LabelRepo { return &automock.LabelRepo{} },
   230  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   231  				svc := &automock.TenantStorageService{}
   232  				tnt := input[0]
   233  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   234  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{subaccountFromDB}, nil).Once()
   235  				return svc
   236  			},
   237  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   238  		},
   239  		{
   240  			name: "Success when subaccount is created in the correct parent tenant",
   241  			jobConfigFn: func() resync.JobConfig {
   242  				return configForTenantType(tenant.Subaccount)
   243  			},
   244  			transactionerFn: txGen.ThatSucceeds,
   245  			directorClientFn: func() *automock.DirectorGraphQLClient {
   246  				client := &automock.DirectorGraphQLClient{}
   247  
   248  				client.On("WriteTenants", txtest.CtxWithDBMatcher(), []graphql.BusinessTenantMappingInput{movedSubaccountInput}).Return(nil).Once()
   249  				return client
   250  			},
   251  			runtimeSvcFn: func() *automock.RuntimeService { return &automock.RuntimeService{} },
   252  			labelRepoFn:  func() *automock.LabelRepo { return &automock.LabelRepo{} },
   253  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   254  				svc := &automock.TenantStorageService{}
   255  				tnt := input[0]
   256  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   257  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{}, nil).Once()
   258  				return svc
   259  			},
   260  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   261  		},
   262  		{
   263  			name: "Success when subaccount is not in a formation",
   264  			jobConfigFn: func() resync.JobConfig {
   265  				return configForTenantType(tenant.Subaccount)
   266  			},
   267  			transactionerFn: txGen.ThatSucceeds,
   268  			directorClientFn: func() *automock.DirectorGraphQLClient {
   269  				client := &automock.DirectorGraphQLClient{}
   270  				client.On("UpdateTenant", txtest.CtxWithDBMatcher(), subaccountInternalTenant, movedSubaccountInput).Return(nil)
   271  				return client
   272  			},
   273  			runtimeSvcFn: func() *automock.RuntimeService {
   274  				svc := &automock.RuntimeService{}
   275  				var emptyFilters []*labelfilter.LabelFilter
   276  
   277  				svc.On("ListByFilters", ctxWithSubaccountMatcher, emptyFilters).Return([]*model.Runtime{subaccountRuntime}, nil).Once()
   278  				return svc
   279  			},
   280  			labelRepoFn: func() *automock.LabelRepo {
   281  				repo := &automock.LabelRepo{}
   282  				repo.On("GetScenarioLabelsForRuntimes", txtest.CtxWithDBMatcher(), sourceParent.ID, []string{subaccountRuntime.ID}).Return(nil, nil).Once()
   283  				return repo
   284  			},
   285  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   286  				svc := &automock.TenantStorageService{}
   287  				tnt := input[0]
   288  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   289  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{subaccountFromDB}, nil).Once()
   290  				svc.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), sourceParentTenantID).Return(sourceParent, nil).Once()
   291  				return svc
   292  			},
   293  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   294  		},
   295  		{
   296  			name: "Success when subaccount is in the default formation",
   297  			jobConfigFn: func() resync.JobConfig {
   298  				return configForTenantType(tenant.Subaccount)
   299  			},
   300  			transactionerFn: txGen.ThatSucceeds,
   301  			directorClientFn: func() *automock.DirectorGraphQLClient {
   302  				client := &automock.DirectorGraphQLClient{}
   303  				client.On("UpdateTenant", txtest.CtxWithDBMatcher(), subaccountInternalTenant, movedSubaccountInput).Return(nil)
   304  				return client
   305  			},
   306  			runtimeSvcFn: func() *automock.RuntimeService {
   307  				svc := &automock.RuntimeService{}
   308  				var emptyFilters []*labelfilter.LabelFilter
   309  
   310  				svc.On("ListByFilters", ctxWithSubaccountMatcher, emptyFilters).Return([]*model.Runtime{subaccountRuntime}, nil).Once()
   311  				return svc
   312  			},
   313  			labelRepoFn: func() *automock.LabelRepo {
   314  				repo := &automock.LabelRepo{}
   315  				defaultLabel := []model.Label{{Value: []interface{}{"DEFAULT"}}}
   316  				repo.On("GetScenarioLabelsForRuntimes", txtest.CtxWithDBMatcher(), sourceParent.ID, []string{subaccountRuntime.ID}).Return(defaultLabel, nil).Once()
   317  				return repo
   318  			},
   319  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   320  				svc := &automock.TenantStorageService{}
   321  				tnt := input[0]
   322  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   323  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{subaccountFromDB}, nil).Once()
   324  				svc.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), sourceParentTenantID).Return(sourceParent, nil).Once()
   325  				return svc
   326  			},
   327  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   328  		},
   329  		{
   330  			name: "Success when target parent tenant does not exist: tenant is skipped",
   331  			jobConfigFn: func() resync.JobConfig {
   332  				return configForTenantType(tenant.Subaccount)
   333  			},
   334  			transactionerFn:  txGen.ThatSucceeds,
   335  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   336  			runtimeSvcFn:     func() *automock.RuntimeService { return &automock.RuntimeService{} },
   337  			labelRepoFn:      func() *automock.LabelRepo { return &automock.LabelRepo{} },
   338  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   339  				svc := &automock.TenantStorageService{}
   340  				tnt := input[0]
   341  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{}, nil).Once()
   342  				return svc
   343  			},
   344  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   345  		},
   346  		{
   347  			name: "Success when subaccount is already moved",
   348  			jobConfigFn: func() resync.JobConfig {
   349  				return configForTenantType(tenant.Subaccount)
   350  			},
   351  			transactionerFn:  txGen.ThatSucceeds,
   352  			directorClientFn: func() *automock.DirectorGraphQLClient { return &automock.DirectorGraphQLClient{} },
   353  			runtimeSvcFn:     func() *automock.RuntimeService { return &automock.RuntimeService{} },
   354  			labelRepoFn:      func() *automock.LabelRepo { return &automock.LabelRepo{} },
   355  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   356  				svc := &automock.TenantStorageService{}
   357  				tnt := input[0]
   358  				subaccountFromDBWithNewParent := *subaccountFromDB
   359  				subaccountFromDBWithNewParent.Parent = targetParent.ID
   360  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   361  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{&subaccountFromDBWithNewParent}, nil).Once()
   362  				return svc
   363  			},
   364  			tenantsInput: []model.MovedSubaccountMappingInput{movedSubaccount1},
   365  		},
   366  		{
   367  			name: "Fail when subaccount is in formation",
   368  			jobConfigFn: func() resync.JobConfig {
   369  				return configForTenantType(tenant.Subaccount)
   370  			},
   371  			transactionerFn: txGen.ThatDoesntExpectCommit,
   372  			directorClientFn: func() *automock.DirectorGraphQLClient {
   373  				client := &automock.DirectorGraphQLClient{}
   374  				//client.On("UpdateTenant", txtest.CtxWithDBMatcher(), subaccountInternalTenant, movedSubaccountInput).Return(nil)
   375  				return client
   376  			},
   377  			runtimeSvcFn: func() *automock.RuntimeService {
   378  				svc := &automock.RuntimeService{}
   379  				var emptyFilters []*labelfilter.LabelFilter
   380  
   381  				svc.On("ListByFilters", ctxWithSubaccountMatcher, emptyFilters).Return([]*model.Runtime{subaccountRuntime}, nil).Once()
   382  				return svc
   383  			},
   384  			labelRepoFn: func() *automock.LabelRepo {
   385  				repo := &automock.LabelRepo{}
   386  				label := []model.Label{{Value: []interface{}{"my-scenario"}}}
   387  				repo.On("GetScenarioLabelsForRuntimes", txtest.CtxWithDBMatcher(), sourceParent.ID, []string{subaccountRuntime.ID}).Return(label, nil).Once()
   388  				return repo
   389  			},
   390  			tenantStorageSvcFn: func(input []model.MovedSubaccountMappingInput) *automock.TenantStorageService {
   391  				svc := &automock.TenantStorageService{}
   392  				tnt := input[0]
   393  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.TargetTenant}).Return([]*model.BusinessTenantMapping{targetParent}, nil).Once()
   394  				svc.On("ListsByExternalIDs", txtest.CtxWithDBMatcher(), []string{tnt.SubaccountID}).Return([]*model.BusinessTenantMapping{subaccountFromDB}, nil).Once()
   395  				svc.On("GetTenantByExternalID", txtest.CtxWithDBMatcher(), sourceParentTenantID).Return(sourceParent, nil).Once()
   396  				return svc
   397  			},
   398  			tenantsInput:   []model.MovedSubaccountMappingInput{movedSubaccount1},
   399  			expectedErrMsg: "is in scenario",
   400  		},
   401  	}
   402  
   403  	for _, tc := range testCases {
   404  		t.Run(tc.name, func(t *testing.T) {
   405  			cfg := tc.jobConfigFn()
   406  			init()
   407  			eventAPIClient := &automock.EventAPIClient{}
   408  			persist, transact := tc.transactionerFn()
   409  			directorClient := tc.directorClientFn()
   410  			storageSvc := tc.tenantStorageSvcFn(tc.tenantsInput)
   411  			runtimeSvc := tc.runtimeSvcFn()
   412  			labelRepo := tc.labelRepoFn()
   413  			defer mock.AssertExpectationsForObjects(t, persist, transact, directorClient, eventAPIClient, storageSvc, runtimeSvc, labelRepo)
   414  
   415  			mover := resync.NewSubaccountsMover(cfg, transact, directorClient, eventAPIClient, tenantConverter, storageSvc, runtimeSvc, labelRepo)
   416  			err := mover.MoveTenants(ctx, tc.tenantsInput)
   417  			if len(tc.expectedErrMsg) > 0 {
   418  				require.Error(t, err)
   419  				require.Contains(t, err.Error(), tc.expectedErrMsg)
   420  			} else {
   421  				require.NoError(t, err)
   422  			}
   423  		})
   424  	}
   425  }
   426  
   427  func movedEventFieldsFromTenant(tenantFieldMapping resync.TenantFieldMapping, movedSAFieldMapping resync.MovedSubaccountsFieldMapping, tenantInput model.MovedSubaccountMappingInput) map[string]string {
   428  	eventFields := eventFieldsFromTenant(tenant.Subaccount, tenantFieldMapping, tenantInput.TenantMappingInput)
   429  	eventFields[movedSAFieldMapping.SourceTenant] = tenantInput.SourceTenant
   430  	eventFields[movedSAFieldMapping.TargetTenant] = tenantInput.TargetTenant
   431  	eventFields[movedSAFieldMapping.SubaccountID] = tenantInput.SubaccountID
   432  	return eventFields
   433  }