diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs index ee97655562..a549aea80e 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs @@ -1,11 +1,5 @@ -using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Repositories; -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; @@ -14,113 +8,87 @@ public class CreateDefaultCollectionsTests { /// /// Test that CreateDefaultCollectionsAsync successfully creates default collections for new users + /// with correct permissions /// - [DatabaseTheory, DatabaseData] - public async Task UpsertDefaultCollectionsAsync_CreatesDefaultCollections_Success( + [Theory, DatabaseData] + public async Task CreateDefaultCollectionsAsync_CreatesDefaultCollections_Success( IUserRepository userRepository, IOrganizationRepository organizationRepository, ICollectionRepository collectionRepository, IOrganizationUserRepository organizationUserRepository) { // Arrange - var user1 = await userRepository.CreateAsync(new User - { - Name = "Test User 1", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var user2 = await userRepository.CreateAsync(new User - { - Name = "Test User 2", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user1.Id, - Status = OrganizationUserStatusType.Confirmed, - }); - - var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user2.Id, - Status = OrganizationUserStatusType.Confirmed, - }); + var user1 = await userRepository.CreateTestUserAsync("user1"); + var user2 = await userRepository.CreateTestUserAsync("user2"); + var organization = await organizationRepository.CreateTestOrganizationAsync(); + var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1); + var orgUser2 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user2); // Act await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, - new[] { orgUser1.Id, orgUser2.Id }, + [orgUser1.Id, orgUser2.Id], "My Items"); // Assert - var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id); - var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList(); + var collectionsWithAccess = await collectionRepository.GetManyByOrganizationIdWithAccessAsync(organization.Id); + var defaultCollections = collectionsWithAccess + .Where(c => c.Item1.Type == CollectionType.DefaultUserCollection) + .ToList(); Assert.Equal(2, defaultCollections.Count); - Assert.All(defaultCollections, c => Assert.Equal("My Items", c.Name)); - Assert.All(defaultCollections, c => Assert.Equal(organization.Id, c.OrganizationId)); + Assert.All(defaultCollections, c => Assert.Equal("My Items", c.Item1.Name)); + Assert.All(defaultCollections, c => Assert.Equal(organization.Id, c.Item1.OrganizationId)); + + // Verify each user has exactly 1 collection with correct permissions + var orgUser1Collection = Assert.Single(defaultCollections, + c => c.Item2.Users.FirstOrDefault()?.Id == orgUser1.Id); + + Assert.Empty(orgUser1Collection.Item2.Groups); + + var orgUser1CollectionUser = orgUser1Collection.Item2.Users.Single(); + Assert.False(orgUser1CollectionUser.ReadOnly); + Assert.False(orgUser1CollectionUser.HidePasswords); + Assert.True(orgUser1CollectionUser.Manage); + + // Verify each user has exactly 1 collection with correct permissions + var orgUser2Collection = Assert.Single(defaultCollections, + c => c.Item2.Users.FirstOrDefault()?.Id == orgUser2.Id); + + Assert.Empty(orgUser2Collection.Item2.Groups); + + var orgUser2CollectionUser = orgUser2Collection.Item2.Users.Single(); + Assert.False(orgUser2CollectionUser.ReadOnly); + Assert.False(orgUser2CollectionUser.HidePasswords); + Assert.True(orgUser2CollectionUser.Manage); } /// /// Test that calling CreateDefaultCollectionsAsync multiple times does NOT create duplicates /// - [DatabaseTheory, DatabaseData] - public async Task UpsertDefaultCollectionsAsync_CalledMultipleTimes_DoesNotCreateDuplicates( + [Theory, DatabaseData] + public async Task CreateDefaultCollectionsAsync_CalledMultipleTimes_DoesNotCreateDuplicates( IUserRepository userRepository, IOrganizationRepository organizationRepository, ICollectionRepository collectionRepository, IOrganizationUserRepository organizationUserRepository) { // Arrange - var user = await userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user.Id, - Status = OrganizationUserStatusType.Confirmed, - }); + var user = await userRepository.CreateTestUserAsync(); + var organization = await organizationRepository.CreateTestOrganizationAsync(); + var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); // Act - Call twice await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, - new[] { orgUser.Id }, + [orgUser.Id], "My Items"); - // Second call should not create duplicate + // Second call should throw and should not create duplicate await Assert.ThrowsAnyAsync(() => collectionRepository.CreateDefaultCollectionsAsync( organization.Id, - new[] { orgUser.Id }, + [orgUser.Id], "My Items")); // Assert - Only one collection should exist @@ -128,251 +96,12 @@ public class CreateDefaultCollectionsTests var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList(); Assert.Single(defaultCollections); - } - /// - /// Test that UpsertDefaultCollectionsBulkAsync creates semaphores before collections - /// - [DatabaseTheory, DatabaseData] - public async Task UpsertDefaultCollectionsBulkAsync_CreatesSemaphoresBeforeCollections_Success( - IUserRepository userRepository, - IOrganizationRepository organizationRepository, - ICollectionRepository collectionRepository, - IOrganizationUserRepository organizationUserRepository, - DatabaseContext databaseContext) - { - // Arrange - var user = await userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user.Id, - Status = OrganizationUserStatusType.Confirmed, - }); - - // Act - await collectionRepository.UpsertDefaultCollectionsBulkAsync( - organization.Id, - new[] { orgUser.Id }, - "My Items"); - - // Assert - Verify semaphore was created - var semaphore = await databaseContext.DefaultCollectionSemaphores - .FirstOrDefaultAsync(s => s.OrganizationId == organization.Id && s.OrganizationUserId == orgUser.Id); - - Assert.NotNull(semaphore); - Assert.Equal(organization.Id, semaphore.OrganizationId); - Assert.Equal(orgUser.Id, semaphore.OrganizationUserId); - - // Verify collection was created - var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id); - var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList(); - - Assert.Single(defaultCollections); - } - - /// - /// Test that deleting an OrganizationUser cascades to DefaultCollectionSemaphore - /// - [DatabaseTheory, DatabaseData] - public async Task DeleteOrganizationUser_CascadesToSemaphore_Success( - IUserRepository userRepository, - IOrganizationRepository organizationRepository, - ICollectionRepository collectionRepository, - IOrganizationUserRepository organizationUserRepository, - DatabaseContext databaseContext) - { - // Arrange - var user = await userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user.Id, - Status = OrganizationUserStatusType.Confirmed, - }); - - await collectionRepository.CreateDefaultCollectionsAsync( - organization.Id, - new[] { orgUser.Id }, - "My Items"); - - // Verify semaphore exists - var semaphoreBefore = await databaseContext.DefaultCollectionSemaphores - .FirstOrDefaultAsync(s => s.OrganizationUserId == orgUser.Id); - Assert.NotNull(semaphoreBefore); - - // Act - Delete organization user - await organizationUserRepository.DeleteAsync(orgUser); - - // Assert - Semaphore should be cascade deleted - var semaphoreAfter = await databaseContext.DefaultCollectionSemaphores - .FirstOrDefaultAsync(s => s.OrganizationUserId == orgUser.Id); - Assert.Null(semaphoreAfter); - } - - /// - /// Test that deleting an Organization cascades through OrganizationUser to DefaultCollectionSemaphore - /// Note: Cascade path is Organization -> OrganizationUser -> DefaultCollectionSemaphore (not direct) - /// - [DatabaseTheory, DatabaseData] - public async Task DeleteOrganization_CascadesThroughOrganizationUser_Success( - IUserRepository userRepository, - IOrganizationRepository organizationRepository, - ICollectionRepository collectionRepository, - IOrganizationUserRepository organizationUserRepository, - DatabaseContext databaseContext) - { - // Arrange - var user = await userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user.Id, - Status = OrganizationUserStatusType.Confirmed, - }); - - await collectionRepository.CreateDefaultCollectionsAsync( - organization.Id, - new[] { orgUser.Id }, - "My Items"); - - // Verify semaphore exists - var semaphoreBefore = await databaseContext.DefaultCollectionSemaphores - .FirstOrDefaultAsync(s => s.OrganizationId == organization.Id); - Assert.NotNull(semaphoreBefore); - - // 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 databaseContext.DefaultCollectionSemaphores - .FirstOrDefaultAsync(s => s.OrganizationId == organization.Id); - Assert.Null(semaphoreAfter); - } - - /// - /// Test that CreateDefaultCollectionsAsync with empty user list does nothing - /// - [DatabaseTheory, DatabaseData] - public async Task UpsertDefaultCollectionsAsync_WithEmptyList_DoesNothing( - IOrganizationRepository organizationRepository, - ICollectionRepository collectionRepository) - { - // Arrange - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - // Act - await collectionRepository.CreateDefaultCollectionsAsync( - organization.Id, - Array.Empty(), - "My Items"); - - // Assert - No collections should be created - var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id); - Assert.Empty(collections); - } - - /// - /// Test that CreateDefaultCollectionsAsync creates CollectionUser entries with correct permissions - /// - [DatabaseTheory, DatabaseData] - public async Task UpsertDefaultCollectionsAsync_CreatesCollectionUsersWithCorrectPermissions( - IUserRepository userRepository, - IOrganizationRepository organizationRepository, - ICollectionRepository collectionRepository, - IOrganizationUserRepository organizationUserRepository) - { - // Arrange - var user = await userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - - var organization = await organizationRepository.CreateAsync(new Organization - { - Name = "Test Org", - PlanType = PlanType.EnterpriseAnnually, - Plan = "Test Plan", - BillingEmail = "billing@email.com" - }); - - var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser - { - OrganizationId = organization.Id, - UserId = user.Id, - Status = OrganizationUserStatusType.Confirmed, - }); - - // Act - await collectionRepository.CreateDefaultCollectionsAsync( - organization.Id, - new[] { orgUser.Id }, - "My Items"); - - // Assert - var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id); - var defaultCollection = collections.First(c => c.Type == CollectionType.DefaultUserCollection); - - var collectionUsers = await collectionRepository.GetManyUsersByIdAsync(defaultCollection.Id); - var collectionUser = collectionUsers.Single(); - - Assert.Equal(orgUser.Id, collectionUser.Id); - Assert.False(collectionUser.ReadOnly); - Assert.False(collectionUser.HidePasswords); - Assert.True(collectionUser.Manage); + var access = await collectionRepository.GetManyUsersByIdAsync(defaultCollections.Single().Id); + var userAccess = Assert.Single(access); + Assert.Equal(orgUser.Id, userAccess.Id); + Assert.False(userAccess.ReadOnly); + Assert.False(userAccess.HidePasswords); + Assert.True(userAccess.Manage); } }