mirror of
https://github.com/bitwarden/server
synced 2026-01-03 17:14:00 +00:00
[PM-28260] Optimize bulk reinvite endpoint (#6670)
* Implement optimized bulk invite resend command - Added IBulkResendOrganizationInvitesCommand interface to define the bulk resend operation. - Created BulkResendOrganizationInvitesCommand class to handle the logic for resending invites to multiple organization users. - Integrated logging and validation to ensure only valid users receive invites. - Included error handling for non-existent organizations and invalid user statuses. * Add unit tests for BulkResendOrganizationInvitesCommand - Implemented comprehensive test cases for the BulkResendOrganizationInvitesCommand class. - Validated user statuses and ensured correct handling of valid and invalid users during bulk invite resends. - Included tests for scenarios such as organization not found and empty user lists. - Utilized Xunit and NSubstitute for effective testing and mocking of dependencies. * Add IBulkResendOrganizationInvitesCommand to service collection - Registered IBulkResendOrganizationInvitesCommand in the service collection for dependency injection. * Update OrganizationUsersController to utilize IBulkResendOrganizationInvitesCommand - Added IBulkResendOrganizationInvitesCommand to the OrganizationUsersController for handling bulk invite resends based on feature flag. - Updated BulkReinvite method to conditionally use the new command or the legacy service based on the feature flag status. - Enhanced unit tests to verify correct command usage depending on feature flag state, ensuring robust testing for both scenarios.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class BulkResendOrganizationInvitesCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task BulkResendInvitesAsync_ValidatesUsersAndSendsBatchInvite(
|
||||
Organization organization,
|
||||
OrganizationUser validUser1,
|
||||
OrganizationUser validUser2,
|
||||
OrganizationUser acceptedUser,
|
||||
OrganizationUser wrongOrgUser,
|
||||
SutProvider<BulkResendOrganizationInvitesCommand> sutProvider)
|
||||
{
|
||||
validUser1.OrganizationId = organization.Id;
|
||||
validUser1.Status = OrganizationUserStatusType.Invited;
|
||||
validUser2.OrganizationId = organization.Id;
|
||||
validUser2.Status = OrganizationUserStatusType.Invited;
|
||||
acceptedUser.OrganizationId = organization.Id;
|
||||
acceptedUser.Status = OrganizationUserStatusType.Accepted;
|
||||
wrongOrgUser.OrganizationId = Guid.NewGuid();
|
||||
wrongOrgUser.Status = OrganizationUserStatusType.Invited;
|
||||
|
||||
var users = new List<OrganizationUser> { validUser1, validUser2, acceptedUser, wrongOrgUser };
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(userIds).Returns(users);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
var result = (await sutProvider.Sut.BulkResendInvitesAsync(organization.Id, null, userIds)).ToList();
|
||||
|
||||
Assert.Equal(4, result.Count);
|
||||
Assert.Equal(2, result.Count(r => string.IsNullOrEmpty(r.Item2)));
|
||||
Assert.Equal(2, result.Count(r => r.Item2 == "User invalid."));
|
||||
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>()
|
||||
.Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(req =>
|
||||
req.Organization == organization &&
|
||||
req.Users.Length == 2 &&
|
||||
req.InitOrganization == false));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task BulkResendInvitesAsync_AllInvalidUsers_DoesNotSendInvites(
|
||||
Organization organization,
|
||||
List<OrganizationUser> organizationUsers,
|
||||
SutProvider<BulkResendOrganizationInvitesCommand> sutProvider)
|
||||
{
|
||||
foreach (var user in organizationUsers)
|
||||
{
|
||||
user.OrganizationId = organization.Id;
|
||||
user.Status = OrganizationUserStatusType.Confirmed;
|
||||
}
|
||||
|
||||
var userIds = organizationUsers.Select(u => u.Id).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(userIds).Returns(organizationUsers);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
var result = (await sutProvider.Sut.BulkResendInvitesAsync(organization.Id, null, userIds)).ToList();
|
||||
|
||||
Assert.Equal(organizationUsers.Count, result.Count);
|
||||
Assert.All(result, r => Assert.Equal("User invalid.", r.Item2));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().DidNotReceive()
|
||||
.SendInvitesAsync(Arg.Any<SendInvitesRequest>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task BulkResendInvitesAsync_OrganizationNotFound_ThrowsNotFoundException(
|
||||
Guid organizationId,
|
||||
List<Guid> userIds,
|
||||
List<OrganizationUser> organizationUsers,
|
||||
SutProvider<BulkResendOrganizationInvitesCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(userIds).Returns(organizationUsers);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns((Organization?)null);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.BulkResendInvitesAsync(organizationId, null, userIds));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task BulkResendInvitesAsync_EmptyUserList_ReturnsEmpty(
|
||||
Organization organization,
|
||||
SutProvider<BulkResendOrganizationInvitesCommand> sutProvider)
|
||||
{
|
||||
var emptyUserIds = new List<Guid>();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(emptyUserIds).Returns(new List<OrganizationUser>());
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
var result = await sutProvider.Sut.BulkResendInvitesAsync(organization.Id, null, emptyUserIds);
|
||||
|
||||
Assert.Empty(result);
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().DidNotReceive()
|
||||
.SendInvitesAsync(Arg.Any<SendInvitesRequest>());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user