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

     1  package tenant_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"database/sql/driver"
     7  	"fmt"
     8  	"regexp"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    13  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    14  
    15  	"github.com/google/uuid"
    16  	"github.com/jmoiron/sqlx"
    17  
    18  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    19  
    20  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    21  
    22  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    23  
    24  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    25  	tenantEntity "github.com/kyma-incubator/compass/components/director/pkg/tenant"
    26  
    27  	"github.com/DATA-DOG/go-sqlmock"
    28  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant/automock"
    29  	"github.com/kyma-incubator/compass/components/director/internal/model"
    30  	"github.com/kyma-incubator/compass/components/director/internal/repo/testdb"
    31  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func TestPgRepository_Upsert(t *testing.T) {
    37  	t.Run("Success", func(t *testing.T) {
    38  		// GIVEN
    39  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
    40  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
    41  
    42  		mockConverter := &automock.Converter{}
    43  		defer mockConverter.AssertExpectations(t)
    44  		mockConverter.On("ToEntity", tenantMappingModel).Return(tenantMappingEntity).Once()
    45  		db, dbMock := testdb.MockDatabase(t)
    46  		defer dbMock.AssertExpectations(t)
    47  		dbMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO public.business_tenant_mappings ( id, external_name, external_tenant, parent, type, provider_name, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) ON CONFLICT ( external_tenant ) DO UPDATE SET external_name=EXCLUDED.external_name`)).
    48  			WithArgs(fixTenantMappingCreateArgs(*tenantMappingEntity)...).
    49  			WillReturnResult(sqlmock.NewResult(1, 1))
    50  
    51  		ctx := persistence.SaveToContext(context.TODO(), db)
    52  		tenantMappingrepo := tenant.NewRepository(mockConverter)
    53  
    54  		// WHEN
    55  		err := tenantMappingrepo.Upsert(ctx, *tenantMappingModel)
    56  
    57  		// THEN
    58  		require.NoError(t, err)
    59  	})
    60  
    61  	t.Run("Error when upserting", func(t *testing.T) {
    62  		// GIVEN
    63  		tenantModel := newModelBusinessTenantMapping(testID, testName)
    64  		tenantEntity := newEntityBusinessTenantMapping(testID, testName)
    65  
    66  		mockConverter := &automock.Converter{}
    67  		defer mockConverter.AssertExpectations(t)
    68  		mockConverter.On("ToEntity", tenantModel).Return(tenantEntity).Once()
    69  		db, dbMock := testdb.MockDatabase(t)
    70  		defer dbMock.AssertExpectations(t)
    71  		dbMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO public.business_tenant_mappings ( id, external_name, external_tenant, parent, type, provider_name, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) ON CONFLICT ( external_tenant ) DO UPDATE SET external_name=EXCLUDED.external_name`)).
    72  			WithArgs(fixTenantMappingCreateArgs(*tenantEntity)...).
    73  			WillReturnError(testError)
    74  
    75  		ctx := persistence.SaveToContext(context.TODO(), db)
    76  		tenantMappingRepo := tenant.NewRepository(mockConverter)
    77  
    78  		// WHEN
    79  		err := tenantMappingRepo.Upsert(ctx, *tenantModel)
    80  
    81  		// THEN
    82  		require.Error(t, err)
    83  		assert.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
    84  	})
    85  }
    86  
    87  func TestPgRepository_UnsafeCreate(t *testing.T) {
    88  	t.Run("Success", func(t *testing.T) {
    89  		// GIVEN
    90  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
    91  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
    92  
    93  		mockConverter := &automock.Converter{}
    94  		defer mockConverter.AssertExpectations(t)
    95  		mockConverter.On("ToEntity", tenantMappingModel).Return(tenantMappingEntity).Once()
    96  		db, dbMock := testdb.MockDatabase(t)
    97  		defer dbMock.AssertExpectations(t)
    98  		dbMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO public.business_tenant_mappings ( id, external_name, external_tenant, parent, type, provider_name, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? )  ON CONFLICT ( external_tenant ) DO NOTHING`)).
    99  			WithArgs(fixTenantMappingCreateArgs(*tenantMappingEntity)...).
   100  			WillReturnResult(sqlmock.NewResult(-1, 1))
   101  
   102  		ctx := persistence.SaveToContext(context.TODO(), db)
   103  		tenantMappingrepo := tenant.NewRepository(mockConverter)
   104  
   105  		// WHEN
   106  		err := tenantMappingrepo.UnsafeCreate(ctx, *tenantMappingModel)
   107  
   108  		// THEN
   109  		require.NoError(t, err)
   110  	})
   111  
   112  	t.Run("Error when creating", func(t *testing.T) {
   113  		// GIVEN
   114  		tenantModel := newModelBusinessTenantMapping(testID, testName)
   115  		tenantEntity := newEntityBusinessTenantMapping(testID, testName)
   116  
   117  		mockConverter := &automock.Converter{}
   118  		defer mockConverter.AssertExpectations(t)
   119  		mockConverter.On("ToEntity", tenantModel).Return(tenantEntity).Once()
   120  		db, dbMock := testdb.MockDatabase(t)
   121  		defer dbMock.AssertExpectations(t)
   122  		dbMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO public.business_tenant_mappings ( id, external_name, external_tenant, parent, type, provider_name, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? )  ON CONFLICT ( external_tenant ) DO NOTHING`)).
   123  			WithArgs(fixTenantMappingCreateArgs(*tenantEntity)...).
   124  			WillReturnError(testError)
   125  
   126  		ctx := persistence.SaveToContext(context.TODO(), db)
   127  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   128  
   129  		// WHEN
   130  		err := tenantMappingRepo.UnsafeCreate(ctx, *tenantModel)
   131  
   132  		// THEN
   133  		require.Error(t, err)
   134  		assert.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   135  	})
   136  }
   137  
   138  func TestPgRepository_Get(t *testing.T) {
   139  	t.Run("Success", func(t *testing.T) {
   140  		// GIVEN
   141  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
   142  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
   143  
   144  		mockConverter := &automock.Converter{}
   145  		defer mockConverter.AssertExpectations(t)
   146  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
   147  		db, dbMock := testdb.MockDatabase(t)
   148  		defer dbMock.AssertExpectations(t)
   149  		rowsToReturn := fixSQLRows([]sqlRow{
   150  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   151  		})
   152  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   153  			WithArgs(testID, tenantEntity.Inactive).
   154  			WillReturnRows(rowsToReturn)
   155  
   156  		ctx := persistence.SaveToContext(context.TODO(), db)
   157  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   158  
   159  		// WHEN
   160  		result, err := tenantMappingRepo.Get(ctx, testID)
   161  
   162  		// THEN
   163  		require.NoError(t, err)
   164  		require.NotNil(t, result)
   165  		assert.Equal(t, tenantMappingModel, result)
   166  	})
   167  
   168  	t.Run("Error when get", func(t *testing.T) {
   169  		// GIVEN
   170  		db, dbMock := testdb.MockDatabase(t)
   171  		defer dbMock.AssertExpectations(t)
   172  
   173  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   174  			WithArgs(testID, tenantEntity.Inactive).WillReturnError(testError)
   175  
   176  		ctx := persistence.SaveToContext(context.TODO(), db)
   177  		tenantMappingRepo := tenant.NewRepository(nil)
   178  
   179  		// WHEN
   180  		result, err := tenantMappingRepo.Get(ctx, testID)
   181  
   182  		// THEN
   183  		require.Error(t, err)
   184  		require.Nil(t, result)
   185  	})
   186  }
   187  
   188  func TestPgRepository_GetByExternalTenant(t *testing.T) {
   189  	t.Run("Success", func(t *testing.T) {
   190  		// GIVEN
   191  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
   192  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
   193  
   194  		mockConverter := &automock.Converter{}
   195  		defer mockConverter.AssertExpectations(t)
   196  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
   197  		db, dbMock := testdb.MockDatabase(t)
   198  		defer dbMock.AssertExpectations(t)
   199  		rowsToReturn := fixSQLRows([]sqlRow{
   200  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   201  		})
   202  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
   203  			WithArgs(testExternal, tenantEntity.Inactive).
   204  			WillReturnRows(rowsToReturn)
   205  
   206  		ctx := persistence.SaveToContext(context.TODO(), db)
   207  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   208  
   209  		// WHEN
   210  		result, err := tenantMappingRepo.GetByExternalTenant(ctx, testExternal)
   211  
   212  		// THEN
   213  		require.NoError(t, err)
   214  		require.NotNil(t, result)
   215  		assert.Equal(t, tenantMappingModel, result)
   216  	})
   217  
   218  	t.Run("Error when getting", func(t *testing.T) {
   219  		// GIVEN
   220  		mockConverter := &automock.Converter{}
   221  		defer mockConverter.AssertExpectations(t)
   222  		db, dbMock := testdb.MockDatabase(t)
   223  		defer dbMock.AssertExpectations(t)
   224  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $ `)).
   225  			WithArgs(testExternal, tenantEntity.Inactive).
   226  			WillReturnError(testError)
   227  
   228  		ctx := persistence.SaveToContext(context.TODO(), db)
   229  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   230  
   231  		// WHEN
   232  		result, err := tenantMappingRepo.GetByExternalTenant(ctx, testExternal)
   233  
   234  		// THEN
   235  		require.Error(t, err)
   236  		assert.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   237  		require.Nil(t, result)
   238  	})
   239  }
   240  
   241  func TestPgRepository_Exists(t *testing.T) {
   242  	t.Run("Success", func(t *testing.T) {
   243  		// GIVEN
   244  		db, dbMock := testdb.MockDatabase(t)
   245  		defer dbMock.AssertExpectations(t)
   246  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT 1 FROM public.business_tenant_mappings WHERE id = $1`)).
   247  			WithArgs(testID).
   248  			WillReturnRows(testdb.RowWhenObjectExist())
   249  
   250  		ctx := persistence.SaveToContext(context.TODO(), db)
   251  		tenantMappingRepo := tenant.NewRepository(nil)
   252  
   253  		// WHEN
   254  		result, err := tenantMappingRepo.Exists(ctx, testID)
   255  
   256  		// THEN
   257  		require.NoError(t, err)
   258  		require.NotNil(t, result)
   259  		assert.True(t, result)
   260  	})
   261  
   262  	t.Run("Error when checking existence", func(t *testing.T) {
   263  		// GIVEN
   264  		db, dbMock := testdb.MockDatabase(t)
   265  		defer dbMock.AssertExpectations(t)
   266  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT 1 FROM public.business_tenant_mappings WHERE id = $1`)).
   267  			WithArgs(testID).
   268  			WillReturnError(testError)
   269  
   270  		ctx := persistence.SaveToContext(context.TODO(), db)
   271  		tenantMappingRepo := tenant.NewRepository(nil)
   272  
   273  		// WHEN
   274  		result, err := tenantMappingRepo.Exists(ctx, testID)
   275  
   276  		// THEN
   277  		require.Error(t, err)
   278  		assert.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   279  		assert.False(t, result)
   280  	})
   281  }
   282  
   283  func TestPgRepository_ExistsByExternalTenant(t *testing.T) {
   284  	t.Run("Success", func(t *testing.T) {
   285  		// GIVEN
   286  		db, dbMock := testdb.MockDatabase(t)
   287  		defer dbMock.AssertExpectations(t)
   288  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT 1 FROM public.business_tenant_mappings WHERE external_tenant = $1`)).
   289  			WithArgs(testExternal).
   290  			WillReturnRows(testdb.RowWhenObjectExist())
   291  
   292  		ctx := persistence.SaveToContext(context.TODO(), db)
   293  		tenantMappingRepo := tenant.NewRepository(nil)
   294  
   295  		// WHEN
   296  		result, err := tenantMappingRepo.ExistsByExternalTenant(ctx, testExternal)
   297  
   298  		// THEN
   299  		require.NoError(t, err)
   300  		require.NotNil(t, result)
   301  		assert.True(t, result)
   302  	})
   303  
   304  	t.Run("Error when checking existence", func(t *testing.T) {
   305  		// GIVEN
   306  		db, dbMock := testdb.MockDatabase(t)
   307  		defer dbMock.AssertExpectations(t)
   308  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT 1 FROM public.business_tenant_mappings WHERE external_tenant = $1`)).
   309  			WithArgs(testExternal).
   310  			WillReturnError(testError)
   311  
   312  		ctx := persistence.SaveToContext(context.TODO(), db)
   313  		tenantMappingRepo := tenant.NewRepository(nil)
   314  
   315  		// WHEN
   316  		result, err := tenantMappingRepo.ExistsByExternalTenant(ctx, testExternal)
   317  
   318  		// THEN
   319  		require.Error(t, err)
   320  		assert.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   321  		assert.False(t, result)
   322  	})
   323  }
   324  
   325  func TestPgRepository_List(t *testing.T) {
   326  	t.Run("Success", func(t *testing.T) {
   327  		// GIVEN
   328  
   329  		initializedVal := true
   330  		notInitializedVal := false
   331  
   332  		tenantModels := []*model.BusinessTenantMapping{
   333  			newModelBusinessTenantMappingWithComputedValues("id1", "name1", &initializedVal),
   334  			newModelBusinessTenantMappingWithComputedValues("id2", "name2", &notInitializedVal),
   335  			newModelBusinessTenantMappingWithComputedValues("id3", "name3", &notInitializedVal),
   336  		}
   337  
   338  		tenantEntities := []*tenantEntity.Entity{
   339  			newEntityBusinessTenantMappingWithComputedValues("id1", "name1", &initializedVal),
   340  			newEntityBusinessTenantMappingWithComputedValues("id2", "name2", &notInitializedVal),
   341  			newEntityBusinessTenantMappingWithComputedValues("id3", "name3", &notInitializedVal),
   342  		}
   343  
   344  		mockConverter := &automock.Converter{}
   345  		defer mockConverter.AssertExpectations(t)
   346  		mockConverter.On("FromEntity", tenantEntities[0]).Return(tenantModels[0]).Once()
   347  		mockConverter.On("FromEntity", tenantEntities[1]).Return(tenantModels[1]).Once()
   348  		mockConverter.On("FromEntity", tenantEntities[2]).Return(tenantModels[2]).Once()
   349  		db, dbMock := testdb.MockDatabase(t)
   350  		defer dbMock.AssertExpectations(t)
   351  
   352  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{
   353  			{sqlRow: sqlRow{id: "id1", name: "name1", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &initializedVal},
   354  			{sqlRow: sqlRow{id: "id2", name: "name2", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &notInitializedVal},
   355  			{sqlRow: sqlRow{id: "id3", name: "name3", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &notInitializedVal},
   356  		})
   357  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT DISTINCT t.id, t.external_name, t.external_tenant, t.parent, t.type, t.provider_name, t.status, ld.tenant_id IS NOT NULL AS initialized FROM public.business_tenant_mappings t LEFT JOIN public.label_definitions ld ON t.id=ld.tenant_id WHERE t.status = $1 ORDER BY initialized DESC, t.external_name ASC`)).
   358  			WithArgs(tenantEntity.Active).
   359  			WillReturnRows(rowsToReturn)
   360  
   361  		ctx := persistence.SaveToContext(context.TODO(), db)
   362  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   363  
   364  		// WHEN
   365  		result, err := tenantMappingRepo.List(ctx)
   366  
   367  		// THEN
   368  		require.NoError(t, err)
   369  		require.NotNil(t, result)
   370  		assert.Equal(t, tenantModels, result)
   371  	})
   372  
   373  	t.Run("Error when listing", func(t *testing.T) {
   374  		// GIVEN
   375  		mockConverter := &automock.Converter{}
   376  		defer mockConverter.AssertExpectations(t)
   377  		db, dbMock := testdb.MockDatabase(t)
   378  		defer dbMock.AssertExpectations(t)
   379  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT DISTINCT t.id, t.external_name, t.external_tenant, t.parent, t.type, t.provider_name, t.status, ld.tenant_id IS NOT NULL AS initialized FROM public.business_tenant_mappings t LEFT JOIN public.label_definitions ld ON t.id=ld.tenant_id WHERE t.status = $1 ORDER BY initialized DESC, t.external_name ASC`)).
   380  			WithArgs(tenantEntity.Active).
   381  			WillReturnError(testError)
   382  
   383  		ctx := persistence.SaveToContext(context.TODO(), db)
   384  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   385  
   386  		// WHEN
   387  		result, err := tenantMappingRepo.List(ctx)
   388  
   389  		// THEN
   390  		require.Error(t, err)
   391  		assert.Contains(t, err.Error(), testError.Error())
   392  		require.Nil(t, result)
   393  	})
   394  
   395  	t.Run("Error when missing persistence context", func(t *testing.T) {
   396  		// GIVEN
   397  		repo := tenant.NewRepository(nil)
   398  		ctx := context.TODO()
   399  
   400  		// WHEN
   401  		_, err := repo.List(ctx)
   402  
   403  		// THEN
   404  		require.EqualError(t, err, "while fetching persistence from context: Internal Server Error: unable to fetch database from context")
   405  	})
   406  }
   407  
   408  func TestPgRepository_ListPageBySearchTerm(t *testing.T) {
   409  	t.Run("Success", func(t *testing.T) {
   410  		// GIVEN
   411  		searchTerm := "name"
   412  		first := 10
   413  		endCursor := ""
   414  		initializedVal := true
   415  		notInitializedVal := false
   416  
   417  		tenantModels := []*model.BusinessTenantMapping{
   418  			newModelBusinessTenantMappingWithComputedValues("id1", "name1", &initializedVal),
   419  			newModelBusinessTenantMappingWithComputedValues("id2", "name2", &notInitializedVal),
   420  			newModelBusinessTenantMappingWithComputedValues("id3", "name3", &notInitializedVal),
   421  		}
   422  
   423  		tenantEntities := []*tenantEntity.Entity{
   424  			newEntityBusinessTenantMappingWithComputedValues("id1", "name1", &initializedVal),
   425  			newEntityBusinessTenantMappingWithComputedValues("id2", "name2", &notInitializedVal),
   426  			newEntityBusinessTenantMappingWithComputedValues("id3", "name3", &notInitializedVal),
   427  		}
   428  
   429  		tenantPage := &model.BusinessTenantMappingPage{
   430  			Data: tenantModels,
   431  			PageInfo: &pagination.Page{
   432  				StartCursor: "",
   433  				EndCursor:   "",
   434  				HasNextPage: false,
   435  			},
   436  			TotalCount: len(tenantModels),
   437  		}
   438  
   439  		mockConverter := &automock.Converter{}
   440  		defer mockConverter.AssertExpectations(t)
   441  		mockConverter.On("FromEntity", tenantEntities[0]).Return(tenantModels[0]).Once()
   442  		mockConverter.On("FromEntity", tenantEntities[1]).Return(tenantModels[1]).Once()
   443  		mockConverter.On("FromEntity", tenantEntities[2]).Return(tenantModels[2]).Once()
   444  		db, dbMock := testdb.MockDatabase(t)
   445  		defer dbMock.AssertExpectations(t)
   446  
   447  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{
   448  			{sqlRow: sqlRow{id: "id1", name: "name1", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &initializedVal},
   449  			{sqlRow: sqlRow{id: "id2", name: "name2", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &notInitializedVal},
   450  			{sqlRow: sqlRow{id: "id3", name: "name3", externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: &notInitializedVal},
   451  		})
   452  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE (status = $1 AND (id::text ILIKE $2 OR external_name ILIKE $3 OR external_tenant ILIKE $4)) ORDER BY external_name LIMIT 10 OFFSET 0`)).
   453  			WithArgs(tenantEntity.Active, "%name%", "%name%", "%name%").
   454  			WillReturnRows(rowsToReturn)
   455  
   456  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM public.business_tenant_mappings WHERE (status = $1 AND (id::text ILIKE $2 OR external_name ILIKE $3 OR external_tenant ILIKE $4))`)).
   457  			WithArgs(tenantEntity.Active, "%name%", "%name%", "%name%").
   458  			WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(3))
   459  
   460  		ctx := persistence.SaveToContext(context.TODO(), db)
   461  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   462  
   463  		// WHEN
   464  		result, err := tenantMappingRepo.ListPageBySearchTerm(ctx, searchTerm, first, endCursor)
   465  
   466  		// THEN
   467  		require.NoError(t, err)
   468  		require.NotNil(t, result)
   469  		assert.Equal(t, tenantPage, result)
   470  	})
   471  
   472  	t.Run("Error when listing", func(t *testing.T) {
   473  		// GIVEN
   474  		searchTerm := "name"
   475  		first := 10
   476  		endCursor := ""
   477  
   478  		mockConverter := &automock.Converter{}
   479  		defer mockConverter.AssertExpectations(t)
   480  		db, dbMock := testdb.MockDatabase(t)
   481  		defer dbMock.AssertExpectations(t)
   482  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE (status = $1 AND (id::text ILIKE $2 OR external_name ILIKE $3 OR external_tenant ILIKE $4)) ORDER BY external_name LIMIT 10 OFFSET 0`)).
   483  			WithArgs(tenantEntity.Active, "%name%", "%name%", "%name%").
   484  			WillReturnError(testError)
   485  
   486  		ctx := persistence.SaveToContext(context.TODO(), db)
   487  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   488  
   489  		// WHEN
   490  		result, err := tenantMappingRepo.ListPageBySearchTerm(ctx, searchTerm, first, endCursor)
   491  
   492  		// THEN
   493  		require.Error(t, err)
   494  		assert.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   495  		require.Nil(t, result)
   496  	})
   497  
   498  	t.Run("Error when missing persistence context", func(t *testing.T) {
   499  		// GIVEN
   500  		searchTerm := "name"
   501  		first := 10
   502  		endCursor := ""
   503  		repo := tenant.NewRepository(nil)
   504  		ctx := context.TODO()
   505  
   506  		// WHEN
   507  		_, err := repo.ListPageBySearchTerm(ctx, searchTerm, first, endCursor)
   508  
   509  		// THEN
   510  		require.EqualError(t, err, "while listing tenants from DB: Internal Server Error: unable to fetch database from context")
   511  	})
   512  }
   513  
   514  func TestPgRepository_ListByExternalTenants(t *testing.T) {
   515  	t.Run("Success", func(t *testing.T) {
   516  		// GIVEN
   517  		initializedVal := true
   518  		tntModel := &model.BusinessTenantMapping{ID: id(), ExternalTenant: id(), Initialized: &initializedVal}
   519  		tntEntity := &tenantEntity.Entity{ID: tntModel.ID, ExternalTenant: tntModel.ExternalTenant, Initialized: &initializedVal}
   520  
   521  		mockConverter := &automock.Converter{}
   522  		defer mockConverter.AssertExpectations(t)
   523  		mockConverter.On("FromEntity", tntEntity).Return(tntModel).Once()
   524  		db, dbMock := testdb.MockDatabase(t)
   525  		defer dbMock.AssertExpectations(t)
   526  
   527  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{{sqlRow: sqlRow{id: tntModel.ID, externalTenant: tntModel.ExternalTenant}, initialized: &initializedVal}})
   528  
   529  		query := `SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant IN ($1)`
   530  
   531  		dbMock.ExpectQuery(regexp.QuoteMeta(query)).
   532  			WithArgs(tntModel.ExternalTenant).
   533  			WillReturnRows(rowsToReturn)
   534  
   535  		ctx := persistence.SaveToContext(context.TODO(), db)
   536  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   537  
   538  		// WHEN
   539  		result, err := tenantMappingRepo.ListByExternalTenants(ctx, []string{tntModel.ExternalTenant})
   540  
   541  		// THEN
   542  		require.NoError(t, err)
   543  		require.NotNil(t, result)
   544  		assert.Equal(t, []*model.BusinessTenantMapping{tntModel}, result)
   545  	})
   546  
   547  	t.Run("Success when high load of tenants is requested", func(t *testing.T) {
   548  		// GIVEN
   549  		initializedVal := true
   550  		tntModel := &model.BusinessTenantMapping{ID: id(), ExternalTenant: id(), Initialized: &initializedVal}
   551  		tntEntity := &tenantEntity.Entity{ID: tntModel.ID, ExternalTenant: tntModel.ExternalTenant, Initialized: &initializedVal}
   552  
   553  		mockConverter := &automock.Converter{}
   554  		defer mockConverter.AssertExpectations(t)
   555  		mockConverter.On("FromEntity", tntEntity).Return(tntModel).Once()
   556  		db, dbMock := testdb.MockDatabase(t)
   557  		defer dbMock.AssertExpectations(t)
   558  
   559  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{{sqlRow: sqlRow{id: tntModel.ID, externalTenant: tntModel.ExternalTenant}, initialized: &initializedVal}})
   560  
   561  		// get first chunk of tenant IDs
   562  		firstChunkIDs := chunkSizedTenantIDs(49999)
   563  		firstChunkIDs = append(firstChunkIDs, tntModel.ExternalTenant)
   564  		firstChunkQuery, firstChunkQueryArgs := buildQueryWithTenantIDs(firstChunkIDs)
   565  		dbMock.ExpectQuery(regexp.QuoteMeta(firstChunkQuery)).
   566  			WithArgs(firstChunkQueryArgs...).
   567  			WillReturnRows(rowsToReturn)
   568  
   569  		// get second chunk of tenant IDs
   570  		secondChunkIDs := chunkSizedTenantIDs(100)
   571  		secondChunkQuery, secondChunkIDsChunkQueryArgs := buildQueryWithTenantIDs(secondChunkIDs)
   572  		dbMock.ExpectQuery(regexp.QuoteMeta(secondChunkQuery)).
   573  			WithArgs(secondChunkIDsChunkQueryArgs...).
   574  			WillReturnRows(fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{}))
   575  
   576  		ctx := persistence.SaveToContext(context.TODO(), db)
   577  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   578  
   579  		// WHEN
   580  		result, err := tenantMappingRepo.ListByExternalTenants(ctx, append(firstChunkIDs, secondChunkIDs...))
   581  
   582  		// THEN
   583  		require.NoError(t, err)
   584  		require.NotNil(t, result)
   585  		assert.Equal(t, []*model.BusinessTenantMapping{tntModel}, result)
   586  	})
   587  
   588  	t.Run("Error when listing", func(t *testing.T) {
   589  		// GIVEN
   590  		externalTenantID := id()
   591  
   592  		mockConverter := &automock.Converter{}
   593  		defer mockConverter.AssertExpectations(t)
   594  		db, dbMock := testdb.MockDatabase(t)
   595  		defer dbMock.AssertExpectations(t)
   596  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant IN ($1)`)).
   597  			WithArgs(externalTenantID).
   598  			WillReturnError(testError)
   599  
   600  		ctx := persistence.SaveToContext(context.TODO(), db)
   601  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   602  
   603  		// WHEN
   604  		result, err := tenantMappingRepo.ListByExternalTenants(ctx, []string{externalTenantID})
   605  
   606  		// THEN
   607  		require.Error(t, err)
   608  		assert.Contains(t, err.Error(), "Unexpected error while executing SQL query")
   609  		require.Nil(t, result)
   610  	})
   611  
   612  	t.Run("Error when missing persistence context", func(t *testing.T) {
   613  		// GIVEN
   614  		repo := tenant.NewRepository(nil)
   615  		ctx := context.TODO()
   616  
   617  		// WHEN
   618  		_, err := repo.ListByExternalTenants(ctx, []string{id()})
   619  
   620  		// THEN
   621  		require.EqualError(t, err, "Internal Server Error: unable to fetch database from context")
   622  	})
   623  }
   624  
   625  func TestPgRepository_ListByParentAndType(t *testing.T) {
   626  	t.Run("Success", func(t *testing.T) {
   627  		// GIVEN
   628  		parentID := "test"
   629  
   630  		resultTntModel := []*model.BusinessTenantMapping{
   631  			newModelBusinessTenantMappingWithParentAndType("id1", "name1", parentID, nil, tenantEntity.Account),
   632  		}
   633  
   634  		tntEntity := newEntityBusinessTenantMappingWithParentAndAccount("id1", "name1", parentID, tenantEntity.Account)
   635  		tntEntity.Initialized = boolToPtr(true)
   636  
   637  		mockConverter := &automock.Converter{}
   638  		mockConverter.On("FromEntity", tntEntity).Return(resultTntModel[0]).Once()
   639  		defer mockConverter.AssertExpectations(t)
   640  
   641  		db, dbMock := testdb.MockDatabase(t)
   642  		defer dbMock.AssertExpectations(t)
   643  
   644  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{
   645  			{sqlRow: sqlRow{id: "id1", name: "name1", externalTenant: testExternal, parent: str.NewNullString(parentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: boolToPtr(true)},
   646  		})
   647  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE parent = $1 AND type = $2`)).
   648  			WithArgs(parentID, tenantEntity.Account).
   649  			WillReturnRows(rowsToReturn)
   650  
   651  		ctx := persistence.SaveToContext(context.TODO(), db)
   652  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   653  
   654  		// WHEN
   655  		result, err := tenantMappingRepo.ListByParentAndType(ctx, parentID, tenantEntity.Account)
   656  
   657  		// THEN
   658  		require.NoError(t, err)
   659  		require.NotNil(t, result)
   660  		assert.Equal(t, resultTntModel, result)
   661  	})
   662  
   663  	t.Run("Error when listing", func(t *testing.T) {
   664  		// GIVEN
   665  		parentID := "test"
   666  
   667  		tntEntity := newEntityBusinessTenantMappingWithParentAndAccount("id1", "name1", parentID, tenantEntity.Account)
   668  		tntEntity.Initialized = boolToPtr(true)
   669  
   670  		mockConverter := &automock.Converter{}
   671  		defer mockConverter.AssertExpectations(t)
   672  
   673  		db, dbMock := testdb.MockDatabase(t)
   674  		defer dbMock.AssertExpectations(t)
   675  
   676  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE parent = $1 AND type = $2`)).
   677  			WithArgs(parentID, tenantEntity.Account).
   678  			WillReturnError(testError)
   679  
   680  		ctx := persistence.SaveToContext(context.TODO(), db)
   681  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   682  
   683  		// WHEN
   684  		result, err := tenantMappingRepo.ListByParentAndType(ctx, parentID, tenantEntity.Account)
   685  
   686  		// THEN
   687  		require.Error(t, err)
   688  		assert.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   689  		require.Nil(t, result)
   690  	})
   691  
   692  	t.Run("Error when missing persistence context", func(t *testing.T) {
   693  		// GIVEN
   694  		parentID := "test"
   695  		repo := tenant.NewRepository(nil)
   696  		ctx := context.TODO()
   697  
   698  		// WHEN
   699  		result, err := repo.ListByParentAndType(ctx, parentID, tenantEntity.Account)
   700  
   701  		// THEN
   702  		require.EqualError(t, err, "Internal Server Error: unable to fetch database from context")
   703  		assert.Nil(t, result)
   704  	})
   705  }
   706  
   707  func TestPgRepository_ListByType(t *testing.T) {
   708  	t.Run("Success", func(t *testing.T) {
   709  		// GIVEN
   710  		parentID := "test"
   711  
   712  		resultTntModel := []*model.BusinessTenantMapping{
   713  			newModelBusinessTenantMappingWithParentAndType("id1", "name1", parentID, nil, tenantEntity.Account),
   714  		}
   715  
   716  		tntEntity := newEntityBusinessTenantMappingWithParentAndAccount("id1", "name1", parentID, tenantEntity.Account)
   717  		tntEntity.Initialized = boolToPtr(true)
   718  
   719  		mockConverter := &automock.Converter{}
   720  		mockConverter.On("FromEntity", tntEntity).Return(resultTntModel[0]).Once()
   721  		defer mockConverter.AssertExpectations(t)
   722  
   723  		db, dbMock := testdb.MockDatabase(t)
   724  		defer dbMock.AssertExpectations(t)
   725  
   726  		rowsToReturn := fixSQLRowsWithComputedValues([]sqlRowWithComputedValues{
   727  			{sqlRow: sqlRow{id: "id1", name: "name1", externalTenant: testExternal, parent: str.NewNullString(parentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active}, initialized: boolToPtr(true)},
   728  		})
   729  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE type = $1`)).
   730  			WithArgs(tenantEntity.Account).
   731  			WillReturnRows(rowsToReturn)
   732  
   733  		ctx := persistence.SaveToContext(context.TODO(), db)
   734  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   735  
   736  		// WHEN
   737  		result, err := tenantMappingRepo.ListByType(ctx, tenantEntity.Account)
   738  
   739  		// THEN
   740  		require.NoError(t, err)
   741  		require.NotNil(t, result)
   742  		assert.Equal(t, resultTntModel, result)
   743  	})
   744  
   745  	t.Run("Error when listing", func(t *testing.T) {
   746  		// GIVEN
   747  		parentID := "test"
   748  
   749  		tntEntity := newEntityBusinessTenantMappingWithParentAndAccount("id1", "name1", parentID, tenantEntity.Account)
   750  		tntEntity.Initialized = boolToPtr(true)
   751  
   752  		mockConverter := &automock.Converter{}
   753  		defer mockConverter.AssertExpectations(t)
   754  
   755  		db, dbMock := testdb.MockDatabase(t)
   756  		defer dbMock.AssertExpectations(t)
   757  
   758  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE type = $1`)).
   759  			WithArgs(tenantEntity.Account).
   760  			WillReturnError(testError)
   761  
   762  		ctx := persistence.SaveToContext(context.TODO(), db)
   763  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   764  
   765  		// WHEN
   766  		result, err := tenantMappingRepo.ListByType(ctx, tenantEntity.Account)
   767  
   768  		// THEN
   769  		require.Error(t, err)
   770  		assert.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   771  		require.Nil(t, result)
   772  	})
   773  
   774  	t.Run("Error when missing persistence context", func(t *testing.T) {
   775  		// GIVEN
   776  		repo := tenant.NewRepository(nil)
   777  		ctx := context.TODO()
   778  
   779  		// WHEN
   780  		result, err := repo.ListByType(ctx, tenantEntity.Account)
   781  
   782  		// THEN
   783  		require.EqualError(t, err, "Internal Server Error: unable to fetch database from context")
   784  		assert.Nil(t, result)
   785  	})
   786  }
   787  func buildQueryWithTenantIDs(ids []string) (string, []driver.Value) {
   788  	argumentValues := make([]driver.Value, 0)
   789  	var sb strings.Builder
   790  	for i, id := range ids {
   791  		argumentValues = append(argumentValues, id)
   792  		sb.WriteString(fmt.Sprintf("$%d", i+1))
   793  		if i < len(ids)-1 {
   794  			sb.WriteString(", ")
   795  		}
   796  	}
   797  
   798  	queryFormat := `SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant IN (%s)`
   799  	query := fmt.Sprintf(queryFormat, sb.String())
   800  
   801  	return query, argumentValues
   802  }
   803  
   804  func chunkSizedTenantIDs(chunkSize int) []string {
   805  	ids := make([]string, chunkSize)
   806  	for i := 0; i < chunkSize; i++ {
   807  		ids[i] = id()
   808  	}
   809  	return ids
   810  }
   811  
   812  func TestPgRepository_Update(t *testing.T) {
   813  	t.Run("Success", func(t *testing.T) {
   814  		// GIVEN
   815  		tenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   816  		tenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
   817  
   818  		mockConverter := &automock.Converter{}
   819  		mockConverter.On("ToEntity", tenantMappingModel).Return(tenantMappingEntity).Once()
   820  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
   821  
   822  		db, dbMock := testdb.MockDatabase(t)
   823  
   824  		rowsToReturn := fixSQLRows([]sqlRow{
   825  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   826  		})
   827  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   828  			WithArgs(testID, tenantEntity.Inactive).
   829  			WillReturnRows(rowsToReturn)
   830  
   831  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
   832  			WithArgs(testName, testExternal, testParentID, "account", "Compass", tenantEntity.Active, testID).
   833  			WillReturnResult(sqlmock.NewResult(-1, 1))
   834  
   835  		ctx := persistence.SaveToContext(context.TODO(), db)
   836  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   837  
   838  		// WHEN
   839  		err := tenantMappingRepo.Update(ctx, tenantMappingModel)
   840  
   841  		// THEN
   842  		require.NoError(t, err)
   843  		mockConverter.AssertExpectations(t)
   844  		dbMock.AssertExpectations(t)
   845  	})
   846  
   847  	t.Run("Error when getting", func(t *testing.T) {
   848  		// GIVEN
   849  		tenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   850  
   851  		db, dbMock := testdb.MockDatabase(t)
   852  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   853  			WithArgs(testID, tenantEntity.Inactive).
   854  			WillReturnError(testError)
   855  
   856  		ctx := persistence.SaveToContext(context.TODO(), db)
   857  		tenantMappingRepo := tenant.NewRepository(nil)
   858  
   859  		// WHEN
   860  		err := tenantMappingRepo.Update(ctx, tenantMappingModel)
   861  
   862  		// THEN
   863  		require.Error(t, err)
   864  		assert.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   865  		dbMock.AssertExpectations(t)
   866  	})
   867  
   868  	t.Run("Error when updating", func(t *testing.T) {
   869  		// GIVEN
   870  		tenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   871  		tenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
   872  
   873  		mockConverter := &automock.Converter{}
   874  		mockConverter.On("ToEntity", tenantMappingModel).Return(tenantMappingEntity).Once()
   875  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
   876  
   877  		db, dbMock := testdb.MockDatabase(t)
   878  
   879  		rowsToReturn := fixSQLRows([]sqlRow{
   880  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   881  		})
   882  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   883  			WithArgs(testID, tenantEntity.Inactive).
   884  			WillReturnRows(rowsToReturn)
   885  
   886  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
   887  			WithArgs(testName, testExternal, testParentID, "account", "Compass", tenantEntity.Active, testID).
   888  			WillReturnError(testError)
   889  
   890  		ctx := persistence.SaveToContext(context.TODO(), db)
   891  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   892  
   893  		// WHEN
   894  		err := tenantMappingRepo.Update(ctx, tenantMappingModel)
   895  
   896  		// THEN
   897  		require.Error(t, err)
   898  		assert.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   899  		mockConverter.AssertExpectations(t)
   900  		dbMock.AssertExpectations(t)
   901  	})
   902  
   903  	t.Run("Success when parent is updated", func(t *testing.T) {
   904  		// GIVEN
   905  		oldTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   906  		newTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID2, nil, tenantEntity.Account)
   907  		oldTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
   908  		newTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID2)
   909  
   910  		mockConverter := &automock.Converter{}
   911  		mockConverter.On("ToEntity", newTenantMappingModel).Return(newTenantMappingEntity).Once()
   912  		mockConverter.On("FromEntity", oldTenantMappingEntity).Return(oldTenantMappingModel).Once()
   913  
   914  		db, dbMock := testdb.MockDatabase(t)
   915  
   916  		rowsToReturn := fixSQLRows([]sqlRow{
   917  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   918  		})
   919  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   920  			WithArgs(testID, tenantEntity.Inactive).
   921  			WillReturnRows(rowsToReturn)
   922  
   923  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
   924  			WithArgs(testName, testExternal, testParentID2, "account", "Compass", tenantEntity.Active, testID).
   925  			WillReturnResult(sqlmock.NewResult(-1, 1))
   926  
   927  		for topLvlEntity := range resource.TopLevelEntities {
   928  			if _, ok := topLvlEntity.IgnoredTenantAccessTable(); ok {
   929  				continue
   930  			}
   931  			tenantAccesses := fixTenantAccesses()
   932  
   933  			dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
   934  				WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
   935  
   936  			dbMock.ExpectExec(`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_id, id, owner \) \(SELECT parents\.id AS tenant_id, \? as id, \? AS owner FROM parents\)`).
   937  				WithArgs(testParentID2, tenantAccesses[0].ResourceID, true).WillReturnResult(sqlmock.NewResult(-1, 1))
   938  
   939  			dbMock.ExpectExec(`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 (.+) WHERE id IN \(\$2\) AND tenant_id IN \(SELECT id FROM parents\)`).
   940  				WithArgs(testParentID, tenantAccesses[0].ResourceID).WillReturnResult(sqlmock.NewResult(-1, 1))
   941  		}
   942  
   943  		ctx := persistence.SaveToContext(context.TODO(), db)
   944  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   945  
   946  		// WHEN
   947  		err := tenantMappingRepo.Update(ctx, newTenantMappingModel)
   948  
   949  		// THEN
   950  		require.NoError(t, err)
   951  		mockConverter.AssertExpectations(t)
   952  		dbMock.AssertExpectations(t)
   953  	})
   954  
   955  	t.Run("Error when parent is updated and list tenant accesses fail", func(t *testing.T) {
   956  		// GIVEN
   957  		oldTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   958  		newTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID2, nil, tenantEntity.Account)
   959  		oldTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
   960  		newTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID2)
   961  
   962  		mockConverter := &automock.Converter{}
   963  		mockConverter.On("ToEntity", newTenantMappingModel).Return(newTenantMappingEntity).Once()
   964  		mockConverter.On("FromEntity", oldTenantMappingEntity).Return(oldTenantMappingModel).Once()
   965  
   966  		db, dbMock := testdb.MockDatabase(t)
   967  
   968  		rowsToReturn := fixSQLRows([]sqlRow{
   969  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
   970  		})
   971  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
   972  			WithArgs(testID, tenantEntity.Inactive).
   973  			WillReturnRows(rowsToReturn)
   974  
   975  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
   976  			WithArgs(testName, testExternal, testParentID2, "account", "Compass", tenantEntity.Active, testID).
   977  			WillReturnResult(sqlmock.NewResult(-1, 1))
   978  
   979  		dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
   980  			WithArgs(testID, true).WillReturnError(testError)
   981  
   982  		ctx := persistence.SaveToContext(context.TODO(), db)
   983  		tenantMappingRepo := tenant.NewRepository(mockConverter)
   984  
   985  		// WHEN
   986  		err := tenantMappingRepo.Update(ctx, newTenantMappingModel)
   987  
   988  		// THEN
   989  		require.Error(t, err)
   990  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
   991  		mockConverter.AssertExpectations(t)
   992  		dbMock.AssertExpectations(t)
   993  	})
   994  
   995  	t.Run("Error when parent is updated and create tenant access fail", func(t *testing.T) {
   996  		// GIVEN
   997  		oldTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
   998  		newTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID2, nil, tenantEntity.Account)
   999  		oldTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
  1000  		newTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID2)
  1001  
  1002  		mockConverter := &automock.Converter{}
  1003  		mockConverter.On("ToEntity", newTenantMappingModel).Return(newTenantMappingEntity).Once()
  1004  		mockConverter.On("FromEntity", oldTenantMappingEntity).Return(oldTenantMappingModel).Once()
  1005  
  1006  		db, dbMock := testdb.MockDatabase(t)
  1007  
  1008  		rowsToReturn := fixSQLRows([]sqlRow{
  1009  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1010  		})
  1011  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
  1012  			WithArgs(testID, tenantEntity.Inactive).
  1013  			WillReturnRows(rowsToReturn)
  1014  
  1015  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
  1016  			WithArgs(testName, testExternal, testParentID2, "account", "Compass", tenantEntity.Active, testID).
  1017  			WillReturnResult(sqlmock.NewResult(-1, 1))
  1018  
  1019  		appTenantAccesses := fixTenantAccesses()
  1020  		dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1021  			WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
  1022  
  1023  		dbMock.ExpectExec(`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_id, id, owner \) \(SELECT parents\.id AS tenant_id, \? as id, \? AS owner FROM parents\)`).
  1024  			WithArgs(testParentID2, appTenantAccesses[0].ResourceID, true).WillReturnError(testError)
  1025  
  1026  		ctx := persistence.SaveToContext(context.TODO(), db)
  1027  		tenantMappingRepo := tenant.NewRepository(mockConverter)
  1028  
  1029  		// WHEN
  1030  		err := tenantMappingRepo.Update(ctx, newTenantMappingModel)
  1031  
  1032  		// THEN
  1033  		require.Error(t, err)
  1034  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1035  		mockConverter.AssertExpectations(t)
  1036  		dbMock.AssertExpectations(t)
  1037  	})
  1038  
  1039  	t.Run("Error when parent is updated and tenant access delete fail", func(t *testing.T) {
  1040  		// GIVEN
  1041  		oldTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID, nil, tenantEntity.Account)
  1042  		newTenantMappingModel := newModelBusinessTenantMappingWithType(testID, testName, testParentID2, nil, tenantEntity.Account)
  1043  		oldTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID)
  1044  		newTenantMappingEntity := newEntityBusinessTenantMappingWithParent(testID, testName, testParentID2)
  1045  
  1046  		mockConverter := &automock.Converter{}
  1047  		mockConverter.On("ToEntity", newTenantMappingModel).Return(newTenantMappingEntity).Once()
  1048  		mockConverter.On("FromEntity", oldTenantMappingEntity).Return(oldTenantMappingModel).Once()
  1049  
  1050  		db, dbMock := testdb.MockDatabase(t)
  1051  
  1052  		rowsToReturn := fixSQLRows([]sqlRow{
  1053  			{id: testID, name: testName, externalTenant: testExternal, parent: repo.NewValidNullableString(testParentID), typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1054  		})
  1055  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE id = $1 AND status != $2 `)).
  1056  			WithArgs(testID, tenantEntity.Inactive).
  1057  			WillReturnRows(rowsToReturn)
  1058  
  1059  		dbMock.ExpectExec(regexp.QuoteMeta(`UPDATE public.business_tenant_mappings SET external_name = ?, external_tenant = ?, parent = ?, type = ?, provider_name = ?, status = ? WHERE id = ? `)).
  1060  			WithArgs(testName, testExternal, testParentID2, "account", "Compass", tenantEntity.Active, testID).
  1061  			WillReturnResult(sqlmock.NewResult(-1, 1))
  1062  
  1063  		appTenantAccesses := fixTenantAccesses()
  1064  		dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1065  			WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
  1066  
  1067  		dbMock.ExpectExec(`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_id, id, owner \) \(SELECT parents\.id AS tenant_id, \? as id, \? AS owner FROM parents\)`).
  1068  			WithArgs(testParentID2, appTenantAccesses[0].ResourceID, true).WillReturnResult(sqlmock.NewResult(-1, 1))
  1069  
  1070  		dbMock.ExpectExec(`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 (.+) WHERE id IN \(\$2\) AND tenant_id IN \(SELECT id FROM parents\)`).
  1071  			WithArgs(testParentID, appTenantAccesses[0].ResourceID).WillReturnError(testError)
  1072  
  1073  		ctx := persistence.SaveToContext(context.TODO(), db)
  1074  		tenantMappingRepo := tenant.NewRepository(mockConverter)
  1075  
  1076  		// WHEN
  1077  		err := tenantMappingRepo.Update(ctx, newTenantMappingModel)
  1078  
  1079  		// THEN
  1080  		require.Error(t, err)
  1081  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1082  		mockConverter.AssertExpectations(t)
  1083  		dbMock.AssertExpectations(t)
  1084  	})
  1085  }
  1086  
  1087  func TestPgRepository_DeleteByExternalTenant(t *testing.T) {
  1088  	deleteStatement := regexp.QuoteMeta(`DELETE FROM public.business_tenant_mappings WHERE external_tenant = $1`)
  1089  
  1090  	t.Run("Success", func(t *testing.T) {
  1091  		// GIVEN
  1092  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
  1093  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
  1094  
  1095  		mockConverter := &automock.Converter{}
  1096  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
  1097  
  1098  		db, dbMock := testdb.MockDatabase(t)
  1099  		rowsToReturn := fixSQLRows([]sqlRow{
  1100  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1101  		})
  1102  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1103  			WithArgs(testExternal, tenantEntity.Inactive).
  1104  			WillReturnRows(rowsToReturn)
  1105  
  1106  		for topLvlEntity := range resource.TopLevelEntities {
  1107  			if _, ok := topLvlEntity.IgnoredTenantAccessTable(); ok {
  1108  				continue
  1109  			}
  1110  			tenantAccesses := fixTenantAccesses()
  1111  
  1112  			dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1113  				WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
  1114  
  1115  			dbMock.ExpectExec(`DELETE FROM (.+) WHERE id IN \(\$1\)`).
  1116  				WithArgs(tenantAccesses[0].ResourceID).WillReturnResult(sqlmock.NewResult(-1, 1))
  1117  		}
  1118  
  1119  		dbMock.ExpectExec(deleteStatement).
  1120  			WithArgs(testExternal).
  1121  			WillReturnResult(sqlmock.NewResult(-1, 1))
  1122  
  1123  		ctx := persistence.SaveToContext(context.TODO(), db)
  1124  		repo := tenant.NewRepository(mockConverter)
  1125  
  1126  		// WHEN
  1127  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1128  
  1129  		// THEN
  1130  		require.NoError(t, err)
  1131  		dbMock.AssertExpectations(t)
  1132  		mockConverter.AssertExpectations(t)
  1133  	})
  1134  
  1135  	t.Run("Success when getting tenant before delete returns not found", func(t *testing.T) {
  1136  		// GIVEN
  1137  		db, dbMock := testdb.MockDatabase(t)
  1138  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1139  			WithArgs(testExternal, tenantEntity.Inactive).
  1140  			WillReturnError(sql.ErrNoRows)
  1141  
  1142  		ctx := persistence.SaveToContext(context.TODO(), db)
  1143  		repo := tenant.NewRepository(nil)
  1144  
  1145  		// WHEN
  1146  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1147  
  1148  		// THEN
  1149  		require.NoError(t, err)
  1150  		dbMock.AssertExpectations(t)
  1151  	})
  1152  
  1153  	t.Run("Error when getting tenant before delete fail", func(t *testing.T) {
  1154  		// GIVEN
  1155  		db, dbMock := testdb.MockDatabase(t)
  1156  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1157  			WithArgs(testExternal, tenantEntity.Inactive).
  1158  			WillReturnError(testError)
  1159  
  1160  		ctx := persistence.SaveToContext(context.TODO(), db)
  1161  		repo := tenant.NewRepository(nil)
  1162  
  1163  		// WHEN
  1164  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1165  
  1166  		// THEN
  1167  		require.Error(t, err)
  1168  		require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
  1169  		dbMock.AssertExpectations(t)
  1170  	})
  1171  
  1172  	t.Run("Error when List tenant access fail", func(t *testing.T) {
  1173  		// GIVEN
  1174  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
  1175  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
  1176  
  1177  		mockConverter := &automock.Converter{}
  1178  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
  1179  
  1180  		db, dbMock := testdb.MockDatabase(t)
  1181  		rowsToReturn := fixSQLRows([]sqlRow{
  1182  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1183  		})
  1184  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1185  			WithArgs(testExternal, tenantEntity.Inactive).
  1186  			WillReturnRows(rowsToReturn)
  1187  
  1188  		dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1189  			WithArgs(testID, true).WillReturnError(testError)
  1190  
  1191  		ctx := persistence.SaveToContext(context.TODO(), db)
  1192  		repo := tenant.NewRepository(mockConverter)
  1193  
  1194  		// WHEN
  1195  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1196  
  1197  		// THEN
  1198  		require.Error(t, err)
  1199  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1200  		dbMock.AssertExpectations(t)
  1201  		mockConverter.AssertExpectations(t)
  1202  	})
  1203  
  1204  	t.Run("Error when Delete tenant access fail", func(t *testing.T) {
  1205  		// GIVEN
  1206  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
  1207  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
  1208  
  1209  		mockConverter := &automock.Converter{}
  1210  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
  1211  
  1212  		db, dbMock := testdb.MockDatabase(t)
  1213  		rowsToReturn := fixSQLRows([]sqlRow{
  1214  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1215  		})
  1216  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1217  			WithArgs(testExternal, tenantEntity.Inactive).
  1218  			WillReturnRows(rowsToReturn)
  1219  
  1220  		appTenantAccesses := fixTenantAccesses()
  1221  		dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1222  			WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
  1223  
  1224  		dbMock.ExpectExec(`DELETE FROM (.+) WHERE id IN \(\$1\)`).
  1225  			WithArgs(appTenantAccesses[0].ResourceID).WillReturnError(testError)
  1226  
  1227  		ctx := persistence.SaveToContext(context.TODO(), db)
  1228  		repo := tenant.NewRepository(mockConverter)
  1229  
  1230  		// WHEN
  1231  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1232  
  1233  		// THEN
  1234  		require.Error(t, err)
  1235  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1236  		dbMock.AssertExpectations(t)
  1237  		mockConverter.AssertExpectations(t)
  1238  	})
  1239  
  1240  	t.Run("Error when delete fails", func(t *testing.T) {
  1241  		// GIVEN
  1242  		tenantMappingModel := newModelBusinessTenantMapping(testID, testName)
  1243  		tenantMappingEntity := newEntityBusinessTenantMapping(testID, testName)
  1244  
  1245  		mockConverter := &automock.Converter{}
  1246  		mockConverter.On("FromEntity", tenantMappingEntity).Return(tenantMappingModel).Once()
  1247  
  1248  		db, dbMock := testdb.MockDatabase(t)
  1249  		rowsToReturn := fixSQLRows([]sqlRow{
  1250  			{id: testID, name: testName, externalTenant: testExternal, parent: sql.NullString{}, typeRow: string(tenantEntity.Account), provider: "Compass", status: tenantEntity.Active},
  1251  		})
  1252  		dbMock.ExpectQuery(regexp.QuoteMeta(`SELECT id, external_name, external_tenant, parent, type, provider_name, status FROM public.business_tenant_mappings WHERE external_tenant = $1 AND status != $2 `)).
  1253  			WithArgs(testExternal, tenantEntity.Inactive).
  1254  			WillReturnRows(rowsToReturn)
  1255  
  1256  		for topLvlEntity := range resource.TopLevelEntities {
  1257  			if _, ok := topLvlEntity.IgnoredTenantAccessTable(); ok {
  1258  				continue
  1259  			}
  1260  			tenantAccesses := fixTenantAccesses()
  1261  
  1262  			dbMock.ExpectQuery(`SELECT tenant_id, id, owner FROM (.+) WHERE tenant_id = \$1 AND owner = \$2`).
  1263  				WithArgs(testID, true).WillReturnRows(sqlmock.NewRows(repo.M2MColumns).AddRow(fixTenantAccessesRow()...))
  1264  
  1265  			dbMock.ExpectExec(`DELETE FROM (.+) WHERE id IN \(\$1\)`).
  1266  				WithArgs(tenantAccesses[0].ResourceID).WillReturnResult(sqlmock.NewResult(-1, 1))
  1267  		}
  1268  
  1269  		dbMock.ExpectExec(deleteStatement).
  1270  			WithArgs(testExternal).WillReturnError(testError)
  1271  
  1272  		ctx := persistence.SaveToContext(context.TODO(), db)
  1273  		repo := tenant.NewRepository(mockConverter)
  1274  
  1275  		// WHEN
  1276  		err := repo.DeleteByExternalTenant(ctx, testExternal)
  1277  
  1278  		// THEN
  1279  		require.Error(t, err)
  1280  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1281  		dbMock.AssertExpectations(t)
  1282  		mockConverter.AssertExpectations(t)
  1283  	})
  1284  }
  1285  
  1286  func TestPgRepository_GetLowestOwnerForResource(t *testing.T) {
  1287  	runtimeID := "runtimeID"
  1288  
  1289  	t.Run("Success", func(t *testing.T) {
  1290  		db, dbMock := mockDBSuccess(t, runtimeID)
  1291  		defer dbMock.AssertExpectations(t)
  1292  
  1293  		ctx := persistence.SaveToContext(context.TODO(), db)
  1294  		tenantMappingRepo := tenant.NewRepository(nil)
  1295  
  1296  		// WHEN
  1297  		result, err := tenantMappingRepo.GetLowestOwnerForResource(ctx, resource.Runtime, runtimeID)
  1298  
  1299  		// THEN
  1300  		require.NoError(t, err)
  1301  		require.Equal(t, testID, result)
  1302  	})
  1303  
  1304  	t.Run("Error when getting", func(t *testing.T) {
  1305  		db, dbMock := mockDBError(t, runtimeID)
  1306  		defer dbMock.AssertExpectations(t)
  1307  
  1308  		ctx := persistence.SaveToContext(context.TODO(), db)
  1309  		tenantMappingRepo := tenant.NewRepository(nil)
  1310  
  1311  		// WHEN
  1312  		result, err := tenantMappingRepo.GetLowestOwnerForResource(ctx, resource.Runtime, runtimeID)
  1313  
  1314  		// THEN
  1315  		require.Error(t, err)
  1316  		require.Empty(t, result)
  1317  	})
  1318  }
  1319  
  1320  func TestPgRepository_GetCustomerIDParentRecursively(t *testing.T) {
  1321  	dbQuery := `WITH RECURSIVE parents AS
  1322                     (SELECT t1.id, t1.parent, t1.external_tenant, t1.type
  1323                      FROM business_tenant_mappings t1
  1324                      WHERE id = $1
  1325                      UNION ALL
  1326                      SELECT t2.id, t2.parent, t2.external_tenant, t2.type
  1327                      FROM business_tenant_mappings t2
  1328                               INNER JOIN parents p on p.parent = t2.id)
  1329  			SELECT external_tenant, type FROM parents WHERE parent is null`
  1330  
  1331  	t.Run("Success when parent and type are returned", func(t *testing.T) {
  1332  		// GIVEN
  1333  		db, dbMock := testdb.MockDatabase(t)
  1334  
  1335  		rowsToReturn := sqlmock.NewRows([]string{"external_tenant", "type"}).AddRow(testParentID, tenantEntity.TypeToStr(tenantEntity.Customer))
  1336  		dbMock.ExpectQuery(regexp.QuoteMeta(dbQuery)).
  1337  			WithArgs(testID).
  1338  			WillReturnRows(rowsToReturn)
  1339  
  1340  		ctx := persistence.SaveToContext(context.TODO(), db)
  1341  		tenantMappingRepo := tenant.NewRepository(nil)
  1342  
  1343  		// WHEN
  1344  		customerID, err := tenantMappingRepo.GetCustomerIDParentRecursively(ctx, testID)
  1345  
  1346  		// THEN
  1347  		require.NoError(t, err)
  1348  		require.Equal(t, customerID, testParentID)
  1349  		dbMock.AssertExpectations(t)
  1350  	})
  1351  
  1352  	t.Run("Error when executing db query", func(t *testing.T) {
  1353  		// GIVEN
  1354  		db, dbMock := testdb.MockDatabase(t)
  1355  		dbMock.ExpectQuery(regexp.QuoteMeta(dbQuery)).
  1356  			WithArgs(testID).WillReturnError(testError)
  1357  
  1358  		ctx := persistence.SaveToContext(context.TODO(), db)
  1359  		tenantMappingRepo := tenant.NewRepository(nil)
  1360  
  1361  		// WHEN
  1362  		customerID, err := tenantMappingRepo.GetCustomerIDParentRecursively(ctx, testID)
  1363  
  1364  		// THEN
  1365  		require.Error(t, err)
  1366  		require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query")
  1367  		require.Empty(t, customerID)
  1368  		dbMock.AssertExpectations(t)
  1369  	})
  1370  
  1371  	t.Run("Error if missing persistence context", func(t *testing.T) {
  1372  		// GIVEN
  1373  		ctx := context.TODO()
  1374  		tenantMappingRepo := tenant.NewRepository(nil)
  1375  		// WHEN
  1376  		_, err := tenantMappingRepo.GetCustomerIDParentRecursively(ctx, testID)
  1377  		// THEN
  1378  		require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error())
  1379  	})
  1380  
  1381  	t.Run("Return empty string when returned type is not customer", func(t *testing.T) {
  1382  		// GIVEN
  1383  		db, dbMock := testdb.MockDatabase(t)
  1384  
  1385  		rowsToReturn := sqlmock.NewRows([]string{"external_tenant", "type"}).AddRow(testParentID, tenantEntity.TypeToStr(tenantEntity.Account))
  1386  		dbMock.ExpectQuery(regexp.QuoteMeta(dbQuery)).
  1387  			WithArgs(testID).
  1388  			WillReturnRows(rowsToReturn)
  1389  
  1390  		ctx := persistence.SaveToContext(context.TODO(), db)
  1391  		tenantMappingRepo := tenant.NewRepository(nil)
  1392  
  1393  		// WHEN
  1394  		customerID, err := tenantMappingRepo.GetCustomerIDParentRecursively(ctx, testID)
  1395  
  1396  		// THEN
  1397  		require.NoError(t, err)
  1398  		require.Empty(t, customerID)
  1399  		dbMock.AssertExpectations(t)
  1400  	})
  1401  
  1402  	t.Run("Error when empty parent is returned", func(t *testing.T) {
  1403  		// GIVEN
  1404  		db, dbMock := testdb.MockDatabase(t)
  1405  
  1406  		rowsToReturn := sqlmock.NewRows([]string{"external_tenant", "type"}).AddRow("", tenantEntity.TypeToStr(tenantEntity.Customer))
  1407  		dbMock.ExpectQuery(regexp.QuoteMeta(dbQuery)).
  1408  			WithArgs(testID).
  1409  			WillReturnRows(rowsToReturn)
  1410  
  1411  		ctx := persistence.SaveToContext(context.TODO(), db)
  1412  		tenantMappingRepo := tenant.NewRepository(nil)
  1413  
  1414  		// WHEN
  1415  		customerID, err := tenantMappingRepo.GetCustomerIDParentRecursively(ctx, testID)
  1416  
  1417  		// THEN
  1418  		expectedError := fmt.Sprintf("external parent customer ID for internal tenant ID: %s can not be empty", testID)
  1419  		require.Error(t, err)
  1420  		require.EqualError(t, err, expectedError)
  1421  		require.Empty(t, customerID)
  1422  		dbMock.AssertExpectations(t)
  1423  	})
  1424  }
  1425  
  1426  const selectTenantsQuery = `(SELECT tenant_id FROM tenant_runtimes ta WHERE ta.id = $1 AND ta.owner = true AND (NOT EXISTS(SELECT 1 FROM public.business_tenant_mappings WHERE parent = ta.tenant_id) OR (NOT EXISTS(SELECT 1 FROM tenant_runtimes ta2 WHERE ta2.id = $2 AND ta2.owner = true AND ta2.tenant_id IN (SELECT id FROM public.business_tenant_mappings WHERE parent = ta.tenant_id)))))`
  1427  
  1428  func mockDBSuccess(t *testing.T, runtimeID string) (*sqlx.DB, testdb.DBMock) {
  1429  	db, dbMock := testdb.MockDatabase(t)
  1430  	rowsToReturn := sqlmock.NewRows([]string{"tenant_id"}).AddRow(testID)
  1431  	dbMock.ExpectQuery(regexp.QuoteMeta(selectTenantsQuery)).
  1432  		WithArgs(runtimeID, runtimeID).
  1433  		WillReturnRows(rowsToReturn)
  1434  	return db, dbMock
  1435  }
  1436  
  1437  func mockDBError(t *testing.T, runtimeID string) (*sqlx.DB, testdb.DBMock) {
  1438  	db, dbMock := testdb.MockDatabase(t)
  1439  	dbMock.ExpectQuery(regexp.QuoteMeta(selectTenantsQuery)).
  1440  		WithArgs(runtimeID, runtimeID).WillReturnError(testError)
  1441  	return db, dbMock
  1442  }
  1443  
  1444  func id() string {
  1445  	return uuid.New().String()
  1446  }