You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
666 lines
30 KiB
666 lines
30 KiB
using Bit.Core.Entities; |
|
using Bit.Core.Enums; |
|
using Bit.Core.Exceptions; |
|
using Bit.Core.Models.Business; |
|
using Bit.Core.Models.StaticStore; |
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; |
|
using Bit.Core.Repositories; |
|
using Bit.Core.SecretsManager.Repositories; |
|
using Bit.Core.Services; |
|
using Bit.Core.Settings; |
|
using Bit.Core.Test.AutoFixture.OrganizationFixtures; |
|
using Bit.Core.Utilities; |
|
using Bit.Test.Common.AutoFixture; |
|
using Bit.Test.Common.AutoFixture.Attributes; |
|
using NSubstitute; |
|
using Xunit; |
|
|
|
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate; |
|
|
|
[SutProviderCustomize] |
|
[SecretsManagerOrganizationCustomize] |
|
public class UpdateSecretsManagerSubscriptionCommandTests |
|
{ |
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually2019)] |
|
[BitAutoData(PlanType.EnterpriseAnnually2020)] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2019)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2020)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly2019)] |
|
[BitAutoData(PlanType.TeamsMonthly2020)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually2019)] |
|
[BitAutoData(PlanType.TeamsAnnually2020)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task UpdateSubscriptionAsync_UpdateEverything_ValidInput_Passes( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.Seats = 400; |
|
organization.SmSeats = 10; |
|
organization.MaxAutoscaleSmSeats = 20; |
|
organization.SmServiceAccounts = 200; |
|
organization.MaxAutoscaleSmServiceAccounts = 350; |
|
|
|
var updateSmSeats = 15; |
|
var updateSmServiceAccounts = 300; |
|
var updateMaxAutoscaleSmSeats = 16; |
|
var updateMaxAutoscaleSmServiceAccounts = 301; |
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = updateSmSeats, |
|
SmServiceAccounts = updateSmServiceAccounts, |
|
MaxAutoscaleSmSeats = updateMaxAutoscaleSmSeats, |
|
MaxAutoscaleSmServiceAccounts = updateMaxAutoscaleSmServiceAccounts |
|
}; |
|
|
|
await sutProvider.Sut.UpdateSubscriptionAsync(update); |
|
|
|
var plan = StaticStore.GetPlan(organization.PlanType); |
|
await sutProvider.GetDependency<IPaymentService>().Received(1) |
|
.AdjustSmSeatsAsync(organization, plan, update.SmSeatsExcludingBase); |
|
await sutProvider.GetDependency<IPaymentService>().Received(1) |
|
.AdjustServiceAccountsAsync(organization, plan, update.SmServiceAccountsExcludingBase); |
|
|
|
// TODO: call ReferenceEventService - see AC-1481 |
|
|
|
AssertUpdatedOrganization(() => Arg.Is<Organization>(org => |
|
org.Id == organization.Id && |
|
org.SmSeats == updateSmSeats && |
|
org.MaxAutoscaleSmSeats == updateMaxAutoscaleSmSeats && |
|
org.SmServiceAccounts == updateSmServiceAccounts && |
|
org.MaxAutoscaleSmServiceAccounts == updateMaxAutoscaleSmServiceAccounts), |
|
sutProvider); |
|
|
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxSeatLimitReachedEmailAsync(default, default, default); |
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(default, default, default); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task UpdateSubscriptionAsync_ValidInput_WithNullMaxAutoscale_Passes( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
|
|
const int updateSmSeats = 15; |
|
const int updateSmServiceAccounts = 450; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = updateSmSeats, |
|
MaxAutoscaleSmSeats = null, |
|
SmServiceAccounts = updateSmServiceAccounts, |
|
MaxAutoscaleSmServiceAccounts = null |
|
}; |
|
|
|
await sutProvider.Sut.UpdateSubscriptionAsync(update); |
|
|
|
var plan = StaticStore.GetPlan(organization.PlanType); |
|
await sutProvider.GetDependency<IPaymentService>().Received(1) |
|
.AdjustSmSeatsAsync(organization, plan, update.SmSeatsExcludingBase); |
|
await sutProvider.GetDependency<IPaymentService>().Received(1) |
|
.AdjustServiceAccountsAsync(organization, plan, update.SmServiceAccountsExcludingBase); |
|
|
|
// TODO: call ReferenceEventService - see AC-1481 |
|
|
|
AssertUpdatedOrganization(() => Arg.Is<Organization>(org => |
|
org.Id == organization.Id && |
|
org.SmSeats == updateSmSeats && |
|
org.MaxAutoscaleSmSeats == null && |
|
org.SmServiceAccounts == updateSmServiceAccounts && |
|
org.MaxAutoscaleSmServiceAccounts == null), |
|
sutProvider); |
|
|
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxSeatLimitReachedEmailAsync(default, default, default); |
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(default, default, default); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(false, "Cannot update subscription on a self-hosted instance.")] |
|
[BitAutoData(true, "Cannot autoscale on a self-hosted instance.")] |
|
public async Task UpdatingSubscription_WhenSelfHosted_ThrowsBadRequestException( |
|
bool autoscaling, |
|
string expectedError, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, autoscaling).AdjustSeats(2); |
|
|
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains(expectedError, exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_NoSecretsManagerAccess_ThrowsException( |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider, |
|
Organization organization) |
|
{ |
|
organization.UseSecretsManager = false; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
|
|
Assert.Contains("Organization has no access to Secrets Manager.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_OrganizationEnrolledInSmBeta_ThrowsException( |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider, |
|
Organization organization) |
|
{ |
|
organization.UseSecretsManager = true; |
|
organization.SecretsManagerBeta = true; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
|
|
Assert.Contains("Organization is enrolled in Secrets Manager Beta", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually2019)] |
|
[BitAutoData(PlanType.EnterpriseAnnually2020)] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2019)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2020)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly2019)] |
|
[BitAutoData(PlanType.TeamsMonthly2020)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually2019)] |
|
[BitAutoData(PlanType.TeamsAnnually2020)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task UpdateSubscriptionAsync_PaidPlan_NullGatewayCustomerId_ThrowsException( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.GatewayCustomerId = null; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustSeats(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("No payment method found.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually2019)] |
|
[BitAutoData(PlanType.EnterpriseAnnually2020)] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2019)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2020)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly2019)] |
|
[BitAutoData(PlanType.TeamsMonthly2020)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually2019)] |
|
[BitAutoData(PlanType.TeamsAnnually2020)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task UpdateSubscriptionAsync_PaidPlan_NullGatewaySubscriptionId_ThrowsException( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.GatewaySubscriptionId = null; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustSeats(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("No subscription found.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually2019)] |
|
[BitAutoData(PlanType.EnterpriseAnnually2020)] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2019)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2020)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly2019)] |
|
[BitAutoData(PlanType.TeamsMonthly2020)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually2019)] |
|
[BitAutoData(PlanType.TeamsAnnually2020)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success(PlanType planType, Guid organizationId, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var plan = StaticStore.GetPlan(planType); |
|
|
|
var organizationSeats = plan.SecretsManager.BaseSeats + 10; |
|
var organizationMaxAutoscaleSeats = 20; |
|
var organizationServiceAccounts = plan.SecretsManager.BaseServiceAccount + 10; |
|
var organizationMaxAutoscaleServiceAccounts = 300; |
|
|
|
var organization = new Organization |
|
{ |
|
Id = organizationId, |
|
PlanType = planType, |
|
GatewayCustomerId = "1", |
|
GatewaySubscriptionId = "2", |
|
UseSecretsManager = true, |
|
SmSeats = organizationSeats, |
|
MaxAutoscaleSmSeats = organizationMaxAutoscaleSeats, |
|
SmServiceAccounts = organizationServiceAccounts, |
|
MaxAutoscaleSmServiceAccounts = organizationMaxAutoscaleServiceAccounts |
|
}; |
|
|
|
var smServiceAccountsAdjustment = 10; |
|
var expectedSmServiceAccounts = organizationServiceAccounts + smServiceAccountsAdjustment; |
|
var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.SecretsManager.BaseServiceAccount; |
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(10); |
|
|
|
await sutProvider.Sut.UpdateSubscriptionAsync(update); |
|
|
|
await sutProvider.GetDependency<IPaymentService>().Received(1).AdjustServiceAccountsAsync( |
|
Arg.Is<Organization>(o => o.Id == organizationId), |
|
plan, |
|
expectedSmServiceAccountsExcludingBase); |
|
// TODO: call ReferenceEventService - see AC-1481 |
|
AssertUpdatedOrganization(() => Arg.Is<Organization>(o => |
|
o.Id == organizationId |
|
&& o.SmSeats == organizationSeats |
|
&& o.MaxAutoscaleSmSeats == organizationMaxAutoscaleSeats |
|
&& o.SmServiceAccounts == expectedSmServiceAccounts |
|
&& o.MaxAutoscaleSmServiceAccounts == organizationMaxAutoscaleServiceAccounts), sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_UpdateSeatsToAutoscaleLimit_EmailsOwners( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = 10, |
|
MaxAutoscaleSmSeats = 10 |
|
}; |
|
|
|
await sutProvider.Sut.UpdateSubscriptionAsync(update); |
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1).SendSecretsManagerMaxSeatLimitReachedEmailAsync( |
|
organization, organization.MaxAutoscaleSmSeats.Value, Arg.Any<IEnumerable<string>>()); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_OrgWithNullSmSeatOnSeatsAdjustment_ThrowsException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.SmSeats = null; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustSeats(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
|
|
Assert.Contains("Organization has no Secrets Manager seat limit, no need to adjust seats", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_SmSeatAutoscaling_Subtracting_ThrowsBadRequestException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, true).AdjustSeats(-2); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Cannot use autoscaling to subtract seats.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.Free)] |
|
public async Task UpdateSubscriptionAsync_WithHasAdditionalSeatsOptionFalse_ThrowsBadRequestException( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustSeats(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("You have reached the maximum number of Secrets Manager seats (2) for this plan", |
|
exception.Message, StringComparison.InvariantCultureIgnoreCase); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task SmSeatAutoscaling_MaxLimitReached_ThrowsBadRequestException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.SmSeats = 9; |
|
organization.MaxAutoscaleSmSeats = 10; |
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, true).AdjustSeats(2); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Secrets Manager seat limit has been reached.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_SeatsAdjustmentGreaterThanMaxAutoscaleSeats_ThrowsException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = 15, |
|
MaxAutoscaleSmSeats = 10 |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenSmSeatsLessThanOne( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = 0, |
|
}; |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id).Returns(8); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("You must have at least 1 Secrets Manager seat.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenOccupiedSeatsExceedNewSeatTotal( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.SmSeats = 8; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = 7, |
|
}; |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id).Returns(8); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("8 users are currently occupying Secrets Manager seats. You cannot decrease your subscription below your current occupied seat count", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_UpdateServiceAccountsToAutoscaleLimit_EmailsOwners( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmServiceAccounts = 300, |
|
MaxAutoscaleSmServiceAccounts = 300 |
|
}; |
|
|
|
await sutProvider.Sut.UpdateSubscriptionAsync(update); |
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1).SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync( |
|
organization, organization.MaxAutoscaleSmServiceAccounts.Value, Arg.Any<IEnumerable<string>>()); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task AdjustServiceAccountsAsync_ThrowsBadRequestException_WhenSmServiceAccountsIsNull( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.SmServiceAccounts = null; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Organization has no service accounts limit, no need to adjust service accounts", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ServiceAccountAutoscaling_Subtracting_ThrowsBadRequestException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, true).AdjustServiceAccounts(-2); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Cannot use autoscaling to subtract service accounts.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.Free)] |
|
public async Task UpdateSubscriptionAsync_WithHasAdditionalServiceAccountOptionFalse_ThrowsBadRequestException( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(1); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("You have reached the maximum number of service accounts (3) for this plan", |
|
exception.Message, StringComparison.InvariantCultureIgnoreCase); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task ServiceAccountAutoscaling_MaxLimitReached_ThrowsBadRequestException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.SmServiceAccounts = 9; |
|
organization.MaxAutoscaleSmServiceAccounts = 10; |
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, true).AdjustServiceAccounts(2); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Secrets Manager service account limit has been reached.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ServiceAccountsGreaterThanMaxAutoscaleSeats_ThrowsException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmServiceAccounts = 15, |
|
MaxAutoscaleSmServiceAccounts = 10 |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Cannot set max service accounts autoscaling below service account amount", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ServiceAccountsLessThanPlanMinimum_ThrowsException( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmServiceAccounts = 199, |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>( |
|
() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Plan has a minimum of 200 service accounts", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.EnterpriseAnnually2019)] |
|
[BitAutoData(PlanType.EnterpriseAnnually2020)] |
|
[BitAutoData(PlanType.EnterpriseAnnually)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2019)] |
|
[BitAutoData(PlanType.EnterpriseMonthly2020)] |
|
[BitAutoData(PlanType.EnterpriseMonthly)] |
|
[BitAutoData(PlanType.TeamsMonthly2019)] |
|
[BitAutoData(PlanType.TeamsMonthly2020)] |
|
[BitAutoData(PlanType.TeamsMonthly)] |
|
[BitAutoData(PlanType.TeamsAnnually2019)] |
|
[BitAutoData(PlanType.TeamsAnnually2020)] |
|
[BitAutoData(PlanType.TeamsAnnually)] |
|
[BitAutoData(PlanType.TeamsStarter)] |
|
public async Task UpdateSmServiceAccounts_WhenCurrentServiceAccountsIsGreaterThanNew_ThrowsBadRequestException( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var currentServiceAccounts = 301; |
|
organization.PlanType = planType; |
|
organization.SmServiceAccounts = currentServiceAccounts; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) { SmServiceAccounts = 201 }; |
|
|
|
sutProvider.GetDependency<IServiceAccountRepository>() |
|
.GetServiceAccountCountByOrganizationIdAsync(organization.Id) |
|
.Returns(currentServiceAccounts); |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Your organization currently has 301 service accounts. You cannot decrease your subscription below your current service account usage", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenMaxAutoscaleSeatsBelowSeatCount( |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
SmSeats = 10, |
|
MaxAutoscaleSmSeats = 5 |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.Free)] |
|
public async Task UpdateMaxAutoscaleSmSeats_ThrowsBadRequestException_WhenExceedsPlanMaxUsers( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.SmSeats = 2; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
MaxAutoscaleSmSeats = 3 |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Your plan has a Secrets Manager seat limit of 2, but you have specified a max autoscale count of 3.Reduce your max autoscale count.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.Free)] |
|
public async Task UpdateMaxAutoscaleSmSeats_ThrowsBadRequestException_WhenPlanDoesNotAllowAutoscale( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.SmSeats = 2; |
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) |
|
{ |
|
MaxAutoscaleSmSeats = 2 |
|
}; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Your plan does not allow Secrets Manager seat autoscaling", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData(PlanType.Free)] |
|
public async Task UpdateMaxAutoscaleSmServiceAccounts_ThrowsBadRequestException_WhenPlanDoesNotAllowAutoscale( |
|
PlanType planType, |
|
Organization organization, |
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
organization.PlanType = planType; |
|
organization.SmServiceAccounts = 3; |
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false) { MaxAutoscaleSmServiceAccounts = 3 }; |
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); |
|
Assert.Contains("Your plan does not allow service accounts autoscaling.", exception.Message); |
|
await VerifyDependencyNotCalledAsync(sutProvider); |
|
} |
|
|
|
private static async Task VerifyDependencyNotCalledAsync(SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
await sutProvider.GetDependency<IPaymentService>().DidNotReceive() |
|
.AdjustSmSeatsAsync(Arg.Any<Organization>(), Arg.Any<Plan>(), Arg.Any<int>()); |
|
await sutProvider.GetDependency<IPaymentService>().DidNotReceive() |
|
.AdjustServiceAccountsAsync(Arg.Any<Organization>(), Arg.Any<Plan>(), Arg.Any<int>()); |
|
// TODO: call ReferenceEventService - see AC-1481 |
|
await sutProvider.GetDependency<IMailService>().DidNotReceive() |
|
.SendOrganizationMaxSeatLimitReachedEmailAsync(Arg.Any<Organization>(), Arg.Any<int>(), |
|
Arg.Any<IEnumerable<string>>()); |
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default); |
|
sutProvider.GetDependency<IApplicationCacheService>().DidNotReceiveWithAnyArgs().UpsertOrganizationAbilityAsync(default); |
|
} |
|
|
|
private void AssertUpdatedOrganization(Func<Organization> organizationMatcher, SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider) |
|
{ |
|
sutProvider.GetDependency<IOrganizationRepository>().Received(1).ReplaceAsync(organizationMatcher()); |
|
sutProvider.GetDependency<IApplicationCacheService>().Received(1).UpsertOrganizationAbilityAsync(organizationMatcher()); |
|
} |
|
}
|
|
|