1
0
mirror of https://github.com/bitwarden/server synced 2026-01-28 15:23:38 +00:00

Added tests

This commit is contained in:
Jared McCannon
2026-01-21 08:17:51 -06:00
parent c266fe7339
commit c3bbed780d
2 changed files with 415 additions and 25 deletions

View File

@@ -104,19 +104,18 @@ public class RestoreOrganizationUserCommand(
var status = OrganizationService.GetPriorActiveOrganizationUserStatusType(organizationUser);
await organizationUserRepository.RestoreAsync(organizationUser.Id, status);
organizationUser.Status = status;
if (organizationUser.UserId.HasValue
&& (await policyRequirementQuery.GetAsync<OrganizationDataOwnershipPolicyRequirement>(organizationUser.UserId
.Value)).State == OrganizationDataOwnershipState.Enabled
&& organizationUser.Status == OrganizationUserStatusType.Confirmed
&& status == OrganizationUserStatusType.Confirmed
&& !string.IsNullOrWhiteSpace(defaultCollectionName))
{
await collectionRepository.CreateDefaultCollectionsAsync(organizationUser.OrganizationId,
[organizationUser.Id],
defaultCollectionName);
}
organizationUser.Status = status;
}
private async Task CheckUserForOtherFreeOrganizationOwnershipAsync(OrganizationUser organizationUser)

View File

@@ -37,7 +37,7 @@ public class RestoreOrganizationUserCommandTests
Sponsored = 0,
Users = 1
});
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
@@ -81,7 +81,7 @@ public class RestoreOrganizationUserCommandTests
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("you cannot restore yourself", exception.Message.ToLowerInvariant());
@@ -107,7 +107,7 @@ public class RestoreOrganizationUserCommandTests
RestoreUser_Setup(organization, restoringUser, organizationUser, sutProvider);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, restoringUser.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, restoringUser.Id, null));
Assert.Contains("only owners can restore other owners", exception.Message.ToLowerInvariant());
@@ -133,7 +133,7 @@ public class RestoreOrganizationUserCommandTests
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("already active", exception.Message.ToLowerInvariant());
@@ -172,7 +172,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant());
@@ -216,7 +216,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
@@ -272,7 +272,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
@@ -309,7 +309,7 @@ public class RestoreOrganizationUserCommandTests
Sponsored = 0,
Users = 1
});
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
@@ -349,7 +349,7 @@ public class RestoreOrganizationUserCommandTests
}
]));
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
@@ -395,7 +395,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com is not compliant with the single organization policy", exception.Message.ToLowerInvariant());
@@ -447,7 +447,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com is not compliant with the single organization and two-step login policy", exception.Message.ToLowerInvariant());
@@ -509,7 +509,7 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Contains("test@bitwarden.com is not compliant with the single organization and two-step login policy", exception.Message.ToLowerInvariant());
@@ -548,7 +548,7 @@ public class RestoreOrganizationUserCommandTests
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
@@ -599,7 +599,7 @@ public class RestoreOrganizationUserCommandTests
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null));
Assert.Equal("User is an owner/admin of another free organization. Please have them upgrade to a paid plan to restore their account.", exception.Message);
}
@@ -651,7 +651,7 @@ public class RestoreOrganizationUserCommandTests
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await organizationUserRepository
.Received(1)
@@ -707,7 +707,7 @@ public class RestoreOrganizationUserCommandTests
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
await organizationUserRepository
.Received(1)
@@ -782,7 +782,7 @@ public class RestoreOrganizationUserCommandTests
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, new[] { orgUser1.Id, orgUser2.Id }, owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, new[] { orgUser1.Id, orgUser2.Id }, owner.Id, userService, null);
// Assert
Assert.Equal(2, result.Count);
@@ -843,7 +843,7 @@ public class RestoreOrganizationUserCommandTests
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService, null);
// Assert
Assert.Equal(3, result.Count);
@@ -914,7 +914,7 @@ public class RestoreOrganizationUserCommandTests
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService, null);
// Assert
Assert.Equal(3, result.Count);
@@ -992,7 +992,7 @@ public class RestoreOrganizationUserCommandTests
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService, null);
// Assert
Assert.Equal(3, result.Count);
@@ -1056,7 +1056,7 @@ public class RestoreOrganizationUserCommandTests
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id], owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id], owner.Id, userService, null);
// Assert
Assert.Single(result);
@@ -1107,7 +1107,7 @@ public class RestoreOrganizationUserCommandTests
.Returns([new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication }]);
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id], owner.Id, userService);
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id], owner.Id, userService, null);
Assert.Single(result);
Assert.Equal(string.Empty, result[0].Item2);
@@ -1138,5 +1138,396 @@ public class RestoreOrganizationUserCommandTests
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(requestingOrganizationUser != null && requestingOrganizationUser.Type is OrganizationUserType.Owner);
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(requestingOrganizationUser != null && (requestingOrganizationUser.Type is OrganizationUserType.Owner or OrganizationUserType.Admin));
// Setup default disabled OrganizationDataOwnershipPolicyRequirement for any user
sutProvider.GetDependency<IPolicyRequirementQuery>()
.GetAsync<OrganizationDataOwnershipPolicyRequirement>(Arg.Any<Guid>())
.Returns(new OrganizationDataOwnershipPolicyRequirement(OrganizationDataOwnershipState.Disabled, []));
}
private static void SetupOrganizationDataOwnershipPolicy(
SutProvider<RestoreOrganizationUserCommand> sutProvider,
Guid userId,
Guid organizationId,
OrganizationUserStatusType orgUserStatus,
bool policyEnabled)
{
var policyDetails = policyEnabled
? new List<PolicyDetails>
{
new()
{
OrganizationId = organizationId,
OrganizationUserId = Guid.NewGuid(),
OrganizationUserStatus = orgUserStatus,
PolicyType = PolicyType.OrganizationDataOwnership
}
}
: new List<PolicyDetails>();
var policyRequirement = new OrganizationDataOwnershipPolicyRequirement(
policyEnabled ? OrganizationDataOwnershipState.Enabled : OrganizationDataOwnershipState.Disabled,
policyDetails);
sutProvider.GetDependency<IPolicyRequirementQuery>()
.GetAsync<OrganizationDataOwnershipPolicyRequirement>(userId)
.Returns(policyRequirement);
}
#region Single User Restore - Default Collection Tests
[Theory, BitAutoData]
public async Task RestoreUser_WithDataOwnershipPolicyEnabled_AndConfirmedUser_CreatesDefaultCollection(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organizationUser.Email = null; // This causes user to restore to Confirmed status
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
SetupOrganizationDataOwnershipPolicy(
sutProvider,
organizationUser.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, defaultCollectionName);
// Assert
await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.CreateDefaultCollectionsAsync(
organization.Id,
Arg.Is<IEnumerable<Guid>>(ids => ids.Single() == organizationUser.Id),
defaultCollectionName);
}
[Theory, BitAutoData]
public async Task RestoreUser_WithDataOwnershipPolicyDisabled_DoesNotCreateDefaultCollection(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organizationUser.Email = null; // This causes user to restore to Confirmed status
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
SetupOrganizationDataOwnershipPolicy(
sutProvider,
organizationUser.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: false);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, defaultCollectionName);
// Assert
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task RestoreUser_WithNullDefaultCollectionName_DoesNotCreateDefaultCollection(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organizationUser.Email = null; // This causes user to restore to Confirmed status
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
SetupOrganizationDataOwnershipPolicy(
sutProvider,
organizationUser.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null);
// Assert
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
[Theory]
[BitAutoData("")]
[BitAutoData(" ")]
public async Task RestoreUser_WithEmptyOrWhitespaceDefaultCollectionName_DoesNotCreateDefaultCollection(
string defaultCollectionName,
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organizationUser.Email = null; // This causes user to restore to Confirmed status
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
SetupOrganizationDataOwnershipPolicy(
sutProvider,
organizationUser.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, defaultCollectionName);
// Assert
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task RestoreUser_UserRestoredToInvitedStatus_DoesNotCreateDefaultCollection(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organization.PlanType = PlanType.EnterpriseAnnually; // Non-Free plan to avoid ownership check requiring UserId
organizationUser.Email = "test@example.com"; // Non-null email means user restores to Invited status
organizationUser.UserId = null; // User not linked to account yet
organizationUser.Key = null;
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, defaultCollectionName);
// Assert - User was restored to Invited status, so no collection should be created
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task RestoreUser_WithNoUserId_DoesNotCreateDefaultCollection(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
organization.PlanType = PlanType.EnterpriseAnnually; // Non-Free plan to avoid ownership check requiring UserId
organizationUser.UserId = null; // No linked user account
organizationUser.Email = "test@example.com";
organizationUser.Key = null;
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
// Act
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, defaultCollectionName);
// Assert
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
#endregion
#region Bulk User Restore - Default Collection Tests
[Theory, BitAutoData]
public async Task RestoreUsers_Bulk_WithDataOwnershipPolicy_CreatesCollectionsForEligibleUsers(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
RestoreUser_Setup(organization, owner, orgUser1, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var userService = Substitute.For<IUserService>();
// orgUser1: Will restore to Confirmed (Email = null)
orgUser1.Email = null;
orgUser1.OrganizationId = organization.Id;
// orgUser2: Will restore to Invited (Email not null)
orgUser2.Email = "test@example.com";
orgUser2.UserId = null;
orgUser2.Key = null;
orgUser2.OrganizationId = organization.Id;
organizationUserRepository
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id)))
.Returns([orgUser1, orgUser2]);
// Setup policy for orgUser1 (the one with UserId)
SetupOrganizationDataOwnershipPolicy(
sutProvider,
orgUser1.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>
{
(orgUser1.UserId!.Value, true)
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(
organization.Id,
[orgUser1.Id, orgUser2.Id],
owner.Id,
userService,
defaultCollectionName);
// Assert - Only orgUser1 should have a collection created (Confirmed with policy enabled)
await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.CreateDefaultCollectionsAsync(
organization.Id,
Arg.Is<IEnumerable<Guid>>(ids => ids.Single() == orgUser1.Id),
defaultCollectionName);
}
[Theory, BitAutoData]
public async Task RestoreUsers_Bulk_WithMixedPolicyStates_OnlyCreatesForEnabledPolicy(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2,
string defaultCollectionName,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
RestoreUser_Setup(organization, owner, orgUser1, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var userService = Substitute.For<IUserService>();
// Both users will restore to Confirmed
orgUser1.Email = null;
orgUser1.OrganizationId = organization.Id;
orgUser2.Email = null;
orgUser2.OrganizationId = organization.Id;
organizationUserRepository
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id)))
.Returns([orgUser1, orgUser2]);
// Setup policy enabled only for orgUser1
SetupOrganizationDataOwnershipPolicy(
sutProvider,
orgUser1.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
// Setup policy disabled for orgUser2
SetupOrganizationDataOwnershipPolicy(
sutProvider,
orgUser2.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: false);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>
{
(orgUser1.UserId!.Value, true),
(orgUser2.UserId!.Value, true)
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(
organization.Id,
[orgUser1.Id, orgUser2.Id],
owner.Id,
userService,
defaultCollectionName);
// Assert - Only orgUser1 should have a collection created (policy enabled)
await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.CreateDefaultCollectionsAsync(
organization.Id,
Arg.Is<IEnumerable<Guid>>(ids => ids.Single() == orgUser1.Id),
defaultCollectionName);
}
[Theory, BitAutoData]
public async Task RestoreUsers_Bulk_WithNullCollectionName_DoesNotCreateAnyCollections(
Organization organization,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2,
SutProvider<RestoreOrganizationUserCommand> sutProvider)
{
// Arrange
RestoreUser_Setup(organization, owner, orgUser1, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var userService = Substitute.For<IUserService>();
// Both users will restore to Confirmed
orgUser1.Email = null;
orgUser1.OrganizationId = organization.Id;
orgUser2.Email = null;
orgUser2.OrganizationId = organization.Id;
organizationUserRepository
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id)))
.Returns([orgUser1, orgUser2]);
// Setup policy enabled for both users
SetupOrganizationDataOwnershipPolicy(
sutProvider,
orgUser1.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
SetupOrganizationDataOwnershipPolicy(
sutProvider,
orgUser2.UserId!.Value,
organization.Id,
OrganizationUserStatusType.Revoked,
policyEnabled: true);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>
{
(orgUser1.UserId!.Value, true),
(orgUser2.UserId!.Value, true)
});
// Act
var result = await sutProvider.Sut.RestoreUsersAsync(
organization.Id,
[orgUser1.Id, orgUser2.Id],
owner.Id,
userService,
null); // Null collection name
// Assert - No collections should be created
await sutProvider.GetDependency<ICollectionRepository>()
.DidNotReceive()
.CreateDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
}
#endregion
}