1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

[PM-24067] Check for unverified bank account in free trial / inactive subscription warning (#6117)

* [NO LOGIC] Move query to core

* Check for unverified bank account in free trial and inactive subscription warnings

* Run dotnet format

* fix test

* Run dotnet format

* Remove errant file
This commit is contained in:
Alex Morask
2025-07-24 09:59:23 -05:00
committed by GitHub
parent 988b994624
commit 2d1f914eae
10 changed files with 200 additions and 99 deletions

View File

@@ -14,7 +14,7 @@ using NSubstitute;
using Xunit;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Bit.Core.Test.Billing.OrganizationFeatures.OrganizationLicenses;
namespace Bit.Core.Test.Billing.Organizations.Commands;
[SutProviderCustomize]
public class UpdateOrganizationLicenseCommandTests

View File

@@ -18,7 +18,7 @@ using NSubstitute.ReturnsExtensions;
using Stripe;
using Xunit;
namespace Bit.Core.Test.Billing.OrganizationFeatures.OrganizationLicenses;
namespace Bit.Core.Test.Billing.Organizations.Queries;
[SubscriptionInfoCustomize]
[OrganizationLicenseCustomize]

View File

@@ -1,12 +1,13 @@
using Bit.Api.Billing.Queries.Organizations;
using Bit.Core.AdminConsole.Entities;
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.Caches;
using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Organizations.Queries;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Services;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
@@ -15,17 +16,17 @@ using Stripe;
using Stripe.TestHelpers;
using Xunit;
namespace Bit.Api.Test.Billing.Queries.Organizations;
namespace Bit.Core.Test.Billing.Organizations.Queries;
[SutProviderCustomize]
public class OrganizationWarningsQueryTests
public class GetOrganizationWarningsQueryTests
{
private static readonly string[] _requiredExpansions = ["customer", "latest_invoice", "test_clock"];
[Theory, BitAutoData]
public async Task Run_NoSubscription_NoWarnings(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
sutProvider.GetDependency<ISubscriberService>()
.GetSubscription(organization, Arg.Is<SubscriptionGetOptions>(options =>
@@ -46,7 +47,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_FreeTrialWarning(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
var now = DateTime.UtcNow;
@@ -70,6 +71,7 @@ public class OrganizationWarningsQueryTests
});
sutProvider.GetDependency<ICurrentContext>().EditSubscription(organization.Id).Returns(true);
sutProvider.GetDependency<ISetupIntentCache>().Get(organization.Id).Returns((string?)null);
var response = await sutProvider.Sut.Run(organization);
@@ -79,10 +81,90 @@ public class OrganizationWarningsQueryTests
});
}
[Theory, BitAutoData]
public async Task Run_Has_FreeTrialWarning_WithUnverifiedBankAccount_NoWarning(
Organization organization,
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
var now = DateTime.UtcNow;
const string setupIntentId = "setup_intent_id";
sutProvider.GetDependency<ISubscriberService>()
.GetSubscription(organization, Arg.Is<SubscriptionGetOptions>(options =>
options.Expand.SequenceEqual(_requiredExpansions)
))
.Returns(new Subscription
{
Status = StripeConstants.SubscriptionStatus.Trialing,
TrialEnd = now.AddDays(7),
Customer = new Customer
{
InvoiceSettings = new CustomerInvoiceSettings(),
Metadata = new Dictionary<string, string>()
},
TestClock = new TestClock
{
FrozenTime = now
}
});
sutProvider.GetDependency<ICurrentContext>().EditSubscription(organization.Id).Returns(true);
sutProvider.GetDependency<ISetupIntentCache>().Get(organization.Id).Returns(setupIntentId);
sutProvider.GetDependency<IStripeAdapter>().SetupIntentGet(setupIntentId, Arg.Is<SetupIntentGetOptions>(
options => options.Expand.Contains("payment_method"))).Returns(new SetupIntent
{
Status = "requires_action",
NextAction = new SetupIntentNextAction
{
VerifyWithMicrodeposits = new SetupIntentNextActionVerifyWithMicrodeposits()
},
PaymentMethod = new PaymentMethod
{
UsBankAccount = new PaymentMethodUsBankAccount()
}
});
var response = await sutProvider.Sut.Run(organization);
Assert.Null(response.FreeTrial);
}
[Theory, BitAutoData]
public async Task Run_Has_InactiveSubscriptionWarning_AddPaymentMethodOptionalTrial(
Organization organization,
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
organization.Enabled = true;
sutProvider.GetDependency<ISubscriberService>()
.GetSubscription(organization, Arg.Is<SubscriptionGetOptions>(options =>
options.Expand.SequenceEqual(_requiredExpansions)
))
.Returns(new Subscription
{
Status = StripeConstants.SubscriptionStatus.Trialing,
Customer = new Customer
{
InvoiceSettings = new CustomerInvoiceSettings(),
Metadata = new Dictionary<string, string>()
}
});
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
sutProvider.GetDependency<ISetupIntentCache>().Get(organization.Id).Returns((string?)null);
var response = await sutProvider.Sut.Run(organization);
Assert.True(response is
{
InactiveSubscription.Resolution: "add_payment_method_optional_trial"
});
}
[Theory, BitAutoData]
public async Task Run_Has_InactiveSubscriptionWarning_ContactProvider(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
organization.Enabled = false;
@@ -109,7 +191,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_InactiveSubscriptionWarning_AddPaymentMethod(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
organization.Enabled = false;
@@ -135,7 +217,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_InactiveSubscriptionWarning_Resubscribe(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
organization.Enabled = false;
@@ -161,7 +243,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_InactiveSubscriptionWarning_ContactOwner(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
organization.Enabled = false;
@@ -187,7 +269,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_ResellerRenewalWarning_Upcoming(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
var now = DateTime.UtcNow;
@@ -225,7 +307,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_ResellerRenewalWarning_Issued(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
var now = DateTime.UtcNow;
@@ -269,7 +351,7 @@ public class OrganizationWarningsQueryTests
[Theory, BitAutoData]
public async Task Run_Has_ResellerRenewalWarning_PastDue(
Organization organization,
SutProvider<OrganizationWarningsQuery> sutProvider)
SutProvider<GetOrganizationWarningsQuery> sutProvider)
{
var now = DateTime.UtcNow;

View File

@@ -12,7 +12,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.Billing.OrganizationFeatures.OrganizationLicenses;
namespace Bit.Core.Test.Billing.Organizations.Queries;
[SutProviderCustomize]
public class GetSelfHostedOrganizationLicenseQueryTests