mirror of
https://github.com/bitwarden/server.git
synced 2025-05-25 05:21:03 -05:00
Added more tests for the updates
This commit is contained in:
parent
1dbe37a250
commit
bd5189491e
@ -78,7 +78,6 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
return new Success<IEnumerable<OrganizationUser>>([]);
|
||||
}
|
||||
|
||||
// Validate we can add those seats
|
||||
var validationResult = await inviteUsersValidation.ValidateAsync(new InviteUserOrganizationValidationRequest
|
||||
{
|
||||
Invites = invitesToSend.ToArray(),
|
||||
|
@ -23,7 +23,7 @@ public class PasswordManagerSubscriptionUpdate
|
||||
|
||||
public int? UpdatedSeatTotal => Seats + SeatsRequiredToAdd;
|
||||
|
||||
public bool MaxSeatsReached => Seats.HasValue && MaxAutoScaleSeats.HasValue && Seats.Value == MaxAutoScaleSeats.Value;
|
||||
public bool MaxSeatsReached => UpdatedSeatTotal.HasValue && MaxAutoScaleSeats.HasValue && UpdatedSeatTotal.Value == MaxAutoScaleSeats.Value;
|
||||
|
||||
public Plan.PasswordManagerPlanFeatures PasswordManagerPlan { get; }
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers;
|
||||
|
||||
public static class InviteScimOrganizationUserRequestHelpers
|
||||
{
|
||||
public static InviteScimOrganizationUserRequest GetInviteScimOrganizationUserRequestDefault(string email,
|
||||
OrganizationDto organizationDto, DateTimeOffset performedAt, string externalId) =>
|
||||
InviteScimOrganizationUserRequest.Create(
|
||||
OrganizationUserSingleEmailInvite.Create(
|
||||
email,
|
||||
[],
|
||||
OrganizationUserType.User,
|
||||
new Permissions(),
|
||||
false),
|
||||
organizationDto,
|
||||
performedAt,
|
||||
externalId
|
||||
);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers;
|
||||
|
||||
public static class InviteUserOrganizationValidationRequestHelpers
|
||||
{
|
||||
public static InviteUserOrganizationValidationRequest GetInviteValidationRequestMock(InviteScimOrganizationUserRequest request,
|
||||
OrganizationDto organizationDto) =>
|
||||
new()
|
||||
{
|
||||
Invites =
|
||||
[
|
||||
OrganizationUserInviteDto.Create(request.Invite.Email,
|
||||
OrganizationUserInvite.Create(request.Invite, request.ExternalId), organizationDto.OrganizationId)
|
||||
],
|
||||
Organization = organizationDto,
|
||||
PerformedBy = Guid.Empty,
|
||||
PerformedAt = request.PerformedAt,
|
||||
OccupiedPmSeats = 0,
|
||||
OccupiedSmSeats = 0,
|
||||
PasswordManagerSubscriptionUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, 0, 0),
|
||||
SecretsManagerSubscriptionUpdate = SecretsManagerSubscriptionUpdate.Create(organizationDto, 0, 0, 0)
|
||||
};
|
||||
|
||||
public static InviteUserOrganizationValidationRequest WithPasswordManagerUpdate(this InviteUserOrganizationValidationRequest request, PasswordManagerSubscriptionUpdate passwordManagerSubscriptionUpdate) =>
|
||||
new()
|
||||
{
|
||||
Invites = request.Invites,
|
||||
Organization = request.Organization,
|
||||
PerformedBy = request.PerformedBy,
|
||||
PerformedAt = request.PerformedAt,
|
||||
OccupiedPmSeats = request.OccupiedPmSeats,
|
||||
OccupiedSmSeats = request.OccupiedSmSeats,
|
||||
PasswordManagerSubscriptionUpdate = passwordManagerSubscriptionUpdate,
|
||||
SecretsManagerSubscriptionUpdate = request.SecretsManagerSubscriptionUpdate
|
||||
};
|
||||
|
||||
public static InviteUserOrganizationValidationRequest WithSecretsManagerUpdate(this InviteUserOrganizationValidationRequest request, SecretsManagerSubscriptionUpdate secretsManagerSubscriptionUpdate) =>
|
||||
new()
|
||||
{
|
||||
Invites = request.Invites,
|
||||
Organization = request.Organization,
|
||||
PerformedBy = request.PerformedBy,
|
||||
PerformedAt = request.PerformedAt,
|
||||
OccupiedPmSeats = request.OccupiedPmSeats,
|
||||
OccupiedSmSeats = request.OccupiedSmSeats,
|
||||
PasswordManagerSubscriptionUpdate = request.PasswordManagerSubscriptionUpdate,
|
||||
SecretsManagerSubscriptionUpdate = secretsManagerSubscriptionUpdate
|
||||
};
|
||||
}
|
@ -8,7 +8,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.V
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Commands;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Models.StaticStore;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
using Bit.Core.Repositories;
|
||||
@ -18,6 +18,8 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using static Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers.InviteScimOrganizationUserRequestHelpers;
|
||||
using static Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers.InviteUserOrganizationValidationRequestHelpers;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
|
||||
@ -39,37 +41,18 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = InviteScimOrganizationUserRequest.Create(
|
||||
OrganizationUserSingleEmailInvite.Create(
|
||||
user.Email,
|
||||
[],
|
||||
OrganizationUserType.User,
|
||||
new Permissions(),
|
||||
false),
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId
|
||||
);
|
||||
externalId);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
|
||||
.Returns([user.Email]);
|
||||
|
||||
var validationRequest = new InviteUserOrganizationValidationRequest
|
||||
{
|
||||
Invites = [],
|
||||
Organization = organizationDto,
|
||||
PerformedBy = Guid.Empty,
|
||||
PerformedAt = request.PerformedAt,
|
||||
OccupiedPmSeats = 0,
|
||||
OccupiedSmSeats = 0,
|
||||
PasswordManagerSubscriptionUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, 0, 0),
|
||||
SecretsManagerSubscriptionUpdate = SecretsManagerSubscriptionUpdate.Create(organizationDto, 0, 0, 0)
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(validationRequest));
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
@ -93,29 +76,22 @@ public class InviteOrganizationUserCommandTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task InviteScimOrganizationUserAsync_WhenEmailDoesNotExistAndRequestIsValid_ThenUserIsSavedAndInviteIsSent(
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = address.Address;
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = InviteScimOrganizationUserRequest.Create(
|
||||
OrganizationUserSingleEmailInvite.Create(
|
||||
user.Email,
|
||||
[],
|
||||
OrganizationUserType.User,
|
||||
new Permissions(),
|
||||
false),
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId
|
||||
);
|
||||
externalId);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
|
||||
@ -125,21 +101,9 @@ public class InviteOrganizationUserCommandTests
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
var validationRequest = new InviteUserOrganizationValidationRequest
|
||||
{
|
||||
Invites = [OrganizationUserInviteDto.Create(request.Invite.Email, OrganizationUserInvite.Create(request.Invite, request.ExternalId), organization.Id)],
|
||||
Organization = organizationDto,
|
||||
PerformedBy = Guid.Empty,
|
||||
PerformedAt = request.PerformedAt,
|
||||
OccupiedPmSeats = 0,
|
||||
OccupiedSmSeats = 0,
|
||||
PasswordManagerSubscriptionUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, 0, 0),
|
||||
SecretsManagerSubscriptionUpdate = SecretsManagerSubscriptionUpdate.Create(organizationDto, 0, 0, 0)
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(validationRequest));
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
@ -158,4 +122,234 @@ public class InviteOrganizationUserCommandTests
|
||||
invite.Organization == organization &&
|
||||
invite.Users.Count(x => x.Email == user.Email) == 1));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task InviteScimOrganizationUserAsync_WhenEmailIsNewAndRequestIsInvalid_ThenFailureIsReturnedWithValidationFailureReason(
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
const string errorMessage = "Org cannot add user for some given reason";
|
||||
|
||||
user.Email = address.Address;
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
|
||||
.Returns([]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Invalid<InviteUserOrganizationValidationRequest>(errorMessage));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<Failure<OrganizationUser>>(result);
|
||||
var failure = result as Failure<OrganizationUser>;
|
||||
|
||||
Assert.Equal(errorMessage, failure.ErrorMessage);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.DidNotReceive()
|
||||
.CreateManyAsync(Arg.Any<IEnumerable<CreateOrganizationUser>>());
|
||||
|
||||
sutProvider.GetDependency<ISendOrganizationInvitesCommand>()
|
||||
.DidNotReceive()
|
||||
.SendInvitesAsync(Arg.Any<SendInvitesRequest>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task InviteScimOrganizationUserAsync_WhenValidInviteCausesOrganizationToReachMaxSeats_ThenOrganizationOwnersShouldBeNotified(
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
OrganizationUserUserDetails ownerDetails,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = address.Address;
|
||||
organization.Seats = 1;
|
||||
organization.MaxAutoscaleSeats = 2;
|
||||
ownerDetails.Type = OrganizationUserType.Owner;
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId);
|
||||
|
||||
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
|
||||
orgUserRepository
|
||||
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
|
||||
.Returns([]);
|
||||
orgUserRepository
|
||||
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns([ownerDetails]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto)
|
||||
.WithPasswordManagerUpdate(PasswordManagerSubscriptionUpdate.Create(organizationDto, organization.Seats.Value, 1))));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<Success<OrganizationUser>>(result);
|
||||
|
||||
sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendOrganizationMaxSeatLimitReachedEmailAsync(organization,
|
||||
organizationDto.MaxAutoScaleSeats.Value,
|
||||
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == ownerDetails.Email)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task InviteScimOrganizationUserAsync_WhenValidInviteIncreasesSeats_ThenSeatTotalShouldBeUpdated(
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
OrganizationUserUserDetails ownerDetails,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = address.Address;
|
||||
organization.Seats = 1;
|
||||
organization.MaxAutoscaleSeats = 2;
|
||||
ownerDetails.Type = OrganizationUserType.Owner;
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId);
|
||||
|
||||
var passwordManagerUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, organization.Seats.Value, 1);
|
||||
|
||||
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
|
||||
orgUserRepository
|
||||
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
|
||||
.Returns([]);
|
||||
orgUserRepository
|
||||
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns([ownerDetails]);
|
||||
|
||||
var orgRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
|
||||
orgRepository.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto)
|
||||
.WithPasswordManagerUpdate(passwordManagerUpdate)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<Success<OrganizationUser>>(result);
|
||||
|
||||
sutProvider.GetDependency<IPaymentService>()
|
||||
.AdjustSeatsAsync(organization, organizationDto.Plan, passwordManagerUpdate.SeatsRequiredToAdd);
|
||||
|
||||
orgRepository.Received(1).ReplaceAsync(Arg.Is<Organization>(x => x.Seats == passwordManagerUpdate.UpdatedSeatTotal));
|
||||
|
||||
sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.Received(1)
|
||||
.UpsertOrganizationAbilityAsync(Arg.Is<Organization>(x => x.Seats == passwordManagerUpdate.UpdatedSeatTotal));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task InviteScimOrganizationUserAsync_WhenValidInviteIncreasesSecretsManagerSeats_ThenSecretsManagerShouldBeUpdated(
|
||||
MailAddress address,
|
||||
Organization organization,
|
||||
OrganizationUser user,
|
||||
FakeTimeProvider timeProvider,
|
||||
string externalId,
|
||||
OrganizationUserUserDetails ownerDetails,
|
||||
SutProvider<InviteOrganizationUsersCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = address.Address;
|
||||
organization.Seats = 1;
|
||||
organization.SmSeats = 1;
|
||||
organization.MaxAutoscaleSeats = 2;
|
||||
ownerDetails.Type = OrganizationUserType.Owner;
|
||||
|
||||
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||
|
||||
var request = GetInviteScimOrganizationUserRequestDefault(user.Email,
|
||||
organizationDto,
|
||||
timeProvider.GetUtcNow(),
|
||||
externalId);
|
||||
|
||||
var secretsManagerSubscriptionUpdate = SecretsManagerSubscriptionUpdate.Create(
|
||||
organizationDto,
|
||||
organization.SmSeats.Value,
|
||||
1,
|
||||
organization.Seats.Value);
|
||||
|
||||
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
|
||||
orgUserRepository
|
||||
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
|
||||
.Returns([]);
|
||||
orgUserRepository
|
||||
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns([ownerDetails]);
|
||||
|
||||
var orgRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
|
||||
orgRepository.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidation>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto)
|
||||
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<Success<OrganizationUser>>(result);
|
||||
|
||||
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
||||
.Received(1)
|
||||
.UpdateSubscriptionAsync(Arg.Is<Core.Models.Business.SecretsManagerSubscriptionUpdate>(update =>
|
||||
update.SmSeats == secretsManagerSubscriptionUpdate.UpdatedSeatTotal));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user