mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[PM-16811] - SCIM Invite Users Optimizations (#5398)
* WIP changes for Invite User optimization from Scim * feature flag string * Added plan validation to PasswordManagerInviteUserValidation. Cleaned up a few things. * Added Secrets Manager Validations and Tests. * Added bulk procedure for saving users, collections and groups from inviting. Added test to validate Ef and Sproc * Created SendOrganizationInvitesCommand and moved some tests from OrgServiceTests. Fixed some tests in org service in relation to moving out SendOrgInviteCommand code. Added side effects to InviteOrganizationUsersCommand * First test of new command. * Added test to verify valid request with a user calls db method and sends the invite * Added more tests for the updates * Added integration test around enabling feature and sending invite via scim. Did a bit of refactoring on the SM validation. Fixed couple bugs found. * Switching over to a local factory. * created response model and split interface out. * switched to initialization block * Moved to private method. Made ScimInvite inherit the single invite base model. Moved create methods to constructors. A few more CR changes included. * Moved `FromOrganization` mapper method to a constructor * Updated to use new pricing client. Supressed null dereference errors. * Fixing bad merge. * Rename of OrgDto * undoing this * Moved into class * turned into a switch statement * Separated into separate files. * Renamed dto and added ctor * Dto rename. Moved from static methods to ctors * Removed unused request model * changes from main * missed value * Fixed some compilation errors. * Fixed some changes. * Removed comment * fixed compiler warning. * Refactored to use new ValidationResult pattern. added mapping method. * Added throwing of Failure as the previous implementation would have. * Cleaned up return. * fixing test. * Made HasSecretsManagerStandalone return if org doesn't have sm. Added overload for lighter weight model and moved common code to private method. * Fixed tests. * Made public method private. added some comments. * Refactor validation parameter to improve clarity and consistency. Added XML doc * fixed test * Removed test only constructor from InviteOrganization * Separated old and new code explicitly. Moved old code checks down into new code as well. Added error and mapper to Failure<T> * Variable/Field/Property renames * Renamed InviteUsersValidation to InviteUsersValidator * Rename for InvitingUserOrganizationValidation to InvitingUserOrganizationValidator * PasswordManagerInviteUserValidation to PasswordManagerInviteUserValidator * Moved XML comment. Added check to see if additional seats are needed. * Fixing name. * Updated names. * Corrected double negation. * Added groups and collection and users checks. * Fixed comment. Fixed multiple enumeration. Changed variable name. * Cleaned up DTO models. Moved some validation steps around. A few quick fixes to address CR concerns. Still need to move a few things yet. * Fixed naming in subscription update models. * put back in the request for now. * Quick rename * Added provider email addresses as well. * Removed valid wrapper to pass in to validation methods. * fix tests * Code Review changes. * Removed unused classes * Using GetPlanOrThrow instead. * Switches to extension method * Made Revert and Adjust Sm methods consistent. Corrected string comparer. Added comment for revert sm. * Fixing compiler complaint. * Adding XML docs * Calculated seat addition for SM. * Fixing compiler complaints. * Renames for organization. * Fixing comparison issue. * Adding error and aligning message. * fixing name of method. * Made extension method. * Rearranged some things. Fixed the tests. * Added test around validating the revert. * Added test to validate the provider email is sent if org is managed by a provider. * Created new errors and removed references in business code to ErrorMessages property. This aligns Invite User code to use Errors instead of ErrorMessages * Delayed the hasSecretsManagerStandalone call as long as possible. * Corrected model name. Corrected SM seat calculation. Added test for it. * Corrected logic and added more tests.
This commit is contained in:
@ -2,10 +2,10 @@
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Context;
|
||||
@ -15,7 +15,6 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Models.Mail;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
@ -37,6 +36,7 @@ using NSubstitute.ReturnsExtensions;
|
||||
using Xunit;
|
||||
using Organization = Bit.Core.AdminConsole.Entities.Organization;
|
||||
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
||||
using OrganizationUserInvite = Bit.Core.Models.Business.OrganizationUserInvite;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
@ -77,15 +77,6 @@ public class OrganizationServiceTests
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
await sutProvider.Sut.ImportAsync(org.Id, null, newUsers, null, false, EventSystemUser.PublicApi);
|
||||
|
||||
@ -100,9 +91,11 @@ public class OrganizationServiceTests
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(
|
||||
Arg.Is<OrganizationInvitesInfo>(info => info.OrgUserTokenPairs.Count() == expectedNewUsersCount && info.IsFreeOrg == (org.PlanType == PlanType.Free) && info.OrganizationName == org.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(
|
||||
Arg.Is<SendInvitesRequest>(
|
||||
info => info.Users.Length == expectedNewUsersCount &&
|
||||
info.Organization == org));
|
||||
|
||||
// Send events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
@ -152,16 +145,6 @@ public class OrganizationServiceTests
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
currentContext.ManageUsers(org.Id).Returns(true);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
await sutProvider.Sut.ImportAsync(org.Id, null, newUsers, null, false, EventSystemUser.PublicApi);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
||||
@ -179,14 +162,15 @@ public class OrganizationServiceTests
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == expectedNewUsersCount && info.IsFreeOrg == (org.PlanType == PlanType.Free) && info.OrganizationName == org.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(request =>
|
||||
request.Users.Length == expectedNewUsersCount &&
|
||||
request.Organization == org));
|
||||
|
||||
// Sent events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>(events =>
|
||||
events.Where(e => e.Item2 == EventType.OrganizationUser_Invited).Count() == expectedNewUsersCount));
|
||||
events.Count(e => e.Item2 == EventType.OrganizationUser_Invited) == expectedNewUsersCount));
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
||||
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
||||
referenceEvent.Type == ReferenceEventType.InvitedUsers && referenceEvent.Id == org.Id &&
|
||||
@ -270,125 +254,15 @@ public class OrganizationServiceTests
|
||||
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(request =>
|
||||
request.Users.DistinctBy(x => x.Email).Count() == invite.Emails.Distinct().Count() &&
|
||||
request.Organization == organization));
|
||||
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize, OrganizationCustomize, BitAutoData]
|
||||
public async Task InviteUsers_SsoOrgWithNullSsoConfig_Passes(Organization organization, OrganizationUser invitor,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
// Org must be able to use SSO to trigger this proper test case as we currently only call to retrieve
|
||||
// an org's SSO config if the org can use SSO
|
||||
organization.UseSso = true;
|
||||
|
||||
// Return null for sso config
|
||||
sutProvider.GetDependency<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).ReturnsNull();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new[] { owner });
|
||||
|
||||
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize, OrganizationCustomize, BitAutoData]
|
||||
public async Task InviteUsers_SsoOrgWithNeverEnabledRequireSsoPolicy_Passes(Organization organization, SsoConfig ssoConfig, OrganizationUser invitor,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
// Org must be able to use SSO and policies to trigger this test case
|
||||
organization.UseSso = true;
|
||||
organization.UsePolicies = true;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new[] { owner });
|
||||
|
||||
ssoConfig.Enabled = true;
|
||||
sutProvider.GetDependency<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig);
|
||||
|
||||
|
||||
// Return null policy to mimic new org that's never turned on the require sso policy
|
||||
sutProvider.GetDependency<IPolicyRepository>().GetManyByOrganizationIdAsync(organization.Id).ReturnsNull();
|
||||
|
||||
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.Admin,
|
||||
@ -637,14 +511,14 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
// sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
// .CreateToken(Arg.Any<OrganizationUser>())
|
||||
// .Returns(
|
||||
// info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
// {
|
||||
// ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
// }
|
||||
// );
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||
@ -655,11 +529,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
await sutProvider.Sut.InviteUserAsync(organization.Id, invitor.UserId, systemUser: null, invite, externalId);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == 1 &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(request =>
|
||||
request.Users.Length == 1 &&
|
||||
request.Organization == organization));
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
@ -712,16 +585,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(true);
|
||||
@ -733,12 +596,11 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
.InviteUserAsync(organization.Id, invitor.UserId, systemUser: null, invite, externalId));
|
||||
Assert.Contains("This user has already been invited", exception.Message);
|
||||
|
||||
// MailService and EventService are still called, but with no OrgUsers
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
!info.OrgUserTokenPairs.Any() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
// SendOrganizationInvitesCommand and EventService are still called, but with no OrgUsers
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(info =>
|
||||
info.Organization == organization &&
|
||||
info.Users.Length == 0));
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => !events.Any()));
|
||||
}
|
||||
@ -787,16 +649,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(true);
|
||||
@ -806,11 +658,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, invites);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == invites.SelectMany(i => i.invite.Emails).Count() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(info =>
|
||||
info.Organization == organization &&
|
||||
info.Users.Length == invites.SelectMany(x => x.invite.Emails).Distinct().Count()));
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
@ -848,23 +699,12 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
currentContext.ManageUsers(organization.Id).Returns(true);
|
||||
|
||||
// Mock tokenable factory to return a token that expires in 5 days
|
||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||
.CreateToken(Arg.Any<OrganizationUser>())
|
||||
.Returns(
|
||||
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
}
|
||||
);
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitingUserId: null, eventSystemUser, invites);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
||||
info.OrgUserTokenPairs.Count() == invites.SelectMany(i => i.invite.Emails).Count() &&
|
||||
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
||||
info.OrganizationName == organization.Name));
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>().Received(1)
|
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(info =>
|
||||
info.Users.Length == invites.SelectMany(i => i.invite.Emails).Count() &&
|
||||
info.Organization == organization));
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>());
|
||||
}
|
||||
|
Reference in New Issue
Block a user