From 546b5a08498c7d5bf2398f871c8efbb0430a2f18 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Thu, 27 Feb 2025 10:21:01 -0500 Subject: [PATCH] [pm-17804] Fix deferred execution issue in EF CreateManyAsync (#5425) * Add failing repository tests * test * clean up comments --------- Co-authored-by: Thomas Rittson --- .../OrganizationUserRepository.cs | 1 + .../OrganizationUserRepositoryTests.cs | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs index ef6460df0e..0165360099 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs @@ -46,6 +46,7 @@ public class OrganizationUserRepository : Repository> CreateManyAsync(IEnumerable organizationUsers) { + organizationUsers = organizationUsers.ToList(); if (!organizationUsers.Any()) { return new List(); diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepositoryTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepositoryTests.cs index e82be49173..092ab95a14 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepositoryTests.cs @@ -2,6 +2,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Repositories; +using Bit.Core.Utilities; using Xunit; namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories; @@ -354,4 +355,73 @@ public class OrganizationUserRepositoryTests Assert.Single(responseModel); Assert.Equal(orgUser1.Id, responseModel.Single().Id); } + + [DatabaseTheory, DatabaseData] + public async Task CreateManyAsync_NoId_Works(IOrganizationRepository organizationRepository, + IUserRepository userRepository, + IOrganizationUserRepository organizationUserRepository) + { + // Arrange + var user1 = await userRepository.CreateTestUserAsync("user1"); + var user2 = await userRepository.CreateTestUserAsync("user2"); + var user3 = await userRepository.CreateTestUserAsync("user3"); + List users = [user1, user2, user3]; + + var org = await organizationRepository.CreateAsync(new Organization + { + Name = $"test-{Guid.NewGuid()}", + BillingEmail = "billing@example.com", // TODO: EF does not enforce this being NOT NULL + Plan = "Test", // TODO: EF does not enforce this being NOT NULl + }); + + var orgUsers = users.Select(u => new OrganizationUser + { + OrganizationId = org.Id, + UserId = u.Id, + Status = OrganizationUserStatusType.Confirmed, + Type = OrganizationUserType.Owner + }); + + var createdOrgUserIds = await organizationUserRepository.CreateManyAsync(orgUsers); + + var readOrgUsers = await organizationUserRepository.GetManyByOrganizationAsync(org.Id, null); + var readOrgUserIds = readOrgUsers.Select(ou => ou.Id); + + Assert.Equal(createdOrgUserIds.ToHashSet(), readOrgUserIds.ToHashSet()); + } + + [DatabaseTheory, DatabaseData] + public async Task CreateManyAsync_WithId_Works(IOrganizationRepository organizationRepository, + IUserRepository userRepository, + IOrganizationUserRepository organizationUserRepository) + { + // Arrange + var user1 = await userRepository.CreateTestUserAsync("user1"); + var user2 = await userRepository.CreateTestUserAsync("user2"); + var user3 = await userRepository.CreateTestUserAsync("user3"); + List users = [user1, user2, user3]; + + var org = await organizationRepository.CreateAsync(new Organization + { + Name = $"test-{Guid.NewGuid()}", + BillingEmail = "billing@example.com", // TODO: EF does not enforce this being NOT NULL + Plan = "Test", // TODO: EF does not enforce this being NOT NULl + }); + + var orgUsers = users.Select(u => new OrganizationUser + { + Id = CoreHelpers.GenerateComb(), // generate ID ahead of time + OrganizationId = org.Id, + UserId = u.Id, + Status = OrganizationUserStatusType.Confirmed, + Type = OrganizationUserType.Owner + }); + + var createdOrgUserIds = await organizationUserRepository.CreateManyAsync(orgUsers); + + var readOrgUsers = await organizationUserRepository.GetManyByOrganizationAsync(org.Id, null); + var readOrgUserIds = readOrgUsers.Select(ou => ou.Id); + + Assert.Equal(createdOrgUserIds.ToHashSet(), readOrgUserIds.ToHashSet()); + } }