1
0
mirror of https://github.com/bitwarden/server synced 2026-01-07 11:03:37 +00:00

Merge branch 'main' into SM-1571-DisableSMAdsForUsers

This commit is contained in:
cd-bitwarden
2025-10-22 17:13:43 -04:00
committed by GitHub
50 changed files with 1311 additions and 289 deletions

View File

@@ -1,7 +1,9 @@
using Bit.Core.Billing.Caches;
using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Payment.Models;
using Bit.Core.Billing.Premium.Commands;
using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services;
using Bit.Core.Entities;
using Bit.Core.Platform.Push;
@@ -14,6 +16,8 @@ using NSubstitute;
using Stripe;
using Xunit;
using Address = Stripe.Address;
using PremiumPlan = Bit.Core.Billing.Pricing.Premium.Plan;
using PremiumPurchasable = Bit.Core.Billing.Pricing.Premium.Purchasable;
using StripeCustomer = Stripe.Customer;
using StripeSubscription = Stripe.Subscription;
@@ -28,6 +32,7 @@ public class CreatePremiumCloudHostedSubscriptionCommandTests
private readonly ISubscriberService _subscriberService = Substitute.For<ISubscriberService>();
private readonly IUserService _userService = Substitute.For<IUserService>();
private readonly IPushNotificationService _pushNotificationService = Substitute.For<IPushNotificationService>();
private readonly IPricingClient _pricingClient = Substitute.For<IPricingClient>();
private readonly CreatePremiumCloudHostedSubscriptionCommand _command;
public CreatePremiumCloudHostedSubscriptionCommandTests()
@@ -36,6 +41,17 @@ public class CreatePremiumCloudHostedSubscriptionCommandTests
baseServiceUri.CloudRegion.Returns("US");
_globalSettings.BaseServiceUri.Returns(baseServiceUri);
// Setup default premium plan with standard pricing
var premiumPlan = new PremiumPlan
{
Name = "Premium",
Available = true,
LegacyYear = null,
Seat = new PremiumPurchasable { Price = 10M, StripePriceId = StripeConstants.Prices.PremiumAnnually },
Storage = new PremiumPurchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal }
};
_pricingClient.GetAvailablePremiumPlan().Returns(premiumPlan);
_command = new CreatePremiumCloudHostedSubscriptionCommand(
_braintreeGateway,
_globalSettings,
@@ -44,7 +60,8 @@ public class CreatePremiumCloudHostedSubscriptionCommandTests
_subscriberService,
_userService,
_pushNotificationService,
Substitute.For<ILogger<CreatePremiumCloudHostedSubscriptionCommand>>());
Substitute.For<ILogger<CreatePremiumCloudHostedSubscriptionCommand>>(),
_pricingClient);
}
[Theory, BitAutoData]

View File

@@ -1,23 +1,38 @@
using Bit.Core.Billing.Payment.Models;
using Bit.Core.Billing.Premium.Commands;
using Bit.Core.Billing.Pricing;
using Bit.Core.Services;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Stripe;
using Xunit;
using static Bit.Core.Billing.Constants.StripeConstants;
using PremiumPlan = Bit.Core.Billing.Pricing.Premium.Plan;
using PremiumPurchasable = Bit.Core.Billing.Pricing.Premium.Purchasable;
namespace Bit.Core.Test.Billing.Premium.Commands;
public class PreviewPremiumTaxCommandTests
{
private readonly ILogger<PreviewPremiumTaxCommand> _logger = Substitute.For<ILogger<PreviewPremiumTaxCommand>>();
private readonly IPricingClient _pricingClient = Substitute.For<IPricingClient>();
private readonly IStripeAdapter _stripeAdapter = Substitute.For<IStripeAdapter>();
private readonly PreviewPremiumTaxCommand _command;
public PreviewPremiumTaxCommandTests()
{
_command = new PreviewPremiumTaxCommand(_logger, _stripeAdapter);
// Setup default premium plan with standard pricing
var premiumPlan = new PremiumPlan
{
Name = "Premium",
Available = true,
LegacyYear = null,
Seat = new PremiumPurchasable { Price = 10M, StripePriceId = Prices.PremiumAnnually },
Storage = new PremiumPurchasable { Price = 4M, StripePriceId = Prices.StoragePlanPersonal }
};
_pricingClient.GetAvailablePremiumPlan().Returns(premiumPlan);
_command = new PreviewPremiumTaxCommand(_logger, _pricingClient, _stripeAdapter);
}
[Fact]

View File

@@ -278,21 +278,27 @@ public class UpdateSecretsManagerSubscriptionCommandTests
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider)
{
// Arrange
const int seatCount = 10;
var existingSeatCount = 9;
// Make sure Password Manager seats is greater or equal to Secrets Manager seats
organization.Seats = seatCount;
const int initialSeatCount = 9;
const int maxSeatCount = 20;
// This represents the total number of users allowed in the organization.
organization.Seats = maxSeatCount;
// This represents the number of Secrets Manager users allowed in the organization.
organization.SmSeats = initialSeatCount;
// This represents the upper limit of Secrets Manager seats that can be automatically scaled.
organization.MaxAutoscaleSmSeats = maxSeatCount;
organization.PlanType = PlanType.EnterpriseAnnually;
var plan = StaticStore.GetPlan(organization.PlanType);
var update = new SecretsManagerSubscriptionUpdate(organization, plan, false)
{
SmSeats = seatCount,
MaxAutoscaleSmSeats = seatCount
SmSeats = 8,
MaxAutoscaleSmSeats = maxSeatCount
};
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id)
.Returns(existingSeatCount);
.Returns(5);
// Act
await sutProvider.Sut.UpdateSubscriptionAsync(update);
@@ -316,21 +322,29 @@ public class UpdateSecretsManagerSubscriptionCommandTests
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider)
{
// Arrange
const int seatCount = 10;
const int existingSeatCount = 10;
var ownerDetailsList = new List<OrganizationUserUserDetails> { new() { Email = "owner@example.com" } };
const int initialSeatCount = 5;
const int maxSeatCount = 10;
// The amount of seats for users in an organization
// This represents the total number of users allowed in the organization.
organization.Seats = maxSeatCount;
// This represents the number of Secrets Manager users allowed in the organization.
organization.SmSeats = initialSeatCount;
// This represents the upper limit of Secrets Manager seats that can be automatically scaled.
organization.MaxAutoscaleSmSeats = maxSeatCount;
var ownerDetailsList = new List<OrganizationUserUserDetails> { new() { Email = "owner@example.com" } };
organization.PlanType = PlanType.EnterpriseAnnually;
var plan = StaticStore.GetPlan(organization.PlanType);
var update = new SecretsManagerSubscriptionUpdate(organization, plan, false)
{
SmSeats = seatCount,
MaxAutoscaleSmSeats = seatCount
SmSeats = maxSeatCount,
MaxAutoscaleSmSeats = maxSeatCount
};
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id)
.Returns(existingSeatCount);
.Returns(maxSeatCount);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByMinimumRoleAsync(organization.Id, OrganizationUserType.Owner)
.Returns(ownerDetailsList);
@@ -340,15 +354,14 @@ public class UpdateSecretsManagerSubscriptionCommandTests
// Assert
// Currently being called once each for different validation methods
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(2)
.Received(1)
.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendSecretsManagerMaxSeatLimitReachedEmailAsync(Arg.Is(organization),
Arg.Is(seatCount),
Arg.Is(maxSeatCount),
Arg.Is<IEnumerable<string>>(emails => emails.Contains(ownerDetailsList[0].Email)));
}