mirror of
https://github.com/bitwarden/server.git
synced 2024-11-28 13:15:12 +01:00
7cbe888b82
* SM-1012: Phase 2, removing SM Beta from the server (but not db) * SM-1012: Add migration for RemoveSMBetaFromOrganization * SM-1012: Dotnet format * SM-1012: Undo RemoveSMBetaFromOrganization EF migration * SM-1012: Redo RemoveSMBetaFromOrganization EF migration * SM-1012: Ran dotnet format
676 lines
30 KiB
C#
676 lines
30 KiB
C#
using Bit.Core.AdminConsole.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;
|
|
organization.Seats = 20;
|
|
|
|
const int updateSmSeats = 15;
|
|
const int updateSmServiceAccounts = 450;
|
|
|
|
// Ensure that SmSeats is different from the original organization.SmSeats
|
|
organization.SmSeats = updateSmSeats + 5;
|
|
|
|
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(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)
|
|
{
|
|
const int seatCount = 10;
|
|
|
|
// Make sure Password Manager seats is greater or equal to Secrets Manager seats
|
|
organization.Seats = seatCount;
|
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false)
|
|
{
|
|
SmSeats = seatCount,
|
|
MaxAutoscaleSmSeats = seatCount
|
|
};
|
|
|
|
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 = organization.SmSeats + 10,
|
|
MaxAutoscaleSmSeats = organization.SmSeats + 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]
|
|
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)
|
|
{
|
|
const int smServiceAccount = 15;
|
|
const int maxAutoscaleSmServiceAccounts = 10;
|
|
|
|
organization.SmServiceAccounts = smServiceAccount - 5;
|
|
organization.MaxAutoscaleSmServiceAccounts = 2 * smServiceAccount;
|
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false)
|
|
{
|
|
SmServiceAccounts = smServiceAccount,
|
|
MaxAutoscaleSmServiceAccounts = maxAutoscaleSmServiceAccounts
|
|
};
|
|
|
|
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)
|
|
{
|
|
const int newSmServiceAccounts = 199;
|
|
|
|
organization.SmServiceAccounts = newSmServiceAccounts - 10;
|
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false)
|
|
{
|
|
SmServiceAccounts = newSmServiceAccounts,
|
|
};
|
|
|
|
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)
|
|
{
|
|
const int smSeats = 10;
|
|
const int maxAutoscaleSmSeats = 5;
|
|
|
|
organization.SmSeats = smSeats - 1;
|
|
organization.MaxAutoscaleSmSeats = smSeats * 2;
|
|
|
|
var update = new SecretsManagerSubscriptionUpdate(organization, false)
|
|
{
|
|
SmSeats = smSeats,
|
|
MaxAutoscaleSmSeats = maxAutoscaleSmSeats
|
|
};
|
|
|
|
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());
|
|
}
|
|
}
|