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

     1  package repo_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/DATA-DOG/go-sqlmock"
    12  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    13  	"github.com/kyma-incubator/compass/components/director/internal/repo/testdb"
    14  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    15  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    16  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    17  	"github.com/lib/pq"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestUpdaterGlobal(t *testing.T) {
    23  	t.Run("Global Update", func(t *testing.T) {
    24  		sut := repo.NewUpdaterGlobal(UserType, "users", []string{"first_name", "last_name", "age"}, []string{"id"})
    25  		givenUser := User{
    26  			ID:        "given_id",
    27  			FirstName: "given_first_name",
    28  			LastName:  "given_last_name",
    29  			Age:       55,
    30  		}
    31  
    32  		t.Run("success", func(t *testing.T) {
    33  			// GIVEN
    34  			db, mock := testdb.MockDatabase(t)
    35  			ctx := persistence.SaveToContext(context.TODO(), db)
    36  			defer mock.AssertExpectations(t)
    37  
    38  			mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ? WHERE id = ?")).
    39  				WithArgs("given_first_name", "given_last_name", 55, "given_id").WillReturnResult(sqlmock.NewResult(0, 1))
    40  			// WHEN
    41  			err := sut.UpdateSingleGlobal(ctx, givenUser)
    42  			// THEN
    43  			require.NoError(t, err)
    44  		})
    45  
    46  		t.Run("success when no id column", func(t *testing.T) {
    47  			// GIVEN
    48  			sut := repo.NewUpdaterGlobal(UserType, "users", []string{"first_name", "last_name", "age"}, []string{})
    49  			db, mock := testdb.MockDatabase(t)
    50  			ctx := persistence.SaveToContext(context.TODO(), db)
    51  			defer mock.AssertExpectations(t)
    52  
    53  			mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ?")).
    54  				WithArgs("given_first_name", "given_last_name", 55).WillReturnResult(sqlmock.NewResult(0, 1))
    55  			// WHEN
    56  			err := sut.UpdateSingleGlobal(ctx, givenUser)
    57  			// THEN
    58  			require.NoError(t, err)
    59  		})
    60  
    61  		t.Run("returns error when operation on db failed", func(t *testing.T) {
    62  			// GIVEN
    63  			db, mock := testdb.MockDatabase(t)
    64  			ctx := persistence.SaveToContext(context.TODO(), db)
    65  			defer mock.AssertExpectations(t)
    66  			mock.ExpectExec("UPDATE users .*").
    67  				WillReturnError(someError())
    68  			// WHEN
    69  			err := sut.UpdateSingleGlobal(ctx, givenUser)
    70  			// THEN
    71  			require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
    72  		})
    73  
    74  		t.Run("returns non unique error", func(t *testing.T) {
    75  			// GIVEN
    76  			db, mock := testdb.MockDatabase(t)
    77  			ctx := persistence.SaveToContext(context.TODO(), db)
    78  			defer mock.AssertExpectations(t)
    79  			mock.ExpectExec("UPDATE users .*").
    80  				WillReturnError(&pq.Error{Code: persistence.UniqueViolation})
    81  			// WHEN
    82  			err := sut.UpdateSingleGlobal(ctx, givenUser)
    83  			// THEN
    84  			require.True(t, apperrors.IsNotUniqueError(err))
    85  		})
    86  
    87  		t.Run("returns error if modified more than one row", func(t *testing.T) {
    88  			// GIVEN
    89  			db, mock := testdb.MockDatabase(t)
    90  			ctx := persistence.SaveToContext(context.TODO(), db)
    91  			defer mock.AssertExpectations(t)
    92  
    93  			mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ? WHERE id = ?")).
    94  				WithArgs("given_first_name", "given_last_name", 55, "given_id").WillReturnResult(sqlmock.NewResult(0, 157))
    95  			// WHEN
    96  			err := sut.UpdateSingleGlobal(ctx, givenUser)
    97  			// THEN
    98  			require.Error(t, err)
    99  			assert.Contains(t, err.Error(), "should update single row, but updated 157 rows")
   100  		})
   101  
   102  		t.Run("returns error if does not modified any row", func(t *testing.T) {
   103  			// GIVEN
   104  			db, mock := testdb.MockDatabase(t)
   105  			ctx := persistence.SaveToContext(context.TODO(), db)
   106  			defer mock.AssertExpectations(t)
   107  
   108  			mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ? WHERE id = ?")).
   109  				WithArgs("given_first_name", "given_last_name", 55, "given_id").WillReturnResult(sqlmock.NewResult(0, 0))
   110  			// WHEN
   111  			err := sut.UpdateSingleGlobal(ctx, givenUser)
   112  			// THEN
   113  			require.Error(t, err)
   114  			assert.Contains(t, err.Error(), "should update single row, but updated 0 rows")
   115  		})
   116  
   117  		t.Run("returns error if missing persistence context", func(t *testing.T) {
   118  			// WHEN
   119  			err := sut.UpdateSingleGlobal(context.TODO(), User{})
   120  			// THEN
   121  			require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error())
   122  		})
   123  
   124  		t.Run("returns error if entity is nil", func(t *testing.T) {
   125  			// WHEN
   126  			err := sut.UpdateSingleGlobal(context.TODO(), nil)
   127  			// THEN
   128  			require.EqualError(t, err, apperrors.NewInternalError("item cannot be nil").Error())
   129  		})
   130  	})
   131  
   132  	t.Run("Update with embedded tenant", func(t *testing.T) {
   133  		tests := []struct {
   134  			Name                string
   135  			AdditionalCondition string
   136  			Method              func(updater repo.UpdaterGlobal) func(ctx context.Context, dbEntity interface{}) error
   137  		}{
   138  			{
   139  				Name: "UpdateSingleGlobal",
   140  				Method: func(updater repo.UpdaterGlobal) func(ctx context.Context, dbEntity interface{}) error {
   141  					return updater.UpdateSingleGlobal
   142  				},
   143  			},
   144  			{
   145  				Name:                "UpdateSingleWithVersionGlobal",
   146  				AdditionalCondition: ", version = version+1",
   147  				Method: func(updater repo.UpdaterGlobal) func(ctx context.Context, dbEntity interface{}) error {
   148  					return updater.UpdateSingleWithVersionGlobal
   149  				},
   150  			},
   151  		}
   152  
   153  		for _, test := range tests {
   154  			t.Run(test.Name, func(t *testing.T) {
   155  				sut := repo.NewUpdaterWithEmbeddedTenant(UserType, "users", []string{"first_name", "last_name", "age"}, "tenant_id", []string{"id"})
   156  				givenUser := User{
   157  					ID:        "given_id",
   158  					Tenant:    "given_tenant",
   159  					FirstName: "given_first_name",
   160  					LastName:  "given_last_name",
   161  					Age:       55,
   162  				}
   163  
   164  				t.Run("success", func(t *testing.T) {
   165  					// GIVEN
   166  					db, mock := testdb.MockDatabase(t)
   167  					ctx := persistence.SaveToContext(context.TODO(), db)
   168  					defer mock.AssertExpectations(t)
   169  
   170  					mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ?"+test.AdditionalCondition+" WHERE id = ? AND tenant_id = ?")).
   171  						WithArgs("given_first_name", "given_last_name", 55, "given_id", "given_tenant").WillReturnResult(sqlmock.NewResult(0, 1))
   172  					// WHEN
   173  					err := test.Method(sut)(ctx, givenUser)
   174  					// THEN
   175  					require.NoError(t, err)
   176  				})
   177  
   178  				t.Run("success when no id column", func(t *testing.T) {
   179  					// GIVEN
   180  					sut := repo.NewUpdaterWithEmbeddedTenant(UserType, "users", []string{"first_name", "last_name", "age"}, "tenant_id", []string{})
   181  					db, mock := testdb.MockDatabase(t)
   182  					ctx := persistence.SaveToContext(context.TODO(), db)
   183  					defer mock.AssertExpectations(t)
   184  
   185  					mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ?"+test.AdditionalCondition+" WHERE tenant_id = ?")).
   186  						WithArgs("given_first_name", "given_last_name", 55, "given_tenant").WillReturnResult(sqlmock.NewResult(0, 1))
   187  					// WHEN
   188  					err := test.Method(sut)(ctx, givenUser)
   189  					// THEN
   190  					require.NoError(t, err)
   191  				})
   192  
   193  				t.Run("returns error when operation on db failed", func(t *testing.T) {
   194  					// GIVEN
   195  					db, mock := testdb.MockDatabase(t)
   196  					ctx := persistence.SaveToContext(context.TODO(), db)
   197  					defer mock.AssertExpectations(t)
   198  					mock.ExpectExec("UPDATE users .*").
   199  						WillReturnError(someError())
   200  					// WHEN
   201  					err := test.Method(sut)(ctx, givenUser)
   202  					// THEN
   203  					require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   204  				})
   205  
   206  				t.Run("context properly canceled", func(t *testing.T) {
   207  					db, mock := testdb.MockDatabase(t)
   208  					defer mock.AssertExpectations(t)
   209  
   210  					ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
   211  					defer cancel()
   212  
   213  					ctx = persistence.SaveToContext(ctx, db)
   214  
   215  					err := test.Method(sut)(ctx, givenUser)
   216  
   217  					require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached")
   218  				})
   219  
   220  				t.Run("returns non unique error", func(t *testing.T) {
   221  					// GIVEN
   222  					db, mock := testdb.MockDatabase(t)
   223  					ctx := persistence.SaveToContext(context.TODO(), db)
   224  					defer mock.AssertExpectations(t)
   225  					mock.ExpectExec("UPDATE users .*").
   226  						WillReturnError(&pq.Error{Code: persistence.UniqueViolation})
   227  					// WHEN
   228  					err := test.Method(sut)(ctx, givenUser)
   229  					// THEN
   230  					require.True(t, apperrors.IsNotUniqueError(err))
   231  				})
   232  
   233  				t.Run("returns error if modified more than one row", func(t *testing.T) {
   234  					// GIVEN
   235  					db, mock := testdb.MockDatabase(t)
   236  					ctx := persistence.SaveToContext(context.TODO(), db)
   237  					defer mock.AssertExpectations(t)
   238  
   239  					mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ?"+test.AdditionalCondition+" WHERE id = ? AND tenant_id = ?")).
   240  						WithArgs("given_first_name", "given_last_name", 55, "given_id", "given_tenant").WillReturnResult(sqlmock.NewResult(0, 157))
   241  					// WHEN
   242  					err := test.Method(sut)(ctx, givenUser)
   243  					// THEN
   244  					require.Error(t, err)
   245  					require.Contains(t, err.Error(), "should update single row, but updated 157 rows")
   246  				})
   247  
   248  				t.Run("returns error if does not modified any row", func(t *testing.T) {
   249  					// GIVEN
   250  					db, mock := testdb.MockDatabase(t)
   251  					ctx := persistence.SaveToContext(context.TODO(), db)
   252  					defer mock.AssertExpectations(t)
   253  
   254  					mock.ExpectExec(regexp.QuoteMeta("UPDATE users SET first_name = ?, last_name = ?, age = ?"+test.AdditionalCondition+" WHERE id = ? AND tenant_id = ?")).
   255  						WithArgs("given_first_name", "given_last_name", 55, "given_id", "given_tenant").WillReturnResult(sqlmock.NewResult(0, 0))
   256  					// WHEN
   257  					err := test.Method(sut)(ctx, givenUser)
   258  					// THEN
   259  					require.Error(t, err)
   260  					if !strings.Contains(err.Error(), apperrors.ShouldBeOwnerMsg) && !strings.Contains(err.Error(), apperrors.ConcurrentUpdateMsg) {
   261  						t.Errorf("unexpected error: %s", err)
   262  					}
   263  				})
   264  
   265  				t.Run("returns error if missing persistence context", func(t *testing.T) {
   266  					// WHEN
   267  					err := test.Method(sut)(context.TODO(), User{})
   268  					// THEN
   269  					require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error())
   270  				})
   271  
   272  				t.Run("returns error if entity is nil", func(t *testing.T) {
   273  					// WHEN
   274  					err := test.Method(sut)(context.TODO(), nil)
   275  					// THEN
   276  					require.EqualError(t, err, apperrors.NewInternalError("item cannot be nil").Error())
   277  				})
   278  			})
   279  		}
   280  	})
   281  }
   282  
   283  func TestUpdater(t *testing.T) {
   284  	tests := []struct {
   285  		Name                    string
   286  		AdditionalCondition     string
   287  		AttachAdditionalQueries func(mock testdb.DBMock, m2mTable, tenant string)
   288  		Method                  func(updater repo.Updater) func(ctx context.Context, resourceType resource.Type, tenant string, dbEntity interface{}) error
   289  	}{
   290  		{
   291  			Name:                    "UpdateSingle",
   292  			AttachAdditionalQueries: func(mock testdb.DBMock, m2mTable, tenant string) {},
   293  			Method: func(updater repo.Updater) func(ctx context.Context, resourceType resource.Type, tenant string, dbEntity interface{}) error {
   294  				return updater.UpdateSingle
   295  			},
   296  		},
   297  		{
   298  			Name: "UpdateSingleWithVersion",
   299  			AttachAdditionalQueries: func(mock testdb.DBMock, m2mTable, tenant string) {
   300  				mock.ExpectQuery(regexp.QuoteMeta(fmt.Sprintf("SELECT 1 FROM %s WHERE id = $1 AND %s", appTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$2")))).
   301  					WithArgs(appID, tenant).WillReturnRows(testdb.RowWhenObjectExist())
   302  			},
   303  			AdditionalCondition: ", version = version+1",
   304  			Method: func(updater repo.Updater) func(ctx context.Context, resourceType resource.Type, tenant string, dbEntity interface{}) error {
   305  				return updater.UpdateSingleWithVersion
   306  			},
   307  		},
   308  	}
   309  
   310  	for _, test := range tests {
   311  		t.Run(test.Name, func(t *testing.T) {
   312  			updater := repo.NewUpdater(appTableName, []string{"name", "description"}, []string{"id"})
   313  			resourceType := resource.Application
   314  			m2mTable, ok := resourceType.TenantAccessTable()
   315  			require.True(t, ok)
   316  
   317  			t.Run("success", func(t *testing.T) {
   318  				// GIVEN
   319  				db, mock := testdb.MockDatabase(t)
   320  				ctx := persistence.SaveToContext(context.TODO(), db)
   321  				defer mock.AssertExpectations(t)
   322  
   323  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   324  
   325  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true))", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   326  					WithArgs(appName, appDescription, appID, tenantID).WillReturnResult(sqlmock.NewResult(0, 1))
   327  				// WHEN
   328  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   329  				// THEN
   330  				require.NoError(t, err)
   331  			})
   332  
   333  			t.Run("success when no id column", func(t *testing.T) {
   334  				updater := repo.NewUpdater(appTableName, []string{"name", "description"}, []string{})
   335  				// GIVEN
   336  				db, mock := testdb.MockDatabase(t)
   337  				ctx := persistence.SaveToContext(context.TODO(), db)
   338  				defer mock.AssertExpectations(t)
   339  
   340  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   341  
   342  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true)", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   343  					WithArgs(appName, appDescription, tenantID).WillReturnResult(sqlmock.NewResult(0, 1))
   344  				// WHEN
   345  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   346  				// THEN
   347  				require.NoError(t, err)
   348  			})
   349  
   350  			t.Run("returns error when operation on db failed", func(t *testing.T) {
   351  				// GIVEN
   352  				db, mock := testdb.MockDatabase(t)
   353  				ctx := persistence.SaveToContext(context.TODO(), db)
   354  				defer mock.AssertExpectations(t)
   355  
   356  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   357  
   358  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true)", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   359  					WithArgs(appName, appDescription, appID, tenantID).WillReturnError(someError())
   360  				// WHEN
   361  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   362  				// THEN
   363  				require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query")
   364  			})
   365  
   366  			t.Run("context properly canceled", func(t *testing.T) {
   367  				db, mock := testdb.MockDatabase(t)
   368  				defer mock.AssertExpectations(t)
   369  
   370  				ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
   371  				defer cancel()
   372  
   373  				ctx = persistence.SaveToContext(ctx, db)
   374  
   375  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   376  
   377  				require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached")
   378  			})
   379  
   380  			t.Run("returns non unique error", func(t *testing.T) {
   381  				// GIVEN
   382  				db, mock := testdb.MockDatabase(t)
   383  				ctx := persistence.SaveToContext(context.TODO(), db)
   384  				defer mock.AssertExpectations(t)
   385  
   386  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   387  
   388  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true)", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   389  					WithArgs(appName, appDescription, appID, tenantID).WillReturnError(&pq.Error{Code: persistence.UniqueViolation})
   390  				// WHEN
   391  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   392  				// THEN
   393  				require.True(t, apperrors.IsNotUniqueError(err))
   394  			})
   395  
   396  			t.Run("returns error if modified more than one row", func(t *testing.T) {
   397  				// GIVEN
   398  				db, mock := testdb.MockDatabase(t)
   399  				ctx := persistence.SaveToContext(context.TODO(), db)
   400  				defer mock.AssertExpectations(t)
   401  
   402  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   403  
   404  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true)", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   405  					WithArgs(appName, appDescription, appID, tenantID).WillReturnResult(sqlmock.NewResult(0, 157))
   406  				// WHEN
   407  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   408  				// THEN
   409  				require.Error(t, err)
   410  				require.Contains(t, err.Error(), "should update single row, but updated 157 rows")
   411  			})
   412  
   413  			t.Run("returns error if does not modified any row", func(t *testing.T) {
   414  				// GIVEN
   415  				db, mock := testdb.MockDatabase(t)
   416  				ctx := persistence.SaveToContext(context.TODO(), db)
   417  				defer mock.AssertExpectations(t)
   418  
   419  				test.AttachAdditionalQueries(mock, m2mTable, tenantID)
   420  
   421  				mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ?%s WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true)", appTableName, test.AdditionalCondition, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   422  					WithArgs(appName, appDescription, appID, tenantID).WillReturnResult(sqlmock.NewResult(0, 0))
   423  				// WHEN
   424  				err := test.Method(updater)(ctx, resourceType, tenantID, fixApp)
   425  				// THEN
   426  				require.Error(t, err)
   427  				if !strings.Contains(err.Error(), apperrors.ShouldBeOwnerMsg) && !strings.Contains(err.Error(), apperrors.ConcurrentUpdateMsg) {
   428  					t.Errorf("unexpected error: %s", err)
   429  				}
   430  			})
   431  
   432  			t.Run("returns error if entity does not have tenant access table", func(t *testing.T) {
   433  				db, mock := testdb.MockDatabase(t)
   434  				ctx := persistence.SaveToContext(context.TODO(), db)
   435  				defer mock.AssertExpectations(t)
   436  				// WHEN
   437  				err := test.Method(updater)(ctx, resource.Type("unknown"), tenantID, fixApp)
   438  				// THEN
   439  				assert.Contains(t, err.Error(), "entity unknown does not have access table")
   440  			})
   441  
   442  			t.Run("returns error if missing persistence context", func(t *testing.T) {
   443  				// WHEN
   444  				err := test.Method(updater)(context.TODO(), resourceType, tenantID, fixApp)
   445  				// THEN
   446  				require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error())
   447  			})
   448  
   449  			t.Run("returns error if entity is nil", func(t *testing.T) {
   450  				// WHEN
   451  				err := test.Method(updater)(context.TODO(), resourceType, tenantID, nil)
   452  				// THEN
   453  				require.EqualError(t, err, apperrors.NewInternalError("item cannot be nil").Error())
   454  			})
   455  		})
   456  	}
   457  
   458  	t.Run("success for BundleInstanceAuth", func(t *testing.T) {
   459  		updater := repo.NewUpdater(biaTableName, []string{"name", "description"}, []string{"id"})
   460  		resourceType := resource.BundleInstanceAuth
   461  		m2mTable, ok := resourceType.TenantAccessTable()
   462  		require.True(t, ok)
   463  		// GIVEN
   464  		db, mock := testdb.MockDatabase(t)
   465  		ctx := persistence.SaveToContext(context.TODO(), db)
   466  		defer mock.AssertExpectations(t)
   467  
   468  		mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("UPDATE %s SET name = ?, description = ? WHERE id = ? AND (id IN (SELECT %s FROM %s WHERE %s = ? AND %s = true) OR owner_id = ?)", biaTableName, repo.M2MResourceIDColumn, m2mTable, repo.M2MTenantIDColumn, repo.M2MOwnerColumn))).
   469  			WithArgs(biaName, biaDescription, biaID, tenantID, tenantID).WillReturnResult(sqlmock.NewResult(0, 1))
   470  		// WHEN
   471  		err := updater.UpdateSingle(ctx, resourceType, tenantID, fixBIA)
   472  		// THEN
   473  		require.NoError(t, err)
   474  	})
   475  
   476  	t.Run("UpdateSingleWithVersion", func(t *testing.T) {
   477  		updater := repo.NewUpdater(appTableName, []string{"name", "description"}, []string{"id"})
   478  		resourceType := resource.Application
   479  		m2mTable, ok := resourceType.TenantAccessTable()
   480  		require.True(t, ok)
   481  
   482  		t.Run("tenant does not have owner access should return error", func(t *testing.T) {
   483  			// GIVEN
   484  			db, mock := testdb.MockDatabase(t)
   485  			ctx := persistence.SaveToContext(context.TODO(), db)
   486  			defer mock.AssertExpectations(t)
   487  
   488  			mock.ExpectQuery(regexp.QuoteMeta(fmt.Sprintf("SELECT 1 FROM %s WHERE id = $1 AND %s", appTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$2")))).
   489  				WithArgs(appID, tenantID).WillReturnRows(testdb.RowWhenObjectDoesNotExist())
   490  
   491  			// WHEN
   492  			err := updater.UpdateSingleWithVersion(ctx, resourceType, tenantID, fixApp)
   493  			// THEN
   494  			require.Error(t, err)
   495  			require.Contains(t, err.Error(), "entity does not exist or caller tenant does not have owner access")
   496  		})
   497  
   498  		t.Run("entity is not identifiable should return error", func(t *testing.T) {
   499  			// GIVEN
   500  			db, mock := testdb.MockDatabase(t)
   501  			ctx := persistence.SaveToContext(context.TODO(), db)
   502  			defer mock.AssertExpectations(t)
   503  
   504  			// WHEN
   505  			err := updater.UpdateSingleWithVersion(ctx, resourceType, tenantID, struct{}{})
   506  			// THEN
   507  			require.Error(t, err)
   508  			require.Contains(t, err.Error(), "id cannot be empty, check if the entity implements Identifiable")
   509  		})
   510  	})
   511  }