From a53bb23c23ef1ceca9de081838ff3cc06326c02a Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 31 Dec 2025 13:29:11 +1000 Subject: [PATCH] Add tests for cascade delete behavior --- .../DefaultCollectionSemaphoreTests.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs new file mode 100644 index 0000000000..38dd28b367 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs @@ -0,0 +1,78 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Billing.Enums; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository; + +/// +/// Tests for DefaultCollectionSemaphore table behavior including cascade deletions +/// +public class DefaultCollectionSemaphoreTests +{ + [Theory, DatabaseData] + public async Task DeleteOrganizationUser_CascadeDeletesSemaphore( + IUserRepository userRepository, + IOrganizationRepository organizationRepository, + ICollectionRepository collectionRepository, + IOrganizationUserRepository organizationUserRepository) + { + // Arrange + var user = await userRepository.CreateTestUserAsync(); + var organization = await organizationRepository.CreateTestOrganizationAsync(); + var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); + + await collectionRepository.UpsertDefaultCollectionsAsync( + organization.Id, + [orgUser.Id], + "My Items"); + + // Verify semaphore exists + var semaphoreBefore = await collectionRepository.GetDefaultCollectionSemaphoresAsync(organization.Id); + Assert.Single(semaphoreBefore, s => s == orgUser.Id); + + // Act - Delete organization user + await organizationUserRepository.DeleteAsync(orgUser); + + // Assert - Semaphore should be cascade deleted + var semaphoreAfter = await collectionRepository.GetDefaultCollectionSemaphoresAsync(organization.Id); + Assert.Empty(semaphoreAfter); + } + + /// + /// Test that deleting an Organization cascades through OrganizationUser to DefaultCollectionSemaphore + /// Note: Cascade path is Organization -> OrganizationUser -> DefaultCollectionSemaphore (not direct) + /// + [Theory, DatabaseData] + public async Task DeleteOrganization_CascadeDeletesSemaphore_ThroughOrganizationUser( + IUserRepository userRepository, + IOrganizationRepository organizationRepository, + ICollectionRepository collectionRepository, + IOrganizationUserRepository organizationUserRepository) + { + // Arrange + var user = await userRepository.CreateTestUserAsync(); + var organization = await organizationRepository.CreateTestOrganizationAsync(); + var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); + + await collectionRepository.UpsertDefaultCollectionsAsync( + organization.Id, + [orgUser.Id], + "My Items"); + + // Verify semaphore exists + var semaphoreBefore = await collectionRepository.GetDefaultCollectionSemaphoresAsync(organization.Id); + Assert.Single(semaphoreBefore, s => s == orgUser.Id); + + // Act - Delete organization (which cascades to OrganizationUser, which cascades to semaphore) + await organizationRepository.DeleteAsync(organization); + + // Assert - Semaphore should be cascade deleted via OrganizationUser + var semaphoreAfter = await collectionRepository.GetDefaultCollectionSemaphoresAsync(organization.Id); + Assert.Empty(semaphoreAfter); + } +}