mirror of
https://github.com/bitwarden/server
synced 2025-12-21 18:53:41 +00:00
* move billing services+tests to billing namespaces * reorganized methods in file and added comment headers * renamed StripeAdapter methods for better clarity * clean up redundant qualifiers * Upgrade Stripe.net to v48.4.0 * Update PreviewTaxAmountCommand * Remove unused UpcomingInvoiceOptionExtensions * Added SubscriptionExtensions with GetCurrentPeriodEnd * Update PremiumUserBillingService * Update OrganizationBillingService * Update GetOrganizationWarningsQuery * Update BillingHistoryInfo * Update SubscriptionInfo * Remove unused Sql Billing folder * Update StripeAdapter * Update StripePaymentService * Update InvoiceCreatedHandler * Update PaymentFailedHandler * Update PaymentSucceededHandler * Update ProviderEventService * Update StripeEventUtilityService * Update SubscriptionDeletedHandler * Update SubscriptionUpdatedHandler * Update UpcomingInvoiceHandler * Update ProviderSubscriptionResponse * Remove unused Stripe Subscriptions Admin Tool * Update RemoveOrganizationFromProviderCommand * Update ProviderBillingService * Update RemoveOrganizatinoFromProviderCommandTests * Update PreviewTaxAmountCommandTests * Update GetCloudOrganizationLicenseQueryTests * Update GetOrganizationWarningsQueryTests * Update StripePaymentServiceTests * Update ProviderBillingControllerTests * Update ProviderEventServiceTests * Update SubscriptionDeletedHandlerTests * Update SubscriptionUpdatedHandlerTests * Resolve Billing test failures I completely removed tests for the StripeEventService as they were using a system I setup a while back that read JSON files of the Stripe event structure. I did not anticipate how frequently these structures would change with each API version and the cost of trying to update these specific JSON files to test a very static data retrieval service far outweigh the benefit. * Resolve Core test failures * Run dotnet format * Remove unused provider migration * Fixed failing tests * Run dotnet format * Replace the old webhook secret key with new one (#6223) * Fix compilation failures in additions * Run dotnet format * Bump Stripe API version * Fix recent addition: CreatePremiumCloudHostedSubscriptionCommand * Fix new code in main according to Stripe update * Fix InvoiceExtensions * Bump SDK version to match API Version * cleanup * fixing items missed after the merge * use expression body for all simple returns * forgot fixes, format, and pr feedback * claude pr feedback * pr feedback and cleanup * more claude feedback --------- Co-authored-by: Alex Morask <amorask@bitwarden.com> Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
162 lines
7.2 KiB
C#
162 lines
7.2 KiB
C#
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.Pricing;
|
|
using Bit.Core.Billing.Services;
|
|
using Bit.Core.Exceptions;
|
|
using Bit.Core.Models.Business;
|
|
using Bit.Core.Models.StaticStore;
|
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions;
|
|
using Bit.Core.Services;
|
|
using Bit.Core.Test.Billing.Mocks;
|
|
using Bit.Test.Common.AutoFixture;
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
|
using NSubstitute;
|
|
using Xunit;
|
|
|
|
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate;
|
|
[SutProviderCustomize]
|
|
public class AddSecretsManagerSubscriptionCommandTests
|
|
{
|
|
[Theory]
|
|
[BitAutoData(PlanType.TeamsAnnually2019)]
|
|
[BitAutoData(PlanType.TeamsAnnually2020)]
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
[BitAutoData(PlanType.TeamsMonthly2019)]
|
|
[BitAutoData(PlanType.TeamsMonthly2020)]
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
|
[BitAutoData(PlanType.TeamsStarter)]
|
|
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
|
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
[BitAutoData(PlanType.EnterpriseMonthly2019)]
|
|
[BitAutoData(PlanType.EnterpriseMonthly2020)]
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
public async Task SignUpAsync_ReturnsSuccessAndClientSecret_WhenOrganizationAndPlanExist(PlanType planType,
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
int additionalServiceAccounts,
|
|
int additionalSmSeats,
|
|
Organization organization,
|
|
bool useSecretsManager)
|
|
{
|
|
organization.PlanType = planType;
|
|
|
|
var plan = MockPlans.Get(organization.PlanType);
|
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(plan);
|
|
|
|
await sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts);
|
|
|
|
sutProvider.GetDependency<IOrganizationService>().Received(1)
|
|
.ValidateSecretsManagerPlan(plan, Arg.Is<OrganizationUpgrade>(c =>
|
|
c.UseSecretsManager == useSecretsManager &&
|
|
c.AdditionalSmSeats == additionalSmSeats &&
|
|
c.AdditionalServiceAccounts == additionalServiceAccounts &&
|
|
c.AdditionalSeats == organization.Seats.GetValueOrDefault()));
|
|
|
|
await sutProvider.GetDependency<IStripePaymentService>().Received()
|
|
.AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts);
|
|
|
|
// TODO: call ReferenceEventService - see AC-1481
|
|
|
|
await sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(Arg.Is<Organization>(c =>
|
|
c.SmSeats == plan.SecretsManager.BaseSeats + additionalSmSeats &&
|
|
c.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + additionalServiceAccounts &&
|
|
c.UseSecretsManager == true));
|
|
}
|
|
|
|
[Theory]
|
|
[BitAutoData]
|
|
public async Task SignUpAsync_ThrowsNotFoundException_WhenOrganizationIsNull(
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
int additionalServiceAccounts,
|
|
int additionalSmSeats)
|
|
{
|
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
|
sutProvider.Sut.SignUpAsync(null, additionalSmSeats, additionalServiceAccounts));
|
|
await VerifyDependencyNotCalledAsync(sutProvider);
|
|
}
|
|
|
|
[Theory]
|
|
[BitAutoData]
|
|
public async Task SignUpAsync_ThrowsGatewayException_WhenGatewayCustomerIdIsNullOrWhitespace(
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
Organization organization,
|
|
int additionalServiceAccounts,
|
|
int additionalSmSeats)
|
|
{
|
|
organization.GatewayCustomerId = null;
|
|
organization.PlanType = PlanType.EnterpriseAnnually;
|
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
|
|
.Returns(MockPlans.Get(organization.PlanType));
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
|
sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts));
|
|
Assert.Contains("No payment method found.", exception.Message);
|
|
await VerifyDependencyNotCalledAsync(sutProvider);
|
|
}
|
|
|
|
[Theory]
|
|
[BitAutoData]
|
|
public async Task SignUpAsync_ThrowsGatewayException_WhenGatewaySubscriptionIdIsNullOrWhitespace(
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
Organization organization,
|
|
int additionalServiceAccounts,
|
|
int additionalSmSeats)
|
|
{
|
|
organization.GatewaySubscriptionId = null;
|
|
organization.PlanType = PlanType.EnterpriseAnnually;
|
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
|
|
.Returns(MockPlans.Get(organization.PlanType));
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
|
sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts));
|
|
Assert.Contains("No subscription found.", exception.Message);
|
|
await VerifyDependencyNotCalledAsync(sutProvider);
|
|
}
|
|
|
|
[Theory]
|
|
[BitAutoData]
|
|
public async Task SignUpAsync_ThrowsException_WhenOrganizationAlreadyHasSecretsManager(
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
Organization organization)
|
|
{
|
|
organization.UseSecretsManager = true;
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
() => sutProvider.Sut.SignUpAsync(organization, 10, 10));
|
|
|
|
Assert.Contains("Organization already uses Secrets Manager", exception.Message);
|
|
await VerifyDependencyNotCalledAsync(sutProvider);
|
|
}
|
|
|
|
[Theory]
|
|
[BitAutoData]
|
|
public async Task SignUpAsync_ThrowsException_WhenOrganizationIsManagedByMSP(
|
|
SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider,
|
|
Organization organization,
|
|
Provider provider)
|
|
{
|
|
organization.UseSecretsManager = false;
|
|
provider.Type = ProviderType.Msp;
|
|
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).Returns(provider);
|
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
|
|
.Returns(MockPlans.Get(organization.PlanType));
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
() => sutProvider.Sut.SignUpAsync(organization, 10, 10));
|
|
|
|
Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message);
|
|
await VerifyDependencyNotCalledAsync(sutProvider);
|
|
}
|
|
|
|
private static async Task VerifyDependencyNotCalledAsync(SutProvider<AddSecretsManagerSubscriptionCommand> sutProvider)
|
|
{
|
|
await sutProvider.GetDependency<IStripePaymentService>().DidNotReceive()
|
|
.AddSecretsManagerToSubscription(Arg.Any<Organization>(), Arg.Any<Plan>(), Arg.Any<int>(), Arg.Any<int>());
|
|
|
|
// TODO: call ReferenceEventService - see AC-1481
|
|
|
|
await sutProvider.GetDependency<IOrganizationService>().DidNotReceive().ReplaceAndUpdateCacheAsync(Arg.Any<Organization>());
|
|
}
|
|
}
|