mirror of
https://github.com/bitwarden/server
synced 2026-01-09 03:53:42 +00:00
[PM-26194] Fix: Provider Portal not automatically disabled, when subscription is cancelled (#6480)
* Add the fix for the bug * Move the org disable to job
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
using Bit.Billing.Constants;
|
||||
using Bit.Billing.Jobs;
|
||||
using Bit.Billing.Services;
|
||||
using Bit.Billing.Services.Implementations;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using Bit.Core.Services;
|
||||
using NSubstitute;
|
||||
using Quartz;
|
||||
using Stripe;
|
||||
using Xunit;
|
||||
|
||||
@@ -16,6 +21,10 @@ public class SubscriptionDeletedHandlerTests
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStripeEventUtilityService _stripeEventUtilityService;
|
||||
private readonly IOrganizationDisableCommand _organizationDisableCommand;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly ISchedulerFactory _schedulerFactory;
|
||||
private readonly IScheduler _scheduler;
|
||||
private readonly SubscriptionDeletedHandler _sut;
|
||||
|
||||
public SubscriptionDeletedHandlerTests()
|
||||
@@ -24,11 +33,19 @@ public class SubscriptionDeletedHandlerTests
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_stripeEventUtilityService = Substitute.For<IStripeEventUtilityService>();
|
||||
_organizationDisableCommand = Substitute.For<IOrganizationDisableCommand>();
|
||||
_providerRepository = Substitute.For<IProviderRepository>();
|
||||
_providerService = Substitute.For<IProviderService>();
|
||||
_schedulerFactory = Substitute.For<ISchedulerFactory>();
|
||||
_scheduler = Substitute.For<IScheduler>();
|
||||
_schedulerFactory.GetScheduler().Returns(_scheduler);
|
||||
_sut = new SubscriptionDeletedHandler(
|
||||
_stripeEventService,
|
||||
_userService,
|
||||
_stripeEventUtilityService,
|
||||
_organizationDisableCommand);
|
||||
_organizationDisableCommand,
|
||||
_providerRepository,
|
||||
_providerService,
|
||||
_schedulerFactory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -59,6 +76,7 @@ public class SubscriptionDeletedHandlerTests
|
||||
// Assert
|
||||
await _organizationDisableCommand.DidNotReceiveWithAnyArgs().DisableAsync(default, default);
|
||||
await _userService.DidNotReceiveWithAnyArgs().DisablePremiumAsync(default, default);
|
||||
await _providerService.DidNotReceiveWithAnyArgs().UpdateAsync(default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -192,4 +210,120 @@ public class SubscriptionDeletedHandlerTests
|
||||
await _organizationDisableCommand.DidNotReceiveWithAnyArgs()
|
||||
.DisableAsync(default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_ProviderSubscriptionCanceled_DisablesProviderAndQueuesJob()
|
||||
{
|
||||
// Arrange
|
||||
var stripeEvent = new Event();
|
||||
var providerId = Guid.NewGuid();
|
||||
var provider = new Provider
|
||||
{
|
||||
Id = providerId,
|
||||
Enabled = true
|
||||
};
|
||||
var subscription = new Subscription
|
||||
{
|
||||
Status = StripeSubscriptionStatus.Canceled,
|
||||
Items = new StripeList<SubscriptionItem>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new SubscriptionItem { CurrentPeriodEnd = DateTime.UtcNow.AddDays(30) }
|
||||
]
|
||||
},
|
||||
Metadata = new Dictionary<string, string> { { "providerId", providerId.ToString() } }
|
||||
};
|
||||
|
||||
_stripeEventService.GetSubscription(stripeEvent, true).Returns(subscription);
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(subscription.Metadata)
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
_providerRepository.GetByIdAsync(providerId).Returns(provider);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(stripeEvent);
|
||||
|
||||
// Assert
|
||||
Assert.False(provider.Enabled);
|
||||
await _providerService.Received(1).UpdateAsync(provider);
|
||||
await _scheduler.Received(1).ScheduleJob(
|
||||
Arg.Is<IJobDetail>(j => j.JobType == typeof(ProviderOrganizationDisableJob)),
|
||||
Arg.Any<ITrigger>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_ProviderSubscriptionCanceled_ProviderNotFound_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
var stripeEvent = new Event();
|
||||
var providerId = Guid.NewGuid();
|
||||
var subscription = new Subscription
|
||||
{
|
||||
Status = StripeSubscriptionStatus.Canceled,
|
||||
Items = new StripeList<SubscriptionItem>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new SubscriptionItem { CurrentPeriodEnd = DateTime.UtcNow.AddDays(30) }
|
||||
]
|
||||
},
|
||||
Metadata = new Dictionary<string, string> { { "providerId", providerId.ToString() } }
|
||||
};
|
||||
|
||||
_stripeEventService.GetSubscription(stripeEvent, true).Returns(subscription);
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(subscription.Metadata)
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
_providerRepository.GetByIdAsync(providerId).Returns((Provider)null);
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
await _sut.HandleAsync(stripeEvent);
|
||||
|
||||
// Assert
|
||||
await _providerService.DidNotReceiveWithAnyArgs().UpdateAsync(default);
|
||||
await _scheduler.DidNotReceiveWithAnyArgs().ScheduleJob(default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_ProviderSubscriptionCanceled_QueuesJobWithCorrectParameters()
|
||||
{
|
||||
// Arrange
|
||||
var stripeEvent = new Event();
|
||||
var providerId = Guid.NewGuid();
|
||||
var expirationDate = DateTime.UtcNow.AddDays(30);
|
||||
var provider = new Provider
|
||||
{
|
||||
Id = providerId,
|
||||
Enabled = true
|
||||
};
|
||||
var subscription = new Subscription
|
||||
{
|
||||
Status = StripeSubscriptionStatus.Canceled,
|
||||
Items = new StripeList<SubscriptionItem>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new SubscriptionItem { CurrentPeriodEnd = expirationDate }
|
||||
]
|
||||
},
|
||||
Metadata = new Dictionary<string, string> { { "providerId", providerId.ToString() } }
|
||||
};
|
||||
|
||||
_stripeEventService.GetSubscription(stripeEvent, true).Returns(subscription);
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(subscription.Metadata)
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
_providerRepository.GetByIdAsync(providerId).Returns(provider);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(stripeEvent);
|
||||
|
||||
// Assert
|
||||
Assert.False(provider.Enabled);
|
||||
await _providerService.Received(1).UpdateAsync(provider);
|
||||
await _scheduler.Received(1).ScheduleJob(
|
||||
Arg.Is<IJobDetail>(j =>
|
||||
j.JobType == typeof(ProviderOrganizationDisableJob) &&
|
||||
j.JobDataMap.GetString("providerId") == providerId.ToString() &&
|
||||
j.JobDataMap.GetString("expirationDate") == expirationDate.ToString("O")),
|
||||
Arg.Is<ITrigger>(t => t.Key.Name == $"disable-trigger-{providerId}"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user