github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/tenant/service_test.go (about)

     1  package tenant_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  	"testing"
     9  
    10  	"github.com/jmoiron/sqlx"
    11  
    12  	"github.com/DATA-DOG/go-sqlmock"
    13  	"github.com/kyma-incubator/compass/components/director/internal/repo/testdb"
    14  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    15  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    16  
    17  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    18  
    19  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    20  	tenantEntity "github.com/kyma-incubator/compass/components/director/pkg/tenant"
    21  	"github.com/stretchr/testify/mock"
    22  
    23  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    24  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant/automock"
    25  	"github.com/kyma-incubator/compass/components/director/internal/model"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestService_GetExternalTenant(t *testing.T) {
    32  	// GIVEN
    33  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
    34  	tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
    35  
    36  	testCases := []struct {
    37  		Name                string
    38  		TenantMappingRepoFn func() *automock.TenantMappingRepository
    39  		ExpectedError       error
    40  		ExpectedOutput      string
    41  	}{
    42  		{
    43  			Name: "Success",
    44  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
    45  				tenantMappingRepo := &automock.TenantMappingRepository{}
    46  				tenantMappingRepo.On("Get", ctx, testID).Return(tenantMappingModel, nil).Once()
    47  				return tenantMappingRepo
    48  			},
    49  			ExpectedOutput: testExternal,
    50  		},
    51  		{
    52  			Name: "Error when getting the internal tenant",
    53  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
    54  				tenantMappingRepo := &automock.TenantMappingRepository{}
    55  				tenantMappingRepo.On("Get", ctx, testID).Return(nil, testError).Once()
    56  				return tenantMappingRepo
    57  			},
    58  			ExpectedError:  testError,
    59  			ExpectedOutput: "",
    60  		},
    61  	}
    62  
    63  	for _, testCase := range testCases {
    64  		t.Run(testCase.Name, func(t *testing.T) {
    65  			tenantMappingRepoFn := testCase.TenantMappingRepoFn()
    66  			svc := tenant.NewService(tenantMappingRepoFn, nil, nil)
    67  
    68  			// WHEN
    69  			result, err := svc.GetExternalTenant(ctx, testID)
    70  
    71  			// THEN
    72  			if testCase.ExpectedError != nil {
    73  				require.Error(t, err)
    74  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
    75  			} else {
    76  				assert.NoError(t, err)
    77  			}
    78  			assert.Equal(t, testCase.ExpectedOutput, result)
    79  
    80  			tenantMappingRepoFn.AssertExpectations(t)
    81  		})
    82  	}
    83  }
    84  
    85  func TestService_GetInternalTenant(t *testing.T) {
    86  	// GIVEN
    87  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
    88  	tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
    89  
    90  	testCases := []struct {
    91  		Name                string
    92  		TenantMappingRepoFn func() *automock.TenantMappingRepository
    93  		ExpectedError       error
    94  		ExpectedOutput      string
    95  	}{
    96  		{
    97  			Name: "Success",
    98  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
    99  				tenantMappingRepo := &automock.TenantMappingRepository{}
   100  				tenantMappingRepo.On("GetByExternalTenant", ctx, testExternal).Return(tenantMappingModel, nil).Once()
   101  				return tenantMappingRepo
   102  			},
   103  			ExpectedOutput: testID,
   104  		},
   105  		{
   106  			Name: "Error when getting the internal tenant",
   107  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   108  				tenantMappingRepo := &automock.TenantMappingRepository{}
   109  				tenantMappingRepo.On("GetByExternalTenant", ctx, testExternal).Return(nil, testError).Once()
   110  				return tenantMappingRepo
   111  			},
   112  			ExpectedError:  testError,
   113  			ExpectedOutput: "",
   114  		},
   115  	}
   116  
   117  	for _, testCase := range testCases {
   118  		t.Run(testCase.Name, func(t *testing.T) {
   119  			tenantMappingRepoFn := testCase.TenantMappingRepoFn()
   120  			svc := tenant.NewService(tenantMappingRepoFn, nil, nil)
   121  
   122  			// WHEN
   123  			result, err := svc.GetInternalTenant(ctx, testExternal)
   124  
   125  			// THEN
   126  			if testCase.ExpectedError != nil {
   127  				require.Error(t, err)
   128  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   129  			} else {
   130  				assert.NoError(t, err)
   131  			}
   132  			assert.Equal(t, testCase.ExpectedOutput, result)
   133  
   134  			tenantMappingRepoFn.AssertExpectations(t)
   135  		})
   136  	}
   137  }
   138  
   139  func TestService_ExtractTenantIDForTenantScopedFormationTemplates(t *testing.T) {
   140  	// GIVEN
   141  	ctx := tenant.SaveToContext(context.TODO(), testID, testExternal)
   142  	ctxWithEmptyTenants := tenant.SaveToContext(context.TODO(), "", "")
   143  
   144  	testCases := []struct {
   145  		Name                string
   146  		Context             context.Context
   147  		TenantMappingRepoFn func() *automock.TenantMappingRepository
   148  		ExpectedError       string
   149  		ExpectedOutput      string
   150  	}{
   151  		{
   152  			Name:    "Success when tenant is GA",
   153  			Context: ctx,
   154  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   155  				tenantMappingRepo := &automock.TenantMappingRepository{}
   156  				tenantMappingRepo.On("Get", ctx, testID).Return(newModelBusinessTenantMappingWithType(testID, testName, "", nil, tenantEntity.Account), nil).Once()
   157  				return tenantMappingRepo
   158  			},
   159  			ExpectedOutput: testID,
   160  		},
   161  		{
   162  			Name:    "Success when tenant is SA",
   163  			Context: ctx,
   164  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   165  				tenantMappingRepo := &automock.TenantMappingRepository{}
   166  				tenantMappingRepo.On("Get", ctx, testID).Return(newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Subaccount), nil).Once()
   167  				return tenantMappingRepo
   168  			},
   169  			ExpectedOutput: testParentID,
   170  		},
   171  		{
   172  			Name:    "Success when empty tenant",
   173  			Context: ctxWithEmptyTenants,
   174  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   175  				return &automock.TenantMappingRepository{}
   176  			},
   177  			ExpectedOutput: "",
   178  		},
   179  		{
   180  			Name:    "Error when getting the internal tenant",
   181  			Context: ctx,
   182  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   183  				tenantMappingRepo := &automock.TenantMappingRepository{}
   184  				tenantMappingRepo.On("Get", ctx, testID).Return(nil, testError).Once()
   185  				return tenantMappingRepo
   186  			},
   187  			ExpectedError:  testError.Error(),
   188  			ExpectedOutput: "",
   189  		},
   190  		{
   191  			Name:    "Error when tenant is not in context",
   192  			Context: context.TODO(),
   193  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   194  				return &automock.TenantMappingRepository{}
   195  			},
   196  			ExpectedError:  "cannot read tenant from context",
   197  			ExpectedOutput: "",
   198  		},
   199  		{
   200  			Name:    "Error when there is only internalID in context",
   201  			Context: tenant.SaveToContext(context.TODO(), testID, ""),
   202  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   203  				return &automock.TenantMappingRepository{}
   204  			},
   205  			ExpectedError:  apperrors.NewTenantNotFoundError("").Error(),
   206  			ExpectedOutput: "",
   207  		},
   208  		{
   209  			Name:    "Error when there is only externalID in context",
   210  			Context: tenant.SaveToContext(context.TODO(), "", testID),
   211  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   212  				return &automock.TenantMappingRepository{}
   213  			},
   214  			ExpectedError:  apperrors.NewTenantNotFoundError(testID).Error(),
   215  			ExpectedOutput: "",
   216  		},
   217  	}
   218  
   219  	for _, testCase := range testCases {
   220  		t.Run(testCase.Name, func(t *testing.T) {
   221  			tenantMappingRepoFn := testCase.TenantMappingRepoFn()
   222  			svc := tenant.NewService(tenantMappingRepoFn, nil, nil)
   223  
   224  			// WHEN
   225  			result, err := svc.ExtractTenantIDForTenantScopedFormationTemplates(testCase.Context)
   226  
   227  			// THEN
   228  			if len(testCase.ExpectedError) > 0 {
   229  				require.Error(t, err)
   230  				assert.Contains(t, err.Error(), testCase.ExpectedError)
   231  			} else {
   232  				assert.NoError(t, err)
   233  			}
   234  			assert.Equal(t, testCase.ExpectedOutput, result)
   235  
   236  			tenantMappingRepoFn.AssertExpectations(t)
   237  		})
   238  	}
   239  }
   240  
   241  func TestService_List(t *testing.T) {
   242  	// GIVEN
   243  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
   244  	modelTenantMappings := []*model.BusinessTenantMapping{
   245  		newModelBusinessTenantMapping("foo1", "bar1"),
   246  		newModelBusinessTenantMapping("foo2", "bar2"),
   247  	}
   248  
   249  	testCases := []struct {
   250  		Name                string
   251  		TenantMappingRepoFn func() *automock.TenantMappingRepository
   252  		ExpectedError       error
   253  		ExpectedOutput      []*model.BusinessTenantMapping
   254  	}{
   255  		{
   256  			Name: "Success",
   257  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   258  				tenantMappingRepo := &automock.TenantMappingRepository{}
   259  				tenantMappingRepo.On("List", ctx).Return(modelTenantMappings, nil).Once()
   260  				return tenantMappingRepo
   261  			},
   262  			ExpectedOutput: modelTenantMappings,
   263  		},
   264  		{
   265  			Name: "Error when listing tenants",
   266  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   267  				tenantMappingRepo := &automock.TenantMappingRepository{}
   268  				tenantMappingRepo.On("List", ctx).Return([]*model.BusinessTenantMapping{}, testError).Once()
   269  				return tenantMappingRepo
   270  			},
   271  			ExpectedError:  testError,
   272  			ExpectedOutput: []*model.BusinessTenantMapping{},
   273  		},
   274  	}
   275  
   276  	for _, testCase := range testCases {
   277  		t.Run(testCase.Name, func(t *testing.T) {
   278  			tenantMappingRepo := testCase.TenantMappingRepoFn()
   279  			svc := tenant.NewService(tenantMappingRepo, nil, nil)
   280  
   281  			// WHEN
   282  			result, err := svc.List(ctx)
   283  
   284  			// THEN
   285  			if testCase.ExpectedError != nil {
   286  				require.Error(t, err)
   287  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   288  			} else {
   289  				assert.NoError(t, err)
   290  			}
   291  			assert.Equal(t, testCase.ExpectedOutput, result)
   292  
   293  			tenantMappingRepo.AssertExpectations(t)
   294  		})
   295  	}
   296  }
   297  
   298  func TestService_ListPageBySearchTerm(t *testing.T) {
   299  	// GIVEN
   300  	searchTerm := ""
   301  	first := 100
   302  	endCursor := ""
   303  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
   304  	modelTenantMappingPage := &model.BusinessTenantMappingPage{
   305  		Data: []*model.BusinessTenantMapping{
   306  			newModelBusinessTenantMapping("foo1", "bar1"),
   307  			newModelBusinessTenantMapping("foo2", "bar2"),
   308  		},
   309  		PageInfo: &pagination.Page{
   310  			StartCursor: "",
   311  			EndCursor:   "",
   312  			HasNextPage: false,
   313  		},
   314  		TotalCount: 2,
   315  	}
   316  
   317  	testCases := []struct {
   318  		Name                string
   319  		TenantMappingRepoFn func() *automock.TenantMappingRepository
   320  		ExpectedError       error
   321  		ExpectedOutput      *model.BusinessTenantMappingPage
   322  	}{
   323  		{
   324  			Name: "Success",
   325  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   326  				tenantMappingRepo := &automock.TenantMappingRepository{}
   327  				tenantMappingRepo.On("ListPageBySearchTerm", ctx, searchTerm, first, endCursor).Return(modelTenantMappingPage, nil).Once()
   328  				return tenantMappingRepo
   329  			},
   330  			ExpectedOutput: modelTenantMappingPage,
   331  		},
   332  		{
   333  			Name: "Error when listing tenants",
   334  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   335  				tenantMappingRepo := &automock.TenantMappingRepository{}
   336  				tenantMappingRepo.On("ListPageBySearchTerm", ctx, searchTerm, first, endCursor).Return(&model.BusinessTenantMappingPage{}, testError).Once()
   337  				return tenantMappingRepo
   338  			},
   339  			ExpectedError:  testError,
   340  			ExpectedOutput: &model.BusinessTenantMappingPage{},
   341  		},
   342  	}
   343  
   344  	for _, testCase := range testCases {
   345  		t.Run(testCase.Name, func(t *testing.T) {
   346  			tenantMappingRepo := testCase.TenantMappingRepoFn()
   347  			svc := tenant.NewService(tenantMappingRepo, nil, nil)
   348  
   349  			// WHEN
   350  			result, err := svc.ListPageBySearchTerm(ctx, searchTerm, first, endCursor)
   351  
   352  			// THEN
   353  			if testCase.ExpectedError != nil {
   354  				require.Error(t, err)
   355  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   356  			} else {
   357  				assert.NoError(t, err)
   358  			}
   359  			assert.Equal(t, testCase.ExpectedOutput, result)
   360  
   361  			tenantMappingRepo.AssertExpectations(t)
   362  		})
   363  	}
   364  }
   365  
   366  func TestService_DeleteMany(t *testing.T) {
   367  	// GIVEN
   368  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
   369  	tenantInput := newModelBusinessTenantMappingInput(testName, "", "", nil)
   370  	testErr := errors.New("test")
   371  	testCases := []struct {
   372  		Name                string
   373  		TenantMappingRepoFn func() *automock.TenantMappingRepository
   374  		ExpectedOutput      error
   375  	}{
   376  		{
   377  			Name: "Success",
   378  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   379  				tenantMappingRepo := &automock.TenantMappingRepository{}
   380  				tenantMappingRepo.On("DeleteByExternalTenant", ctx, tenantInput.ExternalTenant).Return(nil).Once()
   381  				return tenantMappingRepo
   382  			},
   383  			ExpectedOutput: nil,
   384  		},
   385  		{
   386  			Name: "Error while deleting the tenant mapping",
   387  			TenantMappingRepoFn: func() *automock.TenantMappingRepository {
   388  				tenantMappingRepo := &automock.TenantMappingRepository{}
   389  				tenantMappingRepo.On("DeleteByExternalTenant", ctx, tenantInput.ExternalTenant).Return(testErr).Once()
   390  				return tenantMappingRepo
   391  			},
   392  			ExpectedOutput: testErr,
   393  		},
   394  	}
   395  
   396  	for _, testCase := range testCases {
   397  		t.Run(testCase.Name, func(t *testing.T) {
   398  			tenantMappingRepo := testCase.TenantMappingRepoFn()
   399  			svc := tenant.NewService(tenantMappingRepo, nil, nil)
   400  
   401  			// WHEN
   402  			err := svc.DeleteMany(ctx, []string{tenantInput.ExternalTenant})
   403  
   404  			// THEN
   405  			if testCase.ExpectedOutput != nil {
   406  				require.Error(t, err)
   407  				assert.Contains(t, err.Error(), testCase.ExpectedOutput.Error())
   408  			} else {
   409  				assert.NoError(t, err)
   410  			}
   411  
   412  			tenantMappingRepo.AssertExpectations(t)
   413  		})
   414  	}
   415  }
   416  
   417  func TestService_CreateManyIfNotExists(t *testing.T) {
   418  	// GIVEN
   419  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
   420  
   421  	tenantInputs := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInput("test1", "", "", nil),
   422  		newModelBusinessTenantMappingInput("test2", "", "", nil).WithExternalTenant("external2")}
   423  	tenantInputsWithSubdomains := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInput("test1", testSubdomain, "", nil),
   424  		newModelBusinessTenantMappingInput("test2", "", "", nil).WithExternalTenant("external2")}
   425  	tenantInputsWithRegions := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInput("test1", "", testRegion, nil),
   426  		newModelBusinessTenantMappingInput("test2", "", testRegion, nil).WithExternalTenant("external2")}
   427  	tenantInputsWithLicenseType := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInput("test1", "", "", str.Ptr(testLicenseType)),
   428  		newModelBusinessTenantMappingInput("test2", "", "", str.Ptr(testLicenseType)).WithExternalTenant("external2")}
   429  	tenantModelInputsWithParent := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInputWithType(testID, "test1", testParentID, "", "", nil, tenantEntity.Account),
   430  		newModelBusinessTenantMappingInputWithType(testParentID, "test2", "", "", "", nil, tenantEntity.Customer)}
   431  	tenantWithSubdomainAndRegion := newModelBusinessTenantMappingInput("test1", testSubdomain, testRegion, nil)
   432  	tenantModelInputsWithParentOrganization := []model.BusinessTenantMappingInput{newModelBusinessTenantMappingInputWithType(testID, "test1", testParentID, "", "", nil, tenantEntity.Organization),
   433  		newModelBusinessTenantMappingInputWithType(testParentID, "test2", "", "", "", nil, tenantEntity.Folder)}
   434  
   435  	tenantModels := []model.BusinessTenantMapping{*newModelBusinessTenantMapping(testID, "test1"),
   436  		newModelBusinessTenantMapping(testID, "test2").WithExternalTenant("external2")}
   437  	tenantModelsWithLicense := []model.BusinessTenantMapping{*newModelBusinessTenantMappingWithLicense(testID, "test1", str.Ptr(testLicenseType)),
   438  		newModelBusinessTenantMappingWithLicense(testID, "test2", str.Ptr(testLicenseType)).WithExternalTenant("external2")}
   439  
   440  	expectedResult := []string{testID, testID}
   441  	uidSvcFn := func() *automock.UIDService {
   442  		uidSvc := &automock.UIDService{}
   443  		uidSvc.On("Generate").Return(testID)
   444  		return uidSvc
   445  	}
   446  	noopLabelRepo := func() *automock.LabelRepository {
   447  		return &automock.LabelRepository{}
   448  	}
   449  	noopLabelUpsertSvc := func() *automock.LabelUpsertService {
   450  		return &automock.LabelUpsertService{}
   451  	}
   452  	testErr := errors.New("test")
   453  	type testCase struct {
   454  		Name                string
   455  		tenantInputs        []model.BusinessTenantMappingInput
   456  		TenantMappingRepoFn func(string) *automock.TenantMappingRepository
   457  		LabelRepoFn         func() *automock.LabelRepository
   458  		LabelUpsertSvcFn    func() *automock.LabelUpsertService
   459  		UIDSvcFn            func() *automock.UIDService
   460  		ExpectedError       error
   461  		ExpectedResult      []string
   462  	}
   463  
   464  	testCases := []testCase{
   465  		{
   466  			Name:         "Success",
   467  			tenantInputs: tenantInputs,
   468  			TenantMappingRepoFn: func(createRepoFunc string) *automock.TenantMappingRepository {
   469  				return createRepoSvc(ctx, createRepoFunc, tenantModels[0], tenantModels[1])
   470  			},
   471  			UIDSvcFn:         uidSvcFn,
   472  			LabelRepoFn:      noopLabelRepo,
   473  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   474  			ExpectedError:    nil,
   475  			ExpectedResult:   expectedResult,
   476  		},
   477  		{
   478  			Name:         "Success when parent tenant exists with another ID",
   479  			tenantInputs: tenantModelInputsWithParent,
   480  			TenantMappingRepoFn: func(createFunc string) *automock.TenantMappingRepository {
   481  				parent := tenantModelInputsWithParent[1]
   482  				modifiedTenant := tenantModelInputsWithParent[0]
   483  				modifiedTenant.Parent = testInternalParentID
   484  
   485  				tenantMappingRepo := &automock.TenantMappingRepository{}
   486  				tenantMappingRepo.On(createFunc, ctx, *parent.ToBusinessTenantMapping(testTemporaryInternalParentID)).Return(nil).Once()
   487  				tenantMappingRepo.On("GetByExternalTenant", ctx, parent.ExternalTenant).Return(parent.ToBusinessTenantMapping(testInternalParentID), nil).Once()
   488  				tenantMappingRepo.On(createFunc, ctx, *modifiedTenant.ToBusinessTenantMapping(testID)).Return(nil).Once()
   489  				tenantMappingRepo.On("GetByExternalTenant", ctx, modifiedTenant.ExternalTenant).Return(modifiedTenant.ToBusinessTenantMapping(testID), nil).Once()
   490  				return tenantMappingRepo
   491  			},
   492  			UIDSvcFn: func() *automock.UIDService {
   493  				uidSvc := &automock.UIDService{}
   494  				uidSvc.On("Generate").Return(testID).Once()
   495  				uidSvc.On("Generate").Return(testTemporaryInternalParentID).Once()
   496  				return uidSvc
   497  			},
   498  			LabelRepoFn:      noopLabelRepo,
   499  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   500  			ExpectedError:    nil,
   501  			ExpectedResult:   []string{testInternalParentID, testID},
   502  		},
   503  		{
   504  			Name:         "Success when parent tenant organization exists with another ID",
   505  			tenantInputs: tenantModelInputsWithParentOrganization,
   506  			TenantMappingRepoFn: func(createFunc string) *automock.TenantMappingRepository {
   507  				parent := tenantModelInputsWithParentOrganization[1]
   508  				modifiedTenant := tenantModelInputsWithParentOrganization[0]
   509  				modifiedTenant.Parent = testInternalParentID
   510  
   511  				tenantMappingRepo := &automock.TenantMappingRepository{}
   512  				tenantMappingRepo.On(createFunc, ctx, *parent.ToBusinessTenantMapping(testTemporaryInternalParentID)).Return(nil).Once()
   513  				tenantMappingRepo.On("GetByExternalTenant", ctx, parent.ExternalTenant).Return(parent.ToBusinessTenantMapping(testInternalParentID), nil).Once()
   514  				tenantMappingRepo.On(createFunc, ctx, *modifiedTenant.ToBusinessTenantMapping(testID)).Return(nil).Once()
   515  				tenantMappingRepo.On("GetByExternalTenant", ctx, modifiedTenant.ExternalTenant).Return(modifiedTenant.ToBusinessTenantMapping(testID), nil).Once()
   516  				return tenantMappingRepo
   517  			},
   518  			UIDSvcFn: func() *automock.UIDService {
   519  				uidSvc := &automock.UIDService{}
   520  				uidSvc.On("Generate").Return(testID).Once()
   521  				uidSvc.On("Generate").Return(testTemporaryInternalParentID).Once()
   522  				return uidSvc
   523  			},
   524  			LabelRepoFn:      noopLabelRepo,
   525  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   526  			ExpectedError:    nil,
   527  			ExpectedResult:   []string{testInternalParentID, testID},
   528  		},
   529  		{
   530  			Name:         "Success when subdomain should be added",
   531  			tenantInputs: tenantInputsWithSubdomains,
   532  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   533  				return createRepoSvc(ctx, createFuncName, *tenantInputsWithSubdomains[0].ToBusinessTenantMapping(testID), *tenantInputsWithSubdomains[1].ToBusinessTenantMapping(testID))
   534  			},
   535  			UIDSvcFn:    uidSvcFn,
   536  			LabelRepoFn: noopLabelRepo,
   537  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   538  				svc := &automock.LabelUpsertService{}
   539  				label := &model.LabelInput{
   540  					Key:        "subdomain",
   541  					Value:      testSubdomain,
   542  					ObjectID:   testID,
   543  					ObjectType: model.TenantLabelableObject,
   544  				}
   545  				svc.On("UpsertLabel", ctx, testID, label).Return(nil).Once()
   546  				return svc
   547  			},
   548  			ExpectedError:  nil,
   549  			ExpectedResult: expectedResult,
   550  		},
   551  		{
   552  			Name:         "Success when region should be added",
   553  			tenantInputs: tenantInputsWithRegions,
   554  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   555  				return createRepoSvc(ctx, createFuncName, *tenantInputsWithRegions[0].ToBusinessTenantMapping(testID), *tenantInputsWithRegions[1].ToBusinessTenantMapping(testID))
   556  			},
   557  			UIDSvcFn:    uidSvcFn,
   558  			LabelRepoFn: noopLabelRepo,
   559  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   560  				svc := &automock.LabelUpsertService{}
   561  				regionLabel := &model.LabelInput{
   562  					Key:        "region",
   563  					Value:      testRegion,
   564  					ObjectID:   tenantModels[1].ID,
   565  					ObjectType: model.TenantLabelableObject,
   566  				}
   567  				svc.On("UpsertLabel", ctx, testID, regionLabel).Return(nil).Twice()
   568  				return svc
   569  			},
   570  			ExpectedError:  nil,
   571  			ExpectedResult: expectedResult,
   572  		},
   573  		{
   574  			Name:         "Success when licenseType should be added",
   575  			tenantInputs: tenantInputsWithLicenseType,
   576  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   577  				return createRepoSvc(ctx, createFuncName, *tenantInputsWithLicenseType[0].ToBusinessTenantMapping(testID), *tenantInputsWithLicenseType[1].ToBusinessTenantMapping(testID))
   578  			},
   579  			UIDSvcFn:    uidSvcFn,
   580  			LabelRepoFn: noopLabelRepo,
   581  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   582  				svc := &automock.LabelUpsertService{}
   583  				label := &model.LabelInput{
   584  					Key:        "licensetype",
   585  					Value:      testLicenseType,
   586  					ObjectID:   tenantModelsWithLicense[1].ID,
   587  					ObjectType: model.TenantLabelableObject,
   588  				}
   589  				svc.On("UpsertLabel", ctx, testID, label).Return(nil).Twice()
   590  				return svc
   591  			},
   592  			ExpectedError:  nil,
   593  			ExpectedResult: expectedResult,
   594  		},
   595  		{
   596  			Name:         "Error when checking the existence of tenant",
   597  			tenantInputs: []model.BusinessTenantMappingInput{tenantWithSubdomainAndRegion},
   598  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   599  				tenantMappingRepo := &automock.TenantMappingRepository{}
   600  				tenantMappingRepo.On(createFuncName, ctx, *tenantWithSubdomainAndRegion.ToBusinessTenantMapping(testID)).Return(nil).Once()
   601  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantWithSubdomainAndRegion.ExternalTenant).Return(nil, testErr)
   602  				return tenantMappingRepo
   603  			},
   604  			UIDSvcFn:         uidSvcFn,
   605  			LabelRepoFn:      noopLabelRepo,
   606  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   607  			ExpectedError:    testErr,
   608  			ExpectedResult:   nil,
   609  		},
   610  		{
   611  			Name:         "Error when subdomain label setting fails",
   612  			tenantInputs: tenantInputsWithSubdomains,
   613  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   614  				tenantMappingRepo := &automock.TenantMappingRepository{}
   615  				tenantMappingRepo.On(createFuncName, ctx, tenantModels[0]).Return(nil).Once()
   616  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInputsWithSubdomains[0].ExternalTenant).Return(&tenantModels[0], nil)
   617  				return tenantMappingRepo
   618  			},
   619  			UIDSvcFn:    uidSvcFn,
   620  			LabelRepoFn: noopLabelRepo,
   621  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   622  				svc := &automock.LabelUpsertService{}
   623  				label := &model.LabelInput{
   624  					Key:        "subdomain",
   625  					Value:      testSubdomain,
   626  					ObjectID:   testID,
   627  					ObjectType: model.TenantLabelableObject,
   628  				}
   629  				svc.On("UpsertLabel", ctx, testID, label).Return(testErr).Once()
   630  				return svc
   631  			},
   632  			ExpectedError:  testErr,
   633  			ExpectedResult: nil,
   634  		},
   635  		{
   636  			Name:         "Error when region label setting fails",
   637  			tenantInputs: tenantInputsWithRegions,
   638  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   639  				tenantMappingRepo := &automock.TenantMappingRepository{}
   640  				tenantMappingRepo.On(createFuncName, ctx, tenantModels[0]).Return(nil).Once()
   641  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInputsWithRegions[0].ExternalTenant).Return(&tenantModels[0], nil)
   642  				return tenantMappingRepo
   643  			},
   644  			UIDSvcFn:    uidSvcFn,
   645  			LabelRepoFn: noopLabelRepo,
   646  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   647  				svc := &automock.LabelUpsertService{}
   648  				label := &model.LabelInput{
   649  					Key:        "region",
   650  					Value:      testRegion,
   651  					ObjectID:   testID,
   652  					ObjectType: model.TenantLabelableObject,
   653  				}
   654  				svc.On("UpsertLabel", ctx, testID, label).Return(testErr).Once()
   655  				return svc
   656  			},
   657  			ExpectedError:  testErr,
   658  			ExpectedResult: nil,
   659  		},
   660  		{
   661  			Name:         "Error when licenseType label setting fails",
   662  			tenantInputs: tenantInputsWithLicenseType,
   663  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   664  				tenantMappingRepo := &automock.TenantMappingRepository{}
   665  				tenantMappingRepo.On(createFuncName, ctx, tenantModelsWithLicense[0]).Return(nil).Once()
   666  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInputsWithRegions[0].ExternalTenant).Return(&tenantModelsWithLicense[0], nil)
   667  				return tenantMappingRepo
   668  			},
   669  			UIDSvcFn:    uidSvcFn,
   670  			LabelRepoFn: noopLabelRepo,
   671  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   672  				svc := &automock.LabelUpsertService{}
   673  				label := &model.LabelInput{
   674  					Key:        "licensetype",
   675  					Value:      testLicenseType,
   676  					ObjectID:   testID,
   677  					ObjectType: model.TenantLabelableObject,
   678  				}
   679  				svc.On("UpsertLabel", ctx, testID, label).Return(testErr).Once()
   680  				return svc
   681  			},
   682  			ExpectedError:  testErr,
   683  			ExpectedResult: nil,
   684  		},
   685  		{
   686  			Name:         "Error when creating the tenant",
   687  			tenantInputs: tenantInputs,
   688  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   689  				tenantMappingRepo := &automock.TenantMappingRepository{}
   690  				tenantMappingRepo.On(createFuncName, ctx, tenantModels[0]).Return(testErr).Once()
   691  				return tenantMappingRepo
   692  			},
   693  			UIDSvcFn:         uidSvcFn,
   694  			LabelRepoFn:      noopLabelRepo,
   695  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   696  			ExpectedError:    testErr,
   697  			ExpectedResult:   nil,
   698  		},
   699  	}
   700  
   701  	t.Run("CreateManyIfNotExists", func(t *testing.T) {
   702  		for _, testCase := range testCases {
   703  			t.Run(testCase.Name, func(t *testing.T) {
   704  				uidSvc := testCase.UIDSvcFn()
   705  				tenantMappingRepo := testCase.TenantMappingRepoFn("UnsafeCreate")
   706  				labelRepo := testCase.LabelRepoFn()
   707  				labelUpsertSvc := testCase.LabelUpsertSvcFn()
   708  				defer mock.AssertExpectationsForObjects(t, tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc)
   709  
   710  				svc := tenant.NewServiceWithLabels(tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
   711  
   712  				// WHEN
   713  				res, err := svc.CreateManyIfNotExists(ctx, testCase.tenantInputs...)
   714  
   715  				// THEN
   716  				if testCase.ExpectedError != nil {
   717  					require.Error(t, err)
   718  					assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   719  				} else {
   720  					assert.NoError(t, err)
   721  					assert.Equal(t, testCase.ExpectedResult, res)
   722  				}
   723  			})
   724  		}
   725  	})
   726  
   727  	t.Run("UpsertMany", func(t *testing.T) {
   728  		for _, testCase := range testCases {
   729  			t.Run(testCase.Name, func(t *testing.T) {
   730  				uidSvc := testCase.UIDSvcFn()
   731  				tenantMappingRepo := testCase.TenantMappingRepoFn("Upsert")
   732  				labelRepo := testCase.LabelRepoFn()
   733  				labelUpsertSvc := testCase.LabelUpsertSvcFn()
   734  				defer mock.AssertExpectationsForObjects(t, tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc)
   735  
   736  				svc := tenant.NewServiceWithLabels(tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
   737  
   738  				// WHEN
   739  				res, err := svc.UpsertMany(ctx, testCase.tenantInputs...)
   740  
   741  				// THEN
   742  				if testCase.ExpectedError != nil {
   743  					require.Error(t, err)
   744  					assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   745  				} else {
   746  					assert.NoError(t, err)
   747  					assert.Equal(t, testCase.ExpectedResult, res)
   748  				}
   749  			})
   750  		}
   751  	})
   752  }
   753  
   754  func Test_UpsertSingle(t *testing.T) {
   755  	ctx := tenant.SaveToContext(context.TODO(), "test", "external-test")
   756  
   757  	tenantInput := newModelBusinessTenantMappingInput("test1", "", "", nil)
   758  	tenantInputWithSubdomain := newModelBusinessTenantMappingInput("test1", testSubdomain, "", nil)
   759  	tenantInputWithRegion := newModelBusinessTenantMappingInput("test1", "", testRegion, nil)
   760  
   761  	tenantModel := newModelBusinessTenantMapping(testID, "test1")
   762  
   763  	uidSvcFn := func() *automock.UIDService {
   764  		uidSvc := &automock.UIDService{}
   765  		uidSvc.On("Generate").Return(testID)
   766  		return uidSvc
   767  	}
   768  
   769  	noopLabelRepo := func() *automock.LabelRepository {
   770  		return &automock.LabelRepository{}
   771  	}
   772  	noopLabelUpsertSvc := func() *automock.LabelUpsertService {
   773  		return &automock.LabelUpsertService{}
   774  	}
   775  
   776  	testCases := []struct {
   777  		Name                string
   778  		tenantInput         model.BusinessTenantMappingInput
   779  		TenantMappingRepoFn func(string) *automock.TenantMappingRepository
   780  		LabelRepoFn         func() *automock.LabelRepository
   781  		LabelUpsertSvcFn    func() *automock.LabelUpsertService
   782  		UIDSvcFn            func() *automock.UIDService
   783  		ExpectedError       error
   784  		ExpectedResult      string
   785  	}{
   786  		{
   787  			Name:        "Success",
   788  			tenantInput: tenantInput,
   789  			TenantMappingRepoFn: func(createRepoFunc string) *automock.TenantMappingRepository {
   790  				return createRepoSvc(ctx, createRepoFunc, *tenantModel)
   791  			},
   792  			UIDSvcFn:         uidSvcFn,
   793  			LabelRepoFn:      noopLabelRepo,
   794  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   795  			ExpectedError:    nil,
   796  			ExpectedResult:   testID,
   797  		},
   798  		{
   799  			Name:        "Success when subdomain should be added",
   800  			tenantInput: tenantInputWithSubdomain,
   801  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   802  				return createRepoSvc(ctx, createFuncName, *tenantInputWithSubdomain.ToBusinessTenantMapping(testID))
   803  			},
   804  			UIDSvcFn:    uidSvcFn,
   805  			LabelRepoFn: noopLabelRepo,
   806  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   807  				svc := &automock.LabelUpsertService{}
   808  				label := &model.LabelInput{
   809  					Key:        "subdomain",
   810  					Value:      testSubdomain,
   811  					ObjectID:   testID,
   812  					ObjectType: model.TenantLabelableObject,
   813  				}
   814  				svc.On("UpsertLabel", ctx, testID, label).Return(nil).Once()
   815  				return svc
   816  			},
   817  			ExpectedError:  nil,
   818  			ExpectedResult: testID,
   819  		},
   820  		{
   821  			Name:        "Success when region should be added",
   822  			tenantInput: tenantInputWithRegion,
   823  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   824  				return createRepoSvc(ctx, createFuncName, *tenantInputWithRegion.ToBusinessTenantMapping(testID))
   825  			},
   826  			UIDSvcFn:    uidSvcFn,
   827  			LabelRepoFn: noopLabelRepo,
   828  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   829  				svc := &automock.LabelUpsertService{}
   830  				label := &model.LabelInput{
   831  					Key:        "region",
   832  					Value:      testRegion,
   833  					ObjectID:   testID,
   834  					ObjectType: model.TenantLabelableObject,
   835  				}
   836  				svc.On("UpsertLabel", ctx, testID, label).Return(nil).Once()
   837  				return svc
   838  			},
   839  			ExpectedError:  nil,
   840  			ExpectedResult: testID,
   841  		},
   842  		{
   843  			Name:        "Error when checking the existence of tenant",
   844  			tenantInput: tenantInput,
   845  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   846  				tenantMappingRepo := &automock.TenantMappingRepository{}
   847  				tenantMappingRepo.On(createFuncName, ctx, *tenantInput.ToBusinessTenantMapping(testID)).Return(nil).Once()
   848  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInput.ExternalTenant).Return(nil, testError)
   849  				return tenantMappingRepo
   850  			},
   851  			UIDSvcFn:         uidSvcFn,
   852  			LabelRepoFn:      noopLabelRepo,
   853  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   854  			ExpectedError:    testError,
   855  			ExpectedResult:   "",
   856  		},
   857  		{
   858  			Name:        "Error when subdomain label setting fails",
   859  			tenantInput: tenantInputWithSubdomain,
   860  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   861  				tenantMappingRepo := &automock.TenantMappingRepository{}
   862  				tenantMappingRepo.On(createFuncName, ctx, *tenantInputWithSubdomain.ToBusinessTenantMapping(testID)).Return(nil).Once()
   863  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInputWithSubdomain.ExternalTenant).Return(tenantModel, nil)
   864  				return tenantMappingRepo
   865  			},
   866  			UIDSvcFn:    uidSvcFn,
   867  			LabelRepoFn: noopLabelRepo,
   868  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   869  				svc := &automock.LabelUpsertService{}
   870  				label := &model.LabelInput{
   871  					Key:        "subdomain",
   872  					Value:      testSubdomain,
   873  					ObjectID:   testID,
   874  					ObjectType: model.TenantLabelableObject,
   875  				}
   876  				svc.On("UpsertLabel", ctx, testID, label).Return(testError).Once()
   877  				return svc
   878  			},
   879  			ExpectedError:  testError,
   880  			ExpectedResult: "",
   881  		},
   882  		{
   883  			Name:        "Error when region label setting fails",
   884  			tenantInput: tenantInputWithRegion,
   885  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   886  				tenantMappingRepo := &automock.TenantMappingRepository{}
   887  				tenantMappingRepo.On(createFuncName, ctx, *tenantInputWithRegion.ToBusinessTenantMapping(testID)).Return(nil).Once()
   888  				tenantMappingRepo.On("GetByExternalTenant", ctx, tenantInputWithRegion.ExternalTenant).Return(tenantModel, nil)
   889  				return tenantMappingRepo
   890  			},
   891  			UIDSvcFn:    uidSvcFn,
   892  			LabelRepoFn: noopLabelRepo,
   893  			LabelUpsertSvcFn: func() *automock.LabelUpsertService {
   894  				svc := &automock.LabelUpsertService{}
   895  				label := &model.LabelInput{
   896  					Key:        "region",
   897  					Value:      testRegion,
   898  					ObjectID:   testID,
   899  					ObjectType: model.TenantLabelableObject,
   900  				}
   901  				svc.On("UpsertLabel", ctx, testID, label).Return(testError).Once()
   902  				return svc
   903  			},
   904  			ExpectedError:  testError,
   905  			ExpectedResult: "",
   906  		},
   907  		{
   908  			Name:        "Error when creating the tenant",
   909  			tenantInput: tenantInput,
   910  			TenantMappingRepoFn: func(createFuncName string) *automock.TenantMappingRepository {
   911  				tenantMappingRepo := &automock.TenantMappingRepository{}
   912  				tenantMappingRepo.On(createFuncName, ctx, *tenantModel).Return(testError).Once()
   913  				return tenantMappingRepo
   914  			},
   915  			UIDSvcFn:         uidSvcFn,
   916  			LabelRepoFn:      noopLabelRepo,
   917  			LabelUpsertSvcFn: noopLabelUpsertSvc,
   918  			ExpectedError:    testError,
   919  			ExpectedResult:   "",
   920  		},
   921  	}
   922  
   923  	for _, testCase := range testCases {
   924  		t.Run(testCase.Name, func(t *testing.T) {
   925  			uidSvc := testCase.UIDSvcFn()
   926  			tenantMappingRepo := testCase.TenantMappingRepoFn("Upsert")
   927  			labelRepo := testCase.LabelRepoFn()
   928  			labelUpsertSvc := testCase.LabelUpsertSvcFn()
   929  			defer mock.AssertExpectationsForObjects(t, tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc)
   930  
   931  			svc := tenant.NewServiceWithLabels(tenantMappingRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
   932  
   933  			// WHEN
   934  			result, err := svc.UpsertSingle(ctx, testCase.tenantInput)
   935  
   936  			// THEN
   937  			if testCase.ExpectedError != nil {
   938  				require.Error(t, err)
   939  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   940  			} else {
   941  				assert.NoError(t, err)
   942  				require.Equal(t, testCase.ExpectedResult, result)
   943  			}
   944  		})
   945  	}
   946  }
   947  
   948  func Test_MultipleToTenantMapping(t *testing.T) {
   949  	testCases := []struct {
   950  		Name          string
   951  		InputSlice    []model.BusinessTenantMappingInput
   952  		ExpectedSlice []model.BusinessTenantMapping
   953  	}{
   954  		{
   955  			Name: "success with more than one parent chain",
   956  			InputSlice: []model.BusinessTenantMappingInput{
   957  				{
   958  					Name:           "acc1",
   959  					ExternalTenant: "0",
   960  				},
   961  				{
   962  					Name:           "acc2",
   963  					ExternalTenant: "1",
   964  					Parent:         "2",
   965  				},
   966  				{
   967  					Name:           "customer1",
   968  					ExternalTenant: "2",
   969  					Parent:         "4",
   970  				},
   971  				{
   972  					Name:           "acc3",
   973  					ExternalTenant: "3",
   974  				},
   975  				{
   976  					Name:           "x1",
   977  					ExternalTenant: "4",
   978  				},
   979  			},
   980  			ExpectedSlice: []model.BusinessTenantMapping{
   981  				{
   982  					ID:             "0",
   983  					Name:           "acc1",
   984  					ExternalTenant: "0",
   985  					Status:         tenantEntity.Active,
   986  					Type:           tenantEntity.Unknown,
   987  				},
   988  				{
   989  					ID:             "4",
   990  					Name:           "x1",
   991  					ExternalTenant: "4",
   992  					Status:         tenantEntity.Active,
   993  					Type:           tenantEntity.Unknown,
   994  				},
   995  				{
   996  					ID:             "2",
   997  					Name:           "customer1",
   998  					ExternalTenant: "2",
   999  					Parent:         "4",
  1000  					Status:         tenantEntity.Active,
  1001  					Type:           tenantEntity.Unknown,
  1002  				},
  1003  				{
  1004  					ID:             "1",
  1005  					Name:           "acc2",
  1006  					ExternalTenant: "1",
  1007  					Parent:         "2",
  1008  					Status:         tenantEntity.Active,
  1009  					Type:           tenantEntity.Unknown,
  1010  				},
  1011  				{
  1012  					ID:             "3",
  1013  					Name:           "acc3",
  1014  					ExternalTenant: "3",
  1015  					Status:         tenantEntity.Active,
  1016  					Type:           tenantEntity.Unknown,
  1017  				},
  1018  			},
  1019  		},
  1020  	}
  1021  
  1022  	for _, testCase := range testCases {
  1023  		t.Run(testCase.Name, func(t *testing.T) {
  1024  			svc := tenant.NewService(nil, &serialUUIDService{}, nil)
  1025  			require.Equal(t, testCase.ExpectedSlice, svc.MultipleToTenantMapping(testCase.InputSlice))
  1026  		})
  1027  	}
  1028  }
  1029  
  1030  func Test_Update(t *testing.T) {
  1031  	tnt := model.BusinessTenantMappingInput{
  1032  		Name:           testName,
  1033  		ExternalTenant: testExternal,
  1034  		Parent:         testParentID,
  1035  		Subdomain:      testSubdomain,
  1036  		Region:         testRegion,
  1037  		Type:           string(tenantEntity.Account),
  1038  		Provider:       testProvider,
  1039  	}
  1040  	tntToBusinessTenantMapping := &model.BusinessTenantMapping{
  1041  		ID:             testID,
  1042  		Name:           testName,
  1043  		ExternalTenant: testExternal,
  1044  		Parent:         testParentID,
  1045  		Type:           tenantEntity.Account,
  1046  		Provider:       testProvider,
  1047  		Status:         tenantEntity.Active,
  1048  		Initialized:    nil,
  1049  	}
  1050  
  1051  	testCases := []struct {
  1052  		Name                      string
  1053  		InputID                   string
  1054  		InputTenant               model.BusinessTenantMappingInput
  1055  		TenantMappingRepositoryFn func() *automock.TenantMappingRepository
  1056  		ExpectedErr               error
  1057  	}{
  1058  		{
  1059  			Name:        "Success",
  1060  			InputID:     testID,
  1061  			InputTenant: tnt,
  1062  			TenantMappingRepositoryFn: func() *automock.TenantMappingRepository {
  1063  				tenantMappingRepo := &automock.TenantMappingRepository{}
  1064  				tenantMappingRepo.On("Update", mock.Anything, tntToBusinessTenantMapping).Return(nil)
  1065  				return tenantMappingRepo
  1066  			},
  1067  			ExpectedErr: nil,
  1068  		},
  1069  		{
  1070  			Name:        "Returns error when can't update the tenant",
  1071  			InputID:     testID,
  1072  			InputTenant: tnt,
  1073  			TenantMappingRepositoryFn: func() *automock.TenantMappingRepository {
  1074  				tenantMappingRepo := &automock.TenantMappingRepository{}
  1075  				tenantMappingRepo.On("Update", mock.Anything, tntToBusinessTenantMapping).Return(testError)
  1076  				return tenantMappingRepo
  1077  			},
  1078  			ExpectedErr: testError,
  1079  		},
  1080  	}
  1081  
  1082  	for _, testCase := range testCases {
  1083  		t.Run(testCase.Name, func(t *testing.T) {
  1084  			ctx := context.TODO()
  1085  			tenantMappingRepo := testCase.TenantMappingRepositoryFn()
  1086  			serialUUIDService := &serialUUIDService{}
  1087  			svc := tenant.NewService(tenantMappingRepo, serialUUIDService, nil)
  1088  			err := svc.Update(ctx, testCase.InputID, testCase.InputTenant)
  1089  
  1090  			if testCase.ExpectedErr != nil {
  1091  				assert.Error(t, err)
  1092  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1093  			} else {
  1094  				assert.NoError(t, err)
  1095  			}
  1096  		})
  1097  	}
  1098  }
  1099  
  1100  func Test_MoveBeforeIndex(t *testing.T) {
  1101  	testCases := []struct {
  1102  		Name           string
  1103  		InputSlice     []model.BusinessTenantMapping
  1104  		TargetTenantID string
  1105  		TargetIndex    int
  1106  		ExpectedSlice  []model.BusinessTenantMapping
  1107  		ShouldMove     bool
  1108  	}{
  1109  		{
  1110  			Name: "success",
  1111  			InputSlice: []model.BusinessTenantMapping{
  1112  				{ID: "1"}, {ID: "2"}, {ID: "3"}, {ID: "4"}, {ID: "5"},
  1113  			},
  1114  			TargetTenantID: "4",
  1115  			TargetIndex:    1,
  1116  			ExpectedSlice: []model.BusinessTenantMapping{
  1117  				{ID: "1"}, {ID: "4"}, {ID: "2"}, {ID: "3"}, {ID: "5"},
  1118  			},
  1119  			ShouldMove: true,
  1120  		},
  1121  		{
  1122  			Name: "move before first element",
  1123  			InputSlice: []model.BusinessTenantMapping{
  1124  				{ID: "1"}, {ID: "2"}, {ID: "3"}, {ID: "4"}, {ID: "5"},
  1125  			},
  1126  			TargetTenantID: "3",
  1127  			TargetIndex:    0,
  1128  			ExpectedSlice: []model.BusinessTenantMapping{
  1129  				{ID: "3"}, {ID: "1"}, {ID: "2"}, {ID: "4"}, {ID: "5"},
  1130  			},
  1131  			ShouldMove: true,
  1132  		},
  1133  		{
  1134  			Name: "move before last element",
  1135  			InputSlice: []model.BusinessTenantMapping{
  1136  				{ID: "1"}, {ID: "2"}, {ID: "3"}, {ID: "4"}, {ID: "5"},
  1137  			},
  1138  			TargetTenantID: "3",
  1139  			TargetIndex:    4,
  1140  			ExpectedSlice: []model.BusinessTenantMapping{
  1141  				{ID: "1"}, {ID: "2"}, {ID: "3"}, {ID: "4"}, {ID: "5"},
  1142  			},
  1143  		},
  1144  	}
  1145  
  1146  	for _, testCase := range testCases {
  1147  		t.Run(testCase.Name, func(t *testing.T) {
  1148  			result, moved := tenant.MoveBeforeIfShould(testCase.InputSlice, testCase.TargetTenantID, testCase.TargetIndex)
  1149  			require.Equal(t, testCase.ShouldMove, moved)
  1150  			require.Equal(t, testCase.ExpectedSlice, result)
  1151  		})
  1152  	}
  1153  }
  1154  
  1155  func Test_ListLabels(t *testing.T) {
  1156  	const tenantID = "edc6857b-b0c7-49e6-9f0a-e87a9c2a4eb8"
  1157  
  1158  	ctx := context.TODO()
  1159  	testErr := errors.New("failed to list labels")
  1160  
  1161  	t.Run("Success", func(t *testing.T) {
  1162  		labels := map[string]*model.Label{
  1163  			"label-key": {
  1164  				ID:         "5ef5ebd0-987d-4cb6-a3c1-7d710de259a2",
  1165  				Tenant:     str.Ptr(tenantID),
  1166  				Key:        "label-key",
  1167  				Value:      "value",
  1168  				ObjectID:   tenantID,
  1169  				ObjectType: model.TenantLabelableObject,
  1170  			},
  1171  		}
  1172  
  1173  		uidSvc := &automock.UIDService{}
  1174  		labelUpsertSvc := &automock.LabelUpsertService{}
  1175  
  1176  		tenantRepo := &automock.TenantMappingRepository{}
  1177  		tenantRepo.On("Exists", ctx, tenantID).Return(true, nil)
  1178  
  1179  		labelRepo := &automock.LabelRepository{}
  1180  		labelRepo.On("ListForObject", ctx, tenantID, model.TenantLabelableObject, tenantID).Return(labels, nil)
  1181  
  1182  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1183  
  1184  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1185  
  1186  		actualLabels, err := svc.ListLabels(ctx, tenantID)
  1187  		assert.NoError(t, err)
  1188  		assert.Equal(t, labels, actualLabels)
  1189  	})
  1190  
  1191  	t.Run("Error when tenant existence cannot be ensured", func(t *testing.T) {
  1192  		uidSvc := &automock.UIDService{}
  1193  		labelRepo := &automock.LabelRepository{}
  1194  		labelUpsertSvc := &automock.LabelUpsertService{}
  1195  
  1196  		tenantRepo := &automock.TenantMappingRepository{}
  1197  		tenantRepo.On("Exists", ctx, tenantID).Return(false, testErr)
  1198  
  1199  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1200  
  1201  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1202  
  1203  		_, err := svc.ListLabels(ctx, tenantID)
  1204  		assert.Error(t, err)
  1205  		assert.Contains(t, err.Error(), fmt.Sprintf("while checking if tenant with ID %s exists", tenantID))
  1206  	})
  1207  
  1208  	t.Run("Error when tenant does not exist", func(t *testing.T) {
  1209  		uidSvc := &automock.UIDService{}
  1210  		labelRepo := &automock.LabelRepository{}
  1211  		labelUpsertSvc := &automock.LabelUpsertService{}
  1212  
  1213  		tenantRepo := &automock.TenantMappingRepository{}
  1214  		tenantRepo.On("Exists", ctx, tenantID).Return(false, nil)
  1215  
  1216  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1217  
  1218  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1219  
  1220  		_, err := svc.ListLabels(ctx, tenantID)
  1221  		assert.Error(t, err)
  1222  		assert.True(t, apperrors.IsNotFoundError(err))
  1223  	})
  1224  
  1225  	t.Run("Error when fails to list labels from repo", func(t *testing.T) {
  1226  		uidSvc := &automock.UIDService{}
  1227  		labelUpsertSvc := &automock.LabelUpsertService{}
  1228  
  1229  		tenantRepo := &automock.TenantMappingRepository{}
  1230  		tenantRepo.On("Exists", ctx, tenantID).Return(true, nil)
  1231  
  1232  		labelRepo := &automock.LabelRepository{}
  1233  		labelRepo.On("ListForObject", ctx, tenantID, model.TenantLabelableObject, tenantID).Return(nil, testErr)
  1234  
  1235  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1236  
  1237  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1238  
  1239  		_, err := svc.ListLabels(ctx, tenantID)
  1240  		assert.Error(t, err)
  1241  		assert.Contains(t, err.Error(), fmt.Sprintf("whilie listing labels for tenant with ID %s", tenantID))
  1242  	})
  1243  }
  1244  
  1245  func Test_GetTenantByExternalID(t *testing.T) {
  1246  	t.Run("Success", func(t *testing.T) {
  1247  		// GIVEN
  1248  		ctx := context.TODO()
  1249  		expected := &model.BusinessTenantMapping{
  1250  			ID:             testID,
  1251  			Name:           testName,
  1252  			ExternalTenant: testExternal,
  1253  			Status:         tenantEntity.Active,
  1254  			Type:           tenantEntity.Account,
  1255  		}
  1256  
  1257  		uidSvc := &automock.UIDService{}
  1258  		labelUpsertSvc := &automock.LabelUpsertService{}
  1259  		labelRepo := &automock.LabelRepository{}
  1260  
  1261  		tenantRepo := &automock.TenantMappingRepository{}
  1262  		tenantRepo.On("GetByExternalTenant", ctx, testID).Return(expected, nil)
  1263  
  1264  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1265  
  1266  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1267  
  1268  		// WHEN
  1269  		actual, err := svc.GetTenantByExternalID(ctx, testID)
  1270  
  1271  		// THEN
  1272  		assert.NoError(t, err)
  1273  		assert.Equal(t, expected, actual)
  1274  	})
  1275  	t.Run("Returns error when retrieval from DB fails", func(t *testing.T) {
  1276  		// GIVEN
  1277  		ctx := context.TODO()
  1278  
  1279  		uidSvc := &automock.UIDService{}
  1280  		labelUpsertSvc := &automock.LabelUpsertService{}
  1281  		labelRepo := &automock.LabelRepository{}
  1282  
  1283  		tenantRepo := &automock.TenantMappingRepository{}
  1284  		tenantRepo.On("GetByExternalTenant", ctx, testID).Return(nil, testError)
  1285  
  1286  		defer mock.AssertExpectationsForObjects(t, tenantRepo, uidSvc, labelRepo, labelUpsertSvc)
  1287  
  1288  		svc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc, nil)
  1289  
  1290  		// WHEN
  1291  		actual, err := svc.GetTenantByExternalID(ctx, testID)
  1292  
  1293  		// THEN
  1294  		assert.Error(t, err)
  1295  		assert.Nil(t, actual)
  1296  		assert.Equal(t, testError, err)
  1297  	})
  1298  }
  1299  
  1300  func TestService_CreateTenantAccessForResource(t *testing.T) {
  1301  	testCases := []struct {
  1302  		Name             string
  1303  		ConverterFn      func() *automock.BusinessTenantMappingConverter
  1304  		PersistenceFn    func() (*sqlx.DB, testdb.DBMock)
  1305  		Input            *model.TenantAccess
  1306  		ExpectedErrorMsg string
  1307  	}{
  1308  		{
  1309  			Name: "Success",
  1310  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1311  				conv := &automock.BusinessTenantMappingConverter{}
  1312  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1313  				return conv
  1314  			},
  1315  			PersistenceFn: func() (*sqlx.DB, testdb.DBMock) {
  1316  				db, dbMock := testdb.MockDatabase(t)
  1317  
  1318  				dbMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO tenant_applications ( tenant_id, id, owner ) VALUES ( ?, ?, ? ) ON CONFLICT ON CONSTRAINT tenant_applications_pkey DO NOTHING`)).
  1319  					WithArgs(testInternal, testID, true).
  1320  					WillReturnResult(sqlmock.NewResult(1, 1))
  1321  				return db, dbMock
  1322  			},
  1323  			Input: tenantAccessModel,
  1324  		},
  1325  		{
  1326  			Name:             "Error when resource does not have access table",
  1327  			Input:            invalidTenantAccessModel,
  1328  			ExpectedErrorMsg: fmt.Sprintf("entity %q does not have access table", invalidResourceType),
  1329  		},
  1330  		{
  1331  			Name: "Error while creating tenant access",
  1332  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1333  				conv := &automock.BusinessTenantMappingConverter{}
  1334  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1335  				return conv
  1336  			},
  1337  			Input:            tenantAccessModel,
  1338  			ExpectedErrorMsg: fmt.Sprintf("while creating tenant acccess for resource type %q with ID %q for tenant %q", tenantAccessModel.ResourceType, tenantAccessModel.ResourceID, tenantAccessModel.InternalTenantID),
  1339  		},
  1340  	}
  1341  
  1342  	for _, testCase := range testCases {
  1343  		t.Run(testCase.Name, func(t *testing.T) {
  1344  			ctx := context.TODO()
  1345  			converter := unusedConverter()
  1346  			if testCase.ConverterFn != nil {
  1347  				converter = testCase.ConverterFn()
  1348  			}
  1349  			db, dbMock := unusedDBMock(t)
  1350  			if testCase.PersistenceFn != nil {
  1351  				db, dbMock = testCase.PersistenceFn()
  1352  			}
  1353  			ctx = persistence.SaveToContext(ctx, db)
  1354  
  1355  			svc := tenant.NewService(nil, nil, converter)
  1356  
  1357  			// WHEN
  1358  			err := svc.CreateTenantAccessForResource(ctx, testCase.Input)
  1359  
  1360  			if testCase.ExpectedErrorMsg != "" {
  1361  				require.Error(t, err)
  1362  				require.Contains(t, err.Error(), testCase.ExpectedErrorMsg)
  1363  			} else {
  1364  				require.NoError(t, err)
  1365  			}
  1366  
  1367  			mock.AssertExpectationsForObjects(t, converter)
  1368  			dbMock.AssertExpectations(t)
  1369  		})
  1370  	}
  1371  }
  1372  
  1373  func TestService_CreateTenantAccessForResourceRecursively(t *testing.T) {
  1374  	testCases := []struct {
  1375  		Name             string
  1376  		ConverterFn      func() *automock.BusinessTenantMappingConverter
  1377  		PersistenceFn    func() (*sqlx.DB, testdb.DBMock)
  1378  		Input            *model.TenantAccess
  1379  		ExpectedErrorMsg string
  1380  	}{
  1381  		{
  1382  			Name: "Success",
  1383  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1384  				conv := &automock.BusinessTenantMappingConverter{}
  1385  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1386  				return conv
  1387  			},
  1388  			PersistenceFn: func() (*sqlx.DB, testdb.DBMock) {
  1389  				db, dbMock := testdb.MockDatabase(t)
  1390  
  1391  				dbMock.ExpectExec(regexp.QuoteMeta(`WITH RECURSIVE parents AS (SELECT t1.id, t1.parent FROM business_tenant_mappings t1 WHERE id = ? UNION ALL SELECT t2.id, t2.parent FROM business_tenant_mappings t2 INNER JOIN parents t on t2.id = t.parent) INSERT INTO tenant_applications ( tenant_id, id, owner ) (SELECT parents.id AS tenant_id, ? as id, ? AS owner FROM parents)`)).
  1392  					WithArgs(testInternal, testID, true).
  1393  					WillReturnResult(sqlmock.NewResult(1, 1))
  1394  				return db, dbMock
  1395  			},
  1396  			Input: tenantAccessModel,
  1397  		},
  1398  		{
  1399  			Name:             "Error when resource does not have access table",
  1400  			Input:            invalidTenantAccessModel,
  1401  			ExpectedErrorMsg: fmt.Sprintf("entity %q does not have access table", invalidResourceType),
  1402  		},
  1403  		{
  1404  			Name: "Error while creating tenant access",
  1405  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1406  				conv := &automock.BusinessTenantMappingConverter{}
  1407  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1408  				return conv
  1409  			},
  1410  			Input:            tenantAccessModel,
  1411  			ExpectedErrorMsg: fmt.Sprintf("while creating tenant acccess for resource type %q with ID %q for tenant %q", tenantAccessModel.ResourceType, tenantAccessModel.ResourceID, tenantAccessModel.InternalTenantID),
  1412  		},
  1413  	}
  1414  
  1415  	for _, testCase := range testCases {
  1416  		t.Run(testCase.Name, func(t *testing.T) {
  1417  			ctx := context.TODO()
  1418  			converter := unusedConverter()
  1419  			if testCase.ConverterFn != nil {
  1420  				converter = testCase.ConverterFn()
  1421  			}
  1422  			db, dbMock := unusedDBMock(t)
  1423  			if testCase.PersistenceFn != nil {
  1424  				db, dbMock = testCase.PersistenceFn()
  1425  			}
  1426  			ctx = persistence.SaveToContext(ctx, db)
  1427  
  1428  			svc := tenant.NewService(nil, nil, converter)
  1429  
  1430  			// WHEN
  1431  			err := svc.CreateTenantAccessForResourceRecursively(ctx, testCase.Input)
  1432  
  1433  			if testCase.ExpectedErrorMsg != "" {
  1434  				require.Error(t, err)
  1435  				require.Contains(t, err.Error(), testCase.ExpectedErrorMsg)
  1436  			} else {
  1437  				require.NoError(t, err)
  1438  			}
  1439  
  1440  			mock.AssertExpectationsForObjects(t, converter)
  1441  			dbMock.AssertExpectations(t)
  1442  		})
  1443  	}
  1444  }
  1445  
  1446  func TestService_DeleteTenantAccessForResource(t *testing.T) {
  1447  	testCases := []struct {
  1448  		Name             string
  1449  		ConverterFn      func() *automock.BusinessTenantMappingConverter
  1450  		PersistenceFn    func() (*sqlx.DB, testdb.DBMock)
  1451  		Input            *model.TenantAccess
  1452  		ExpectedErrorMsg string
  1453  	}{
  1454  		{
  1455  			Name: "Success",
  1456  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1457  				conv := &automock.BusinessTenantMappingConverter{}
  1458  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1459  				return conv
  1460  			},
  1461  			PersistenceFn: func() (*sqlx.DB, testdb.DBMock) {
  1462  				db, dbMock := testdb.MockDatabase(t)
  1463  
  1464  				dbMock.ExpectExec(regexp.QuoteMeta(`WITH RECURSIVE parents AS (SELECT t1.id, t1.parent FROM business_tenant_mappings t1 WHERE id = $1 UNION ALL SELECT t2.id, t2.parent FROM business_tenant_mappings t2 INNER JOIN parents t on t2.id = t.parent) DELETE FROM tenant_applications WHERE id IN ($2) AND tenant_id IN (SELECT id FROM parents)`)).
  1465  					WithArgs(testInternal, testID).
  1466  					WillReturnResult(sqlmock.NewResult(1, 1))
  1467  				return db, dbMock
  1468  			},
  1469  			Input: tenantAccessModel,
  1470  		},
  1471  		{
  1472  			Name:             "Error when resource does not have access table",
  1473  			Input:            invalidTenantAccessModel,
  1474  			ExpectedErrorMsg: fmt.Sprintf("entity %q does not have access table", invalidResourceType),
  1475  		},
  1476  		{
  1477  			Name: "Error while deleting tenant access",
  1478  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1479  				conv := &automock.BusinessTenantMappingConverter{}
  1480  				conv.On("TenantAccessToEntity", tenantAccessModel).Return(tenantAccessEntity).Once()
  1481  				return conv
  1482  			},
  1483  			Input:            tenantAccessModel,
  1484  			ExpectedErrorMsg: fmt.Sprintf("while deleting tenant acccess for resource type %q with ID %q for tenant %q", tenantAccessModel.ResourceType, tenantAccessModel.ResourceID, tenantAccessModel.InternalTenantID),
  1485  		},
  1486  	}
  1487  
  1488  	for _, testCase := range testCases {
  1489  		t.Run(testCase.Name, func(t *testing.T) {
  1490  			ctx := context.TODO()
  1491  			converter := unusedConverter()
  1492  			if testCase.ConverterFn != nil {
  1493  				converter = testCase.ConverterFn()
  1494  			}
  1495  			db, dbMock := unusedDBMock(t)
  1496  			if testCase.PersistenceFn != nil {
  1497  				db, dbMock = testCase.PersistenceFn()
  1498  			}
  1499  			ctx = persistence.SaveToContext(ctx, db)
  1500  
  1501  			svc := tenant.NewService(nil, nil, converter)
  1502  
  1503  			// WHEN
  1504  			err := svc.DeleteTenantAccessForResourceRecursively(ctx, testCase.Input)
  1505  
  1506  			if testCase.ExpectedErrorMsg != "" {
  1507  				require.Error(t, err)
  1508  				require.Contains(t, err.Error(), testCase.ExpectedErrorMsg)
  1509  			} else {
  1510  				require.NoError(t, err)
  1511  			}
  1512  
  1513  			mock.AssertExpectationsForObjects(t, converter)
  1514  			dbMock.AssertExpectations(t)
  1515  		})
  1516  	}
  1517  }
  1518  
  1519  func TestService_GetTenantAccessForResource(t *testing.T) {
  1520  	testCases := []struct {
  1521  		Name             string
  1522  		ConverterFn      func() *automock.BusinessTenantMappingConverter
  1523  		PersistenceFn    func() (*sqlx.DB, testdb.DBMock)
  1524  		Input            *model.TenantAccess
  1525  		ExpectedErrorMsg string
  1526  		ExpectedOutput   *model.TenantAccess
  1527  	}{
  1528  		{
  1529  			Name: "Success",
  1530  			ConverterFn: func() *automock.BusinessTenantMappingConverter {
  1531  				conv := &automock.BusinessTenantMappingConverter{}
  1532  				conv.On("TenantAccessFromEntity", tenantAccessEntity).Return(tenantAccessModelWithoutExternalTenant).Once()
  1533  				return conv
  1534  			},
  1535  			PersistenceFn: func() (*sqlx.DB, testdb.DBMock) {
  1536  				db, dbMock := testdb.MockDatabase(t)
  1537  				rows := sqlmock.NewRows(tenantAccessTestTableColumns)
  1538  				rows.AddRow(testInternal, testID, true)
  1539  				dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT tenant_id, id, owner FROM tenant_applications WHERE tenant_id = $1 AND id = $2`)).
  1540  					WithArgs(testInternal, testID).WillReturnRows(rows)
  1541  				return db, dbMock
  1542  			},
  1543  			Input:          tenantAccessModel,
  1544  			ExpectedOutput: tenantAccessModelWithoutExternalTenant,
  1545  		},
  1546  		{
  1547  			Name:             "Error when resource does not have access table",
  1548  			Input:            invalidTenantAccessModel,
  1549  			ExpectedErrorMsg: fmt.Sprintf("entity %q does not have access table", invalidResourceType),
  1550  		},
  1551  		{
  1552  			Name: "Error while getting tenant access",
  1553  			PersistenceFn: func() (*sqlx.DB, testdb.DBMock) {
  1554  				db, dbMock := testdb.MockDatabase(t)
  1555  				dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT tenant_id, id, owner FROM tenant_applications WHERE tenant_id = $1 AND id = $2`)).
  1556  					WithArgs(testInternal, testID).WillReturnError(testError)
  1557  				return db, dbMock
  1558  			},
  1559  			Input:            tenantAccessModel,
  1560  			ExpectedErrorMsg: "Unexpected error while executing SQL query",
  1561  		},
  1562  	}
  1563  
  1564  	for _, testCase := range testCases {
  1565  		t.Run(testCase.Name, func(t *testing.T) {
  1566  			ctx := context.TODO()
  1567  			converter := unusedConverter()
  1568  			if testCase.ConverterFn != nil {
  1569  				converter = testCase.ConverterFn()
  1570  			}
  1571  			db, dbMock := unusedDBMock(t)
  1572  			if testCase.PersistenceFn != nil {
  1573  				db, dbMock = testCase.PersistenceFn()
  1574  			}
  1575  			ctx = persistence.SaveToContext(ctx, db)
  1576  
  1577  			svc := tenant.NewService(nil, nil, converter)
  1578  
  1579  			// WHEN
  1580  			result, err := svc.GetTenantAccessForResource(ctx, testCase.Input.InternalTenantID, testCase.Input.ResourceID, testCase.Input.ResourceType)
  1581  
  1582  			if testCase.ExpectedErrorMsg != "" {
  1583  				require.Error(t, err)
  1584  				require.Contains(t, err.Error(), testCase.ExpectedErrorMsg)
  1585  			} else {
  1586  				require.NoError(t, err)
  1587  				require.Equal(t, testCase.ExpectedOutput, result)
  1588  			}
  1589  
  1590  			mock.AssertExpectationsForObjects(t, converter)
  1591  			dbMock.AssertExpectations(t)
  1592  		})
  1593  	}
  1594  }
  1595  
  1596  type serialUUIDService struct {
  1597  	i int
  1598  }
  1599  
  1600  func (s *serialUUIDService) Generate() string {
  1601  	result := s.i
  1602  	s.i++
  1603  	return fmt.Sprintf("%d", result)
  1604  }
  1605  
  1606  func createRepoSvc(ctx context.Context, createFuncName string, tenants ...model.BusinessTenantMapping) *automock.TenantMappingRepository {
  1607  	tenantMappingRepo := &automock.TenantMappingRepository{}
  1608  	for _, t := range tenants {
  1609  		tenantMappingRepo.On(createFuncName, ctx, t).Return(nil).Once()
  1610  		tenantMappingRepo.On("GetByExternalTenant", ctx, t.ExternalTenant).Return(&t, nil).Once()
  1611  	}
  1612  	return tenantMappingRepo
  1613  }