github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/delete_test.go (about) 1 package repo_test 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "testing" 8 "time" 9 10 "github.com/DATA-DOG/go-sqlmock" 11 "github.com/kyma-incubator/compass/components/director/internal/repo" 12 "github.com/kyma-incubator/compass/components/director/internal/repo/testdb" 13 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 14 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 15 "github.com/kyma-incubator/compass/components/director/pkg/resource" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 type methodToTest = func(ctx context.Context, resourceType resource.Type, tenant string, conditions repo.Conditions) error 21 type methodToTestWithoutTenant = func(ctx context.Context, conditions repo.Conditions) error 22 23 func TestDelete(t *testing.T) { 24 deleter := repo.NewDeleter(bundlesTableName) 25 resourceType := resource.Bundle 26 m2mTable, ok := resourceType.TenantAccessTable() 27 require.True(t, ok) 28 29 tc := map[string]methodToTest{ 30 "DeleteMany": deleter.DeleteMany, 31 "DeleteOne": deleter.DeleteOne, 32 } 33 for tn, testedMethod := range tc { 34 t.Run(fmt.Sprintf("[%s] success", tn), func(t *testing.T) { 35 // GIVEN 36 db, mock := testdb.MockDatabase(t) 37 ctx := persistence.SaveToContext(context.TODO(), db) 38 defer mock.AssertExpectations(t) 39 40 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE id = $1 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$2")))). 41 WithArgs(bundleID, tenantID).WillReturnResult(sqlmock.NewResult(-1, 1)) 42 43 // WHEN 44 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("id", bundleID)}) 45 // THEN 46 require.NoError(t, err) 47 }) 48 49 t.Run(fmt.Sprintf("[%s] success when no conditions", tn), func(t *testing.T) { 50 // GIVEN 51 db, mock := testdb.MockDatabase(t) 52 ctx := persistence.SaveToContext(context.TODO(), db) 53 defer mock.AssertExpectations(t) 54 55 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$1")))). 56 WithArgs(tenantID).WillReturnResult(sqlmock.NewResult(-1, 1)) 57 58 // WHEN 59 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{}) 60 // THEN 61 require.NoError(t, err) 62 }) 63 64 t.Run(fmt.Sprintf("[%s] success when more conditions", tn), func(t *testing.T) { 65 // GIVEN 66 db, mock := testdb.MockDatabase(t) 67 ctx := persistence.SaveToContext(context.TODO(), db) 68 defer mock.AssertExpectations(t) 69 70 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE description = $1 AND name = $2 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$3")))). 71 WithArgs(bundleDescription, bundleName, tenantID).WillReturnResult(sqlmock.NewResult(-1, 1)) 72 73 // WHEN 74 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("description", bundleDescription), repo.NewEqualCondition("name", bundleName)}) 75 // THEN 76 require.NoError(t, err) 77 }) 78 79 t.Run(fmt.Sprintf("[%s] fail when 0 entities match conditions", tn), func(t *testing.T) { 80 // GIVEN 81 db, mock := testdb.MockDatabase(t) 82 ctx := persistence.SaveToContext(context.TODO(), db) 83 defer mock.AssertExpectations(t) 84 85 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE description = $1 AND name = $2 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$3")))). 86 WithArgs(bundleDescription, bundleName, tenantID).WillReturnResult(sqlmock.NewResult(-1, 0)) 87 88 // WHEN 89 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("description", bundleDescription), repo.NewEqualCondition("name", bundleName)}) 90 // THEN 91 if tn == "DeleteMany" { 92 require.NoError(t, err) 93 } else { 94 require.Error(t, err) 95 require.Contains(t, err.Error(), apperrors.ShouldBeOwnerMsg) 96 } 97 }) 98 99 t.Run(fmt.Sprintf("[%s] returns error when delete operation returns error", tn), func(t *testing.T) { 100 // GIVEN 101 db, mock := testdb.MockDatabase(t) 102 ctx := persistence.SaveToContext(context.TODO(), db) 103 defer mock.AssertExpectations(t) 104 105 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE description = $1 AND name = $2 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$3")))). 106 WithArgs(bundleDescription, bundleName, tenantID).WillReturnError(someError()) 107 108 // WHEN 109 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("description", bundleDescription), repo.NewEqualCondition("name", bundleName)}) 110 // THEN 111 require.Error(t, err) 112 // THEN 113 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 114 }) 115 116 t.Run(fmt.Sprintf("[%s] context properly canceled", tn), func(t *testing.T) { 117 db, mock := testdb.MockDatabase(t) 118 defer mock.AssertExpectations(t) 119 120 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 121 defer cancel() 122 123 ctx = persistence.SaveToContext(ctx, db) 124 125 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("id", bundleID)}) 126 127 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 128 }) 129 130 t.Run(fmt.Sprintf("[%s] returns error if missing persistence context", tn), func(t *testing.T) { 131 ctx := context.TODO() 132 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("id", bundleID)}) 133 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 134 }) 135 136 t.Run(fmt.Sprintf("[%s] returns error if missing tenant id", tn), func(t *testing.T) { 137 ctx := context.TODO() 138 err := testedMethod(ctx, resourceType, "", repo.Conditions{repo.NewEqualCondition("id", bundleID)}) 139 require.EqualError(t, err, apperrors.NewTenantRequiredError().Error()) 140 }) 141 142 t.Run(fmt.Sprintf("[%s] returns error if entity does not have access table", tn), func(t *testing.T) { 143 db, mock := testdb.MockDatabase(t) 144 ctx := persistence.SaveToContext(context.TODO(), db) 145 defer mock.AssertExpectations(t) 146 147 err := testedMethod(ctx, resource.Type("unknown"), tenantID, repo.Conditions{repo.NewEqualCondition("id", bundleID)}) 148 149 require.Error(t, err) 150 assert.Contains(t, err.Error(), "entity unknown does not have access table") 151 }) 152 } 153 154 t.Run("BIA", func(t *testing.T) { 155 deleter := repo.NewDeleter(biaTableName) 156 resourceType := resource.BundleInstanceAuth 157 m2mTable, ok := resourceType.TenantAccessTable() 158 require.True(t, ok) 159 160 tc := map[string]methodToTest{ 161 "DeleteMany": deleter.DeleteMany, 162 "DeleteOne": deleter.DeleteOne, 163 } 164 for tn, testedMethod := range tc { 165 t.Run(fmt.Sprintf("[%s] success", tn), func(t *testing.T) { 166 // GIVEN 167 db, mock := testdb.MockDatabase(t) 168 ctx := persistence.SaveToContext(context.TODO(), db) 169 defer mock.AssertExpectations(t) 170 171 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE id = $1 AND %s", biaTableName, fmt.Sprintf(tenantIsolationConditionForBIA, m2mTable, "$2", "$3")))). 172 WithArgs(biaID, tenantID, tenantID).WillReturnResult(sqlmock.NewResult(-1, 1)) 173 174 // WHEN 175 err := testedMethod(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("id", biaID)}) 176 // THEN 177 require.NoError(t, err) 178 }) 179 } 180 }) 181 182 t.Run("[DeleteMany] success when more than one resource matches conditions", func(t *testing.T) { 183 // GIVEN 184 db, mock := testdb.MockDatabase(t) 185 ctx := persistence.SaveToContext(context.TODO(), db) 186 defer mock.AssertExpectations(t) 187 188 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE description = $1 AND name = $2 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$3")))). 189 WithArgs(bundleDescription, bundleName, tenantID).WillReturnResult(sqlmock.NewResult(-1, 2)) 190 191 // WHEN 192 err := deleter.DeleteMany(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("description", bundleDescription), repo.NewEqualCondition("name", bundleName)}) 193 // THEN 194 require.NoError(t, err) 195 }) 196 197 t.Run("[DeleteOne] fail when more than one resource matches conditions", func(t *testing.T) { 198 // GIVEN 199 db, mock := testdb.MockDatabase(t) 200 ctx := persistence.SaveToContext(context.TODO(), db) 201 defer mock.AssertExpectations(t) 202 203 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE description = $1 AND name = $2 AND %s", bundlesTableName, fmt.Sprintf(tenantIsolationConditionWithOwnerCheckFmt, m2mTable, "$3")))). 204 WithArgs(bundleDescription, bundleName, tenantID).WillReturnResult(sqlmock.NewResult(-1, 2)) 205 206 // WHEN 207 err := deleter.DeleteOne(ctx, resourceType, tenantID, repo.Conditions{repo.NewEqualCondition("description", bundleDescription), repo.NewEqualCondition("name", bundleName)}) 208 // THEN 209 require.Error(t, err) 210 require.Contains(t, err.Error(), "delete should remove single row, but removed 2 rows") 211 }) 212 } 213 214 func TestDeleteGlobal(t *testing.T) { 215 givenID := "id" 216 sut := repo.NewDeleterGlobal(UserType, userTableName) 217 218 tc := map[string]methodToTestWithoutTenant{ 219 "DeleteMany": sut.DeleteManyGlobal, 220 "DeleteOne": sut.DeleteOneGlobal, 221 } 222 for tn, testedMethod := range tc { 223 t.Run(fmt.Sprintf("[%s] success", tn), func(t *testing.T) { 224 // GIVEN 225 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE id = $1", userTableName)) 226 db, mock := testdb.MockDatabase(t) 227 ctx := persistence.SaveToContext(context.TODO(), db) 228 defer mock.AssertExpectations(t) 229 mock.ExpectExec(expectedQuery).WithArgs(givenID).WillReturnResult(sqlmock.NewResult(-1, 1)) 230 // WHEN 231 err := testedMethod(ctx, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 232 // THEN 233 require.NoError(t, err) 234 }) 235 236 t.Run(fmt.Sprintf("[%s] success when no conditions", tn), func(t *testing.T) { 237 // GIVEN 238 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s", userTableName)) 239 db, mock := testdb.MockDatabase(t) 240 ctx := persistence.SaveToContext(context.TODO(), db) 241 defer mock.AssertExpectations(t) 242 mock.ExpectExec(expectedQuery).WillReturnResult(sqlmock.NewResult(-1, 1)) 243 // WHEN 244 err := testedMethod(ctx, repo.Conditions{}) 245 // THEN 246 require.NoError(t, err) 247 }) 248 249 t.Run(fmt.Sprintf("[%s] success when more conditions", tn), func(t *testing.T) { 250 // GIVEN 251 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE first_name = $1 AND last_name = $2", userTableName)) 252 db, mock := testdb.MockDatabase(t) 253 ctx := persistence.SaveToContext(context.TODO(), db) 254 defer mock.AssertExpectations(t) 255 mock.ExpectExec(expectedQuery).WithArgs("john", "doe").WillReturnResult(sqlmock.NewResult(-1, 1)) 256 // WHEN 257 err := testedMethod(ctx, repo.Conditions{repo.NewEqualCondition("first_name", "john"), repo.NewEqualCondition("last_name", "doe")}) 258 // THEN 259 require.NoError(t, err) 260 }) 261 262 t.Run(fmt.Sprintf("[%s] returns error on db operation", tn), func(t *testing.T) { 263 // GIVEN 264 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE id = $1", userTableName)) 265 db, mock := testdb.MockDatabase(t) 266 ctx := persistence.SaveToContext(context.TODO(), db) 267 defer mock.AssertExpectations(t) 268 mock.ExpectExec(expectedQuery).WithArgs(givenID).WillReturnError(someError()) 269 // WHEN 270 err := testedMethod(ctx, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 271 // THEN 272 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 273 }) 274 275 t.Run("context properly canceled", func(t *testing.T) { 276 db, mock := testdb.MockDatabase(t) 277 defer mock.AssertExpectations(t) 278 279 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 280 defer cancel() 281 282 ctx = persistence.SaveToContext(ctx, db) 283 284 err := testedMethod(ctx, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 285 286 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 287 }) 288 289 t.Run(fmt.Sprintf("[%s] returns error if missing persistence context", tn), func(t *testing.T) { 290 ctx := context.TODO() 291 err := testedMethod(ctx, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 292 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 293 }) 294 } 295 } 296 297 func TestDeleteWithEmbeddedTenant(t *testing.T) { 298 givenID := "id" 299 sut := repo.NewDeleterWithEmbeddedTenant(userTableName, "tenant_id") 300 301 tc := map[string]methodToTest{ 302 "DeleteMany": sut.DeleteMany, 303 "DeleteOne": sut.DeleteOne, 304 } 305 for tn, testedMethod := range tc { 306 t.Run(fmt.Sprintf("[%s] success", tn), func(t *testing.T) { 307 // GIVEN 308 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE tenant_id = $1 AND id = $2", userTableName)) 309 db, mock := testdb.MockDatabase(t) 310 ctx := persistence.SaveToContext(context.TODO(), db) 311 defer mock.AssertExpectations(t) 312 mock.ExpectExec(expectedQuery).WithArgs(tenantID, givenID).WillReturnResult(sqlmock.NewResult(-1, 1)) 313 // WHEN 314 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 315 // THEN 316 require.NoError(t, err) 317 }) 318 319 t.Run(fmt.Sprintf("[%s] success when no conditions", tn), func(t *testing.T) { 320 // GIVEN 321 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE tenant_id = $1", userTableName)) 322 db, mock := testdb.MockDatabase(t) 323 ctx := persistence.SaveToContext(context.TODO(), db) 324 defer mock.AssertExpectations(t) 325 mock.ExpectExec(expectedQuery).WithArgs(tenantID).WillReturnResult(sqlmock.NewResult(-1, 1)) 326 // WHEN 327 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{}) 328 // THEN 329 require.NoError(t, err) 330 }) 331 332 t.Run(fmt.Sprintf("[%s] success when more conditions", tn), func(t *testing.T) { 333 // GIVEN 334 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE tenant_id = $1 AND first_name = $2 AND last_name = $3", userTableName)) 335 db, mock := testdb.MockDatabase(t) 336 ctx := persistence.SaveToContext(context.TODO(), db) 337 defer mock.AssertExpectations(t) 338 mock.ExpectExec(expectedQuery).WithArgs(tenantID, "john", "doe").WillReturnResult(sqlmock.NewResult(-1, 1)) 339 // WHEN 340 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{repo.NewEqualCondition("first_name", "john"), repo.NewEqualCondition("last_name", "doe")}) 341 // THEN 342 require.NoError(t, err) 343 }) 344 345 t.Run(fmt.Sprintf("[%s] returns error on db operation", tn), func(t *testing.T) { 346 // GIVEN 347 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE tenant_id = $1 AND id = $2", userTableName)) 348 db, mock := testdb.MockDatabase(t) 349 ctx := persistence.SaveToContext(context.TODO(), db) 350 defer mock.AssertExpectations(t) 351 mock.ExpectExec(expectedQuery).WithArgs(tenantID, givenID).WillReturnError(someError()) 352 // WHEN 353 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 354 // THEN 355 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 356 }) 357 358 t.Run("context properly canceled", func(t *testing.T) { 359 db, mock := testdb.MockDatabase(t) 360 defer mock.AssertExpectations(t) 361 362 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 363 defer cancel() 364 365 ctx = persistence.SaveToContext(ctx, db) 366 367 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 368 369 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 370 }) 371 372 t.Run(fmt.Sprintf("[%s] returns error if missing persistence context", tn), func(t *testing.T) { 373 ctx := context.TODO() 374 err := testedMethod(ctx, resource.AutomaticScenarioAssigment, tenantID, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 375 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 376 }) 377 } 378 } 379 380 func TestDeleterConditionTreeWithEmbeddedTenant(t *testing.T) { 381 sut := repo.NewDeleterConditionTreeWithEmbeddedTenant(userTableName, "tenant_id") 382 383 t.Run("deletes all items successfully", func(t *testing.T) { 384 db, mock := testdb.MockDatabase(t) 385 defer mock.AssertExpectations(t) 386 387 mock.ExpectExec(regexp.QuoteMeta(`DELETE FROM users WHERE (tenant_id = $1 AND first_name IN ($2, $3))`)). 388 WithArgs(tenantID, "Joe", "Smith").WillReturnResult(sqlmock.NewResult(0, 2)) 389 ctx := persistence.SaveToContext(context.TODO(), db) 390 391 err := sut.DeleteConditionTree(ctx, UserType, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("first_name", []string{"Joe", "Smith"})}) 392 require.NoError(t, err) 393 }) 394 395 t.Run("deletes all items successfully with additional parameters", func(t *testing.T) { 396 db, mock := testdb.MockDatabase(t) 397 defer mock.AssertExpectations(t) 398 399 mock.ExpectExec(regexp.QuoteMeta("DELETE FROM users WHERE (tenant_id = $1 AND (first_name = $2 OR age != $3))")). 400 WithArgs(tenantID, "Joe", 18).WillReturnResult(sqlmock.NewResult(0, 1)) 401 ctx := persistence.SaveToContext(context.TODO(), db) 402 403 conditions := repo.Or(repo.ConditionTreesFromConditions([]repo.Condition{ 404 repo.NewEqualCondition("first_name", "Joe"), 405 repo.NewNotEqualCondition("age", 18), 406 })...) 407 408 err := sut.DeleteConditionTree(ctx, UserType, tenantID, conditions) 409 require.NoError(t, err) 410 }) 411 412 t.Run("returns error if missing persistence context", func(t *testing.T) { 413 ctx := context.TODO() 414 err := sut.DeleteConditionTree(ctx, UserType, tenantID, nil) 415 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 416 }) 417 418 t.Run("returns error on db operation", func(t *testing.T) { 419 db, mock := testdb.MockDatabase(t) 420 defer mock.AssertExpectations(t) 421 422 mock.ExpectExec(`DELETE .*`).WillReturnError(someError()) 423 ctx := persistence.SaveToContext(context.TODO(), db) 424 425 err := sut.DeleteConditionTree(ctx, UserType, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("first_name", []string{"Peter", "Homer"})}) 426 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 427 }) 428 429 t.Run("context properly canceled", func(t *testing.T) { 430 db, mock := testdb.MockDatabase(t) 431 defer mock.AssertExpectations(t) 432 433 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 434 defer cancel() 435 436 ctx = persistence.SaveToContext(ctx, db) 437 err := sut.DeleteConditionTree(ctx, UserType, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("first_name", []string{"Peter", "Homer"})}) 438 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 439 }) 440 } 441 442 func TestDeleterConditionTree(t *testing.T) { 443 sut := repo.NewDeleterConditionTree(biaTableName) 444 resourceType := resource.BundleInstanceAuth 445 m2mTable, ok := resourceType.TenantAccessTable() 446 require.True(t, ok) 447 448 t.Run("deletes all items successfully", func(t *testing.T) { 449 db, mock := testdb.MockDatabase(t) 450 defer mock.AssertExpectations(t) 451 452 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE (%s AND name IN ($3, $4))", biaTableName, fmt.Sprintf(tenantIsolationConditionForBIA, m2mTable, "$1", "$2")))). 453 WithArgs(tenantID, tenantID, "bundle1", "bundle2").WillReturnResult(sqlmock.NewResult(-1, 1)) 454 ctx := persistence.SaveToContext(context.TODO(), db) 455 456 err := sut.DeleteConditionTree(ctx, resource.BundleInstanceAuth, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("name", []string{"bundle1", "bundle2"})}) 457 require.NoError(t, err) 458 }) 459 460 t.Run("deletes all items successfully with additional parameters", func(t *testing.T) { 461 db, mock := testdb.MockDatabase(t) 462 defer mock.AssertExpectations(t) 463 464 mock.ExpectExec(regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE (%s AND (name = $3 OR id != $4))", biaTableName, fmt.Sprintf(tenantIsolationConditionForBIA, m2mTable, "$1", "$2")))). 465 WithArgs(tenantID, tenantID, "bundle1", bundleID).WillReturnResult(sqlmock.NewResult(-1, 1)) 466 ctx := persistence.SaveToContext(context.TODO(), db) 467 468 conditions := repo.Or(repo.ConditionTreesFromConditions([]repo.Condition{ 469 repo.NewEqualCondition("name", "bundle1"), 470 repo.NewNotEqualCondition("id", bundleID), 471 })...) 472 473 err := sut.DeleteConditionTree(ctx, resource.BundleInstanceAuth, tenantID, conditions) 474 require.NoError(t, err) 475 }) 476 477 t.Run("returns error if missing persistence context", func(t *testing.T) { 478 ctx := context.TODO() 479 err := sut.DeleteConditionTree(ctx, resource.BundleInstanceAuth, tenantID, nil) 480 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 481 }) 482 t.Run("returns error if resource type does not have access table", func(t *testing.T) { 483 ctx := context.TODO() 484 err := sut.DeleteConditionTree(ctx, userTableName, tenantID, nil) 485 require.EqualError(t, err, fmt.Sprintf("entity %s does not have access table", userTableName)) 486 }) 487 t.Run("returns error if tenant is not provided", func(t *testing.T) { 488 ctx := context.TODO() 489 err := sut.DeleteConditionTree(ctx, userTableName, "", nil) 490 require.EqualError(t, err, apperrors.NewTenantRequiredError().Error()) 491 }) 492 t.Run("returns error on db operation", func(t *testing.T) { 493 db, mock := testdb.MockDatabase(t) 494 defer mock.AssertExpectations(t) 495 496 mock.ExpectExec(`DELETE .*`).WillReturnError(someError()) 497 ctx := persistence.SaveToContext(context.TODO(), db) 498 499 err := sut.DeleteConditionTree(ctx, resource.BundleInstanceAuth, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("first_name", []string{"Peter", "Homer"})}) 500 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 501 }) 502 503 t.Run("context properly canceled", func(t *testing.T) { 504 db, mock := testdb.MockDatabase(t) 505 defer mock.AssertExpectations(t) 506 507 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 508 defer cancel() 509 510 ctx = persistence.SaveToContext(ctx, db) 511 err := sut.DeleteConditionTree(ctx, resource.BundleInstanceAuth, tenantID, &repo.ConditionTree{Operand: repo.NewInConditionForStringValues("first_name", []string{"Peter", "Homer"})}) 512 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 513 }) 514 } 515 516 func TestDeleteGlobalReactsOnNumberOfRemovedObjects(t *testing.T) { 517 givenID := "id" 518 sut := repo.NewDeleterGlobal(UserType, userTableName) 519 520 cases := map[string]struct { 521 methodToTest methodToTestWithoutTenant 522 givenRowsAffected int64 523 expectedErrString string 524 }{ 525 "[DeleteOne] returns error when removed more than one object": { 526 methodToTest: sut.DeleteOneGlobal, 527 givenRowsAffected: 154, 528 expectedErrString: "Internal Server Error: delete should remove single row, but removed 154 rows", 529 }, 530 "[DeleteOne] returns error when object not found": { 531 methodToTest: sut.DeleteOneGlobal, 532 givenRowsAffected: 0, 533 expectedErrString: "Internal Server Error: delete should remove single row, but removed 0 rows", 534 }, 535 "[Delete Many] success when removed more than one object": { 536 methodToTest: sut.DeleteManyGlobal, 537 givenRowsAffected: 154, 538 expectedErrString: "", 539 }, 540 "[Delete Many] success when not found objects to remove": { 541 methodToTest: sut.DeleteManyGlobal, 542 givenRowsAffected: 0, 543 expectedErrString: "", 544 }, 545 } 546 547 for tn, tc := range cases { 548 t.Run(tn, func(t *testing.T) { 549 // GIVEN 550 expectedQuery := regexp.QuoteMeta(fmt.Sprintf("DELETE FROM %s WHERE id = $1", userTableName)) 551 db, mock := testdb.MockDatabase(t) 552 ctx := persistence.SaveToContext(context.TODO(), db) 553 defer mock.AssertExpectations(t) 554 mock.ExpectExec(expectedQuery).WithArgs(givenID).WillReturnResult(sqlmock.NewResult(0, tc.givenRowsAffected)) 555 // WHEN 556 err := tc.methodToTest(ctx, repo.Conditions{repo.NewEqualCondition("id", givenID)}) 557 // THEN 558 if tc.expectedErrString != "" { 559 require.EqualError(t, err, tc.expectedErrString) 560 } 561 }) 562 } 563 }