using Bit.Admin.AdminConsole.Controllers; using Bit.Admin.AdminConsole.Models; using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Enums; using Bit.Core.Billing.Services; using Bit.Core.Enums; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; namespace Admin.Test.AdminConsole.Controllers; [ControllerCustomize(typeof(OrganizationsController))] [SutProviderCustomize] public class OrganizationsControllerTests { #region Edit (POST) [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_RequiredFFDisabled_NoOp( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false }; var organization = new Organization { Id = organizationId }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .ScaleSeats(Arg.Any(), Arg.Any(), Arg.Any()); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_NonBillableProvider_NoOp( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false }; var organization = new Organization { Id = organizationId }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Created }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .ScaleSeats(Arg.Any(), Arg.Any(), Arg.Any()); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_UnmanagedOrganization_NoOp( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Created }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .ScaleSeats(Arg.Any(), Arg.Any(), Arg.Any()); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_NonCBPlanType_NoOp( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false, Seats = 10, PlanType = PlanType.FamiliesAnnually }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Managed, Seats = 10 }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .ScaleSeats(Arg.Any(), Arg.Any(), Arg.Any()); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_NoUpdateRequired_NoOp( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false, Seats = 10, PlanType = PlanType.EnterpriseMonthly }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Managed, Seats = 10, PlanType = PlanType.EnterpriseMonthly }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .ScaleSeats(Arg.Any(), Arg.Any(), Arg.Any()); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_PlanTypesUpdate_ScalesSeatsCorrectly( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false, Seats = 10, PlanType = PlanType.EnterpriseMonthly }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Managed, Seats = 10, PlanType = PlanType.TeamsMonthly }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert var providerBillingService = sutProvider.GetDependency(); await providerBillingService.Received(1).ScaleSeats(provider, organization.PlanType, -organization.Seats.Value); await providerBillingService.Received(1).ScaleSeats(provider, update.PlanType!.Value, organization.Seats.Value); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_SeatsUpdate_ScalesSeatsCorrectly( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false, Seats = 15, PlanType = PlanType.EnterpriseMonthly }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Managed, Seats = 10, PlanType = PlanType.EnterpriseMonthly }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert var providerBillingService = sutProvider.GetDependency(); await providerBillingService.Received(1).ScaleSeats(provider, organization.PlanType, update.Seats!.Value - organization.Seats.Value); } [BitAutoData] [SutProviderCustomize] [Theory] public async Task Edit_ProviderSeatScaling_FullUpdate_ScalesSeatsCorrectly( SutProvider sutProvider) { // Arrange var organizationId = new Guid(); var update = new OrganizationEditModel { UseSecretsManager = false, Seats = 15, PlanType = PlanType.EnterpriseMonthly }; var organization = new Organization { Id = organizationId, Status = OrganizationStatusType.Managed, Seats = 10, PlanType = PlanType.TeamsMonthly }; sutProvider.GetDependency().GetByIdAsync(organizationId) .Returns(organization); var featureService = sutProvider.GetDependency(); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; sutProvider.GetDependency().GetByOrganizationIdAsync(organizationId).Returns(provider); // Act _ = await sutProvider.Sut.Edit(organizationId, update); // Assert var providerBillingService = sutProvider.GetDependency(); await providerBillingService.Received(1).ScaleSeats(provider, organization.PlanType, -organization.Seats.Value); await providerBillingService.Received(1).ScaleSeats(provider, update.PlanType!.Value, update.Seats!.Value - organization.Seats.Value + organization.Seats.Value); } #endregion }