diff --git a/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs b/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs index 96220b5071ba..8356d871208c 100644 --- a/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs +++ b/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs @@ -112,14 +112,11 @@ private async Task HandleOrganizationUpcomingInvoiceAsync( var plan = await pricingClient.GetPlanOrThrow(organization.PlanType); - var milestone3 = featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3); - var subscriptionAligned = await AlignOrganizationSubscriptionConcernsAsync( organization, @event, subscription, - plan, - milestone3); + plan); /* * Subscription alignment sends out a different version of our Upcoming Invoice email, so we don't need to continue @@ -210,17 +207,14 @@ await stripeAdapter.UpdateCustomerAsync(subscription.CustomerId, /// The Stripe event associated with this operation. /// The organization's subscription. /// The organization's current plan. - /// A flag indicating whether the third milestone is enabled. /// Whether the operation resulted in an updated subscription. private async Task AlignOrganizationSubscriptionConcernsAsync( Organization organization, Event @event, Subscription subscription, - Plan plan, - bool milestone3) + Plan plan) { - // currently these are the only plans that need aligned and both require the same flag and share most of the logic - if (!milestone3 || plan.Type is not (PlanType.FamiliesAnnually2019 or PlanType.FamiliesAnnually2025)) + if (plan.Type is not (PlanType.FamiliesAnnually2019 or PlanType.FamiliesAnnually2025)) { return false; } diff --git a/src/Core/Billing/Pricing/PricingClient.cs b/src/Core/Billing/Pricing/PricingClient.cs index 127eab1fbcd0..c95fc450efb3 100644 --- a/src/Core/Billing/Pricing/PricingClient.cs +++ b/src/Core/Billing/Pricing/PricingClient.cs @@ -3,7 +3,6 @@ using Bit.Core.Billing.Enums; using Bit.Core.Billing.Pricing.Organizations; using Bit.Core.Exceptions; -using Bit.Core.Services; using Bit.Core.Settings; using Microsoft.Extensions.Logging; @@ -13,7 +12,6 @@ namespace Bit.Core.Billing.Pricing; using PremiumPlan = Premium.Plan; public class PricingClient( - IFeatureService featureService, GlobalSettings globalSettings, HttpClient httpClient, ILogger logger) : IPricingClient @@ -40,7 +38,7 @@ public class PricingClient( var plan = await response.Content.ReadFromJsonAsync(); return plan == null ? throw new BillingException(message: "Deserialization of Pricing Service response resulted in null") - : new PlanAdapter(PreProcessFamiliesPreMigrationPlan(plan)); + : new PlanAdapter(plan); } if (response.StatusCode == HttpStatusCode.NotFound) @@ -74,7 +72,7 @@ public async Task> ListPlans() var plans = await response.Content.ReadFromJsonAsync>(); return plans == null ? throw new BillingException(message: "Deserialization of Pricing Service response resulted in null") - : plans.Select(OrganizationPlan (plan) => new PlanAdapter(PreProcessFamiliesPreMigrationPlan(plan))).ToList(); + : plans.Select(OrganizationPlan (plan) => new PlanAdapter(plan)).ToList(); } throw new BillingException( @@ -121,10 +119,7 @@ public async Task> ListPremiumPlans() PlanType.EnterpriseMonthly2020 => "enterprise-monthly-2020", PlanType.EnterpriseMonthly2023 => "enterprise-monthly-2023", PlanType.FamiliesAnnually => "families", - PlanType.FamiliesAnnually2025 => - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3) - ? "families-2025" - : "families", + PlanType.FamiliesAnnually2025 => "families-2025", PlanType.FamiliesAnnually2019 => "families-2019", PlanType.Free => "free", PlanType.TeamsAnnually => "teams-annually", @@ -139,19 +134,4 @@ public async Task> ListPremiumPlans() PlanType.TeamsStarter2023 => "teams-starter-2023", _ => null }; - - /// - /// Safeguard used until the feature flag is enabled. Pricing service will return the - /// 2025PreMigration plan with "families" lookup key. When that is detected and the FF - /// is still disabled, set the lookup key to families-2025 so PlanAdapter will assign - /// the correct plan. - /// - /// The plan to preprocess - private Plan PreProcessFamiliesPreMigrationPlan(Plan plan) - { - if (plan.LookupKey == "families" && !featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3)) - plan.LookupKey = "families-2025"; - return plan; - } - } diff --git a/test/Billing.Test/Services/UpcomingInvoiceHandlerTests.cs b/test/Billing.Test/Services/UpcomingInvoiceHandlerTests.cs index 3f26e0b533f5..b12200f7db69 100644 --- a/test/Billing.Test/Services/UpcomingInvoiceHandlerTests.cs +++ b/test/Billing.Test/Services/UpcomingInvoiceHandlerTests.cs @@ -1419,7 +1419,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndFamilies2019Plan_UpdatesS _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -1527,7 +1526,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndFamilies2019Plan_WithoutP _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -1558,88 +1556,6 @@ await _stripeAdapter.DidNotReceive().UpdateCustomerAsync( Arg.Any()); } - [Fact] - public async Task HandleAsync_WhenMilestone3Disabled_AndFamilies2019Plan_DoesNotUpdateSubscription() - { - // Arrange - var parsedEvent = new Event { Id = "evt_123", Type = "invoice.upcoming" }; - var customerId = "cus_123"; - var subscriptionId = "sub_123"; - var passwordManagerItemId = "si_pm_123"; - - var invoice = new Invoice - { - CustomerId = customerId, - AmountDue = 40000, - NextPaymentAttempt = DateTime.UtcNow.AddDays(7), - Lines = new StripeList - { - Data = [new() { Description = "Test Item" }] - } - }; - - var families2019Plan = new Families2019Plan(); - - var subscription = new Subscription - { - Id = subscriptionId, - CustomerId = customerId, - Items = new StripeList - { - Data = - [ - new() - { - Id = passwordManagerItemId, - Price = new Price { Id = families2019Plan.PasswordManager.StripePlanId } - } - ] - }, - AutomaticTax = new SubscriptionAutomaticTax { Enabled = true }, - Metadata = new Dictionary() - }; - - var customer = new Customer - { - Id = customerId, - Subscriptions = new StripeList { Data = [subscription] }, - Address = new Address { Country = "US" } - }; - - var organization = new Organization - { - Id = _organizationId, - BillingEmail = "org@example.com", - PlanType = PlanType.FamiliesAnnually2019 - }; - - _stripeEventService.GetInvoice(parsedEvent).Returns(invoice); - _stripeAdapter.GetCustomerAsync(customerId, Arg.Any()).Returns(customer); - _stripeEventUtilityService - .GetIdsFromMetadata(subscription.Metadata) - .Returns(new Tuple(_organizationId, null, null)); - _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); - _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); - - // Act - await _sut.HandleAsync(parsedEvent); - - // Assert - should not update subscription or organization when feature flag is disabled - await _stripeAdapter.DidNotReceive().UpdateSubscriptionAsync( - Arg.Any(), - Arg.Is(o => o.Discounts != null)); - - await _organizationRepository.DidNotReceive().ReplaceAsync( - Arg.Is(org => org.PlanType == PlanType.FamiliesAnnually)); - - // Families plan is excluded from tax exempt alignment - await _stripeAdapter.DidNotReceive().UpdateCustomerAsync( - Arg.Any(), - Arg.Any()); - } - [Fact] public async Task HandleAsync_WhenMilestone3Enabled_ButNotFamilies2019Plan_DoesNotUpdateSubscription() { @@ -1697,7 +1613,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_ButNotFamilies2019Plan_DoesN .Returns(new Tuple(_organizationId, null, null)); _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -1772,7 +1687,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndPasswordManagerItemNotFou .Returns(new Tuple(_organizationId, null, null)); _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -1860,7 +1774,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndUpdateFails_LogsErrorAndS _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Simulate update failure @@ -1958,7 +1871,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndCouponNotFound_LogsErrorA _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); _stripeAdapter.GetCouponAsync(CouponIDs.Milestone3SubscriptionDiscount).Returns((Coupon)null); _stripeAdapter.UpdateSubscriptionAsync(Arg.Any(), Arg.Any()) @@ -2058,7 +1970,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndCouponPercentOffIsNull_Lo _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); _stripeAdapter.GetCouponAsync(CouponIDs.Milestone3SubscriptionDiscount).Returns(coupon); _stripeAdapter.UpdateSubscriptionAsync(Arg.Any(), Arg.Any()) @@ -2163,7 +2074,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndSeatAddOnExists_DeletesIt _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -2277,7 +2187,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndSeatAddOnWithQuantityOne_ _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -2398,7 +2307,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_WithPremiumAccessAndSeatAddO _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -2503,7 +2411,6 @@ public async Task HandleAsync_WhenMilestone3Enabled_AndFamilies2025Plan_UpdatesS _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2025).Returns(families2025Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(familiesPlan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); // Act @@ -2534,83 +2441,6 @@ await _mailer.Received(1).SendEmail( email.View.MonthlyRenewalPrice == (familiesPlan.PasswordManager.BasePrice / 12).ToString("C", new CultureInfo("en-US")))); } - [Fact] - public async Task HandleAsync_WhenMilestone3Disabled_AndFamilies2025Plan_DoesNotUpdateSubscription() - { - // Arrange - var parsedEvent = new Event { Id = "evt_123", Type = "invoice.upcoming" }; - var customerId = "cus_123"; - var subscriptionId = "sub_123"; - var passwordManagerItemId = "si_pm_123"; - - var invoice = new Invoice - { - CustomerId = customerId, - AmountDue = 40000, - NextPaymentAttempt = DateTime.UtcNow.AddDays(7), - Lines = new StripeList - { - Data = [new() { Description = "Test Item" }] - } - }; - - var families2025Plan = new Families2025Plan(); - - var subscription = new Subscription - { - Id = subscriptionId, - CustomerId = customerId, - Items = new StripeList - { - Data = - [ - new() - { - Id = passwordManagerItemId, - Price = new Price { Id = families2025Plan.PasswordManager.StripePlanId } - } - ] - }, - AutomaticTax = new SubscriptionAutomaticTax { Enabled = true }, - Metadata = new Dictionary() - }; - - var customer = new Customer - { - Id = customerId, - Subscriptions = new StripeList { Data = [subscription] }, - Address = new Address { Country = "US" } - }; - - var organization = new Organization - { - Id = _organizationId, - BillingEmail = "org@example.com", - PlanType = PlanType.FamiliesAnnually2025 - }; - - _stripeEventService.GetInvoice(parsedEvent).Returns(invoice); - _stripeAdapter.GetCustomerAsync(customerId, Arg.Any()).Returns(customer); - _stripeEventUtilityService - .GetIdsFromMetadata(subscription.Metadata) - .Returns(new Tuple(_organizationId, null, null)); - _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); - _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2025).Returns(families2025Plan); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); - - // Act - await _sut.HandleAsync(parsedEvent); - - // Assert - should not update subscription or organization when feature flag is disabled - await _stripeAdapter.DidNotReceive().UpdateSubscriptionAsync( - Arg.Any(), - Arg.Any()); - - await _organizationRepository.DidNotReceive().ReplaceAsync( - Arg.Is(org => org.PlanType == PlanType.FamiliesAnnually)); - } - #region Premium Renewal Email Tests [Fact] @@ -3084,7 +2914,6 @@ public async Task HandleAsync_Families_DeferEnabled_CallsScheduler() _organizationRepository.GetByIdAsync(_organizationId).Returns(organization); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019).Returns(families2019Plan); _pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually).Returns(new FamiliesPlan()); - _featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.PM32645_DeferPriceMigrationToRenewal).Returns(true); _stripeEventUtilityService.IsSponsoredSubscription(subscription).Returns(false); _stripeAdapter.GetCouponAsync(CouponIDs.Milestone3SubscriptionDiscount) diff --git a/test/Core.Test/Billing/Pricing/PricingClientTests.cs b/test/Core.Test/Billing/Pricing/PricingClientTests.cs index caca997ed1db..83105ea7d17f 100644 --- a/test/Core.Test/Billing/Pricing/PricingClientTests.cs +++ b/test/Core.Test/Billing/Pricing/PricingClientTests.cs @@ -3,7 +3,6 @@ using Bit.Core.Billing.Enums; using Bit.Core.Billing.Pricing; using Bit.Core.Exceptions; -using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Microsoft.Extensions.Logging; @@ -17,10 +16,10 @@ namespace Bit.Core.Test.Billing.Pricing; [SutProviderCustomize] public class PricingClientTests { - #region GetLookupKey Tests (via GetPlan) + #region GetPlan Lookup Key Tests [Fact] - public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagEnabled_UsesFamilies2025LookupKey() + public async Task GetPlan_WithFamiliesAnnually2025_UsesFamilies2025LookupKey() { // Arrange var mockHttp = new MockHttpMessageHandler(); @@ -32,82 +31,6 @@ public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagEnabled_UsesFami mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond("application/json", planJson); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - - var globalSettings = new GlobalSettings { SelfHosted = false }; - - var httpClient = new HttpClient(mockHttp) - { - BaseAddress = new Uri("https://test.com/") - }; - - var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); - - // Act - var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually2025); - - // Assert - Assert.NotNull(result); - Assert.Equal(PlanType.FamiliesAnnually2025, result.Type); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagDisabled_UsesFamiliesLookupKey() - { - // Arrange - var mockHttp = new MockHttpMessageHandler(); - var planJson = CreatePlanJson("families", "Families", "families", 40M, "price_id"); - - mockHttp.Expect(HttpMethod.Get, "https://test.com/plans/organization/families") - .Respond("application/json", planJson); - - mockHttp.When(HttpMethod.Get, "*/plans/organization/*") - .Respond("application/json", planJson); - - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - - var globalSettings = new GlobalSettings { SelfHosted = false }; - - var httpClient = new HttpClient(mockHttp) - { - BaseAddress = new Uri("https://test.com/") - }; - - var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); - - // Act - var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually2025); - - // Assert - Assert.NotNull(result); - // PreProcessFamiliesPreMigrationPlan should change "families" to "families-2025" when FF is disabled - Assert.Equal(PlanType.FamiliesAnnually2025, result.Type); - mockHttp.VerifyNoOutstandingExpectation(); - } - - #endregion - - #region PreProcessFamiliesPreMigrationPlan Tests (via GetPlan) - - [Fact] - public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagDisabled_ReturnsFamiliesAnnually2025PlanType() - { - // Arrange - var mockHttp = new MockHttpMessageHandler(); - // billing-pricing returns "families" lookup key because the flag is off - var planJson = CreatePlanJson("families", "Families", "families", 40M, "price_id"); - - mockHttp.When(HttpMethod.Get, "*/plans/organization/*") - .Respond("application/json", planJson); - - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -116,21 +39,19 @@ public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagDisabled_Returns }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually2025); // Assert Assert.NotNull(result); - // PreProcessFamiliesPreMigrationPlan should convert the families lookup key to families-2025 - // and the PlanAdapter should assign the correct FamiliesAnnually2025 plan type Assert.Equal(PlanType.FamiliesAnnually2025, result.Type); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagEnabled_ReturnsFamiliesAnnually2025PlanType() + public async Task GetPlan_WithFamiliesAnnually2025_ReturnsFamiliesAnnually2025PlanType() { // Arrange var mockHttp = new MockHttpMessageHandler(); @@ -139,9 +60,6 @@ public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagEnabled_ReturnsF mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond("application/json", planJson); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -150,21 +68,19 @@ public async Task GetPlan_WithFamiliesAnnually2025AndFeatureFlagEnabled_ReturnsF }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually2025); // Assert Assert.NotNull(result); - // PreProcessFamiliesPreMigrationPlan should ignore the lookup key because the flag is on - // and the PlanAdapter should assign the correct FamiliesAnnually2025 plan type Assert.Equal(PlanType.FamiliesAnnually2025, result.Type); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task GetPlan_WithFamiliesAnnuallyAndFeatureFlagEnabled_ReturnsFamiliesAnnuallyPlanType() + public async Task GetPlan_WithFamiliesAnnually_ReturnsFamiliesAnnuallyPlanType() { // Arrange var mockHttp = new MockHttpMessageHandler(); @@ -173,9 +89,6 @@ public async Task GetPlan_WithFamiliesAnnuallyAndFeatureFlagEnabled_ReturnsFamil mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond("application/json", planJson); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -184,15 +97,13 @@ public async Task GetPlan_WithFamiliesAnnuallyAndFeatureFlagEnabled_ReturnsFamil }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually); // Assert Assert.NotNull(result); - // PreProcessFamiliesPreMigrationPlan should ignore the lookup key because the flag is on - // and the PlanAdapter should assign the correct FamiliesAnnually plan type Assert.Equal(PlanType.FamiliesAnnually, result.Type); mockHttp.VerifyNoOutstandingExpectation(); } @@ -210,9 +121,6 @@ public async Task GetPlan_WithOtherLookupKey_KeepsLookupKeyUnchanged() mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond("application/json", planJson); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -221,7 +129,7 @@ public async Task GetPlan_WithOtherLookupKey_KeepsLookupKeyUnchanged() }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetPlan(PlanType.EnterpriseAnnually); @@ -237,47 +145,7 @@ public async Task GetPlan_WithOtherLookupKey_KeepsLookupKeyUnchanged() #region ListPlans Tests [Fact] - public async Task ListPlans_WithFeatureFlagDisabled_ReturnsListWithPreProcessing() - { - // Arrange - var mockHttp = new MockHttpMessageHandler(); - // biling-pricing would return "families" because the flag is disabled - var plansJson = $@"[ - {CreatePlanJson("families", "Families", "families", 40M, "price_id")}, - {CreatePlanJson("enterprise-annually", "Enterprise", "enterprise", 144M, "price_id")} - ]"; - - mockHttp.When(HttpMethod.Get, "*/plans/organization") - .Respond("application/json", plansJson); - - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(false); - - var globalSettings = new GlobalSettings { SelfHosted = false }; - - var httpClient = new HttpClient(mockHttp) - { - BaseAddress = new Uri("https://test.com/") - }; - - var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); - - // Act - var result = await pricingClient.ListPlans(); - - // Assert - Assert.NotNull(result); - Assert.Equal(2, result.Count); - // First plan should have been preprocessed from "families" to "families-2025" - Assert.Equal(PlanType.FamiliesAnnually2025, result[0].Type); - // Second plan should remain unchanged - Assert.Equal(PlanType.EnterpriseAnnually, result[1].Type); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task ListPlans_WithFeatureFlagEnabled_ReturnsListWithoutPreProcessing() + public async Task ListPlans_ReturnsPlans() { // Arrange var mockHttp = new MockHttpMessageHandler(); @@ -288,9 +156,6 @@ public async Task ListPlans_WithFeatureFlagEnabled_ReturnsListWithoutPreProcessi mockHttp.When(HttpMethod.Get, "*/plans/organization") .Respond("application/json", plansJson); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -299,7 +164,7 @@ public async Task ListPlans_WithFeatureFlagEnabled_ReturnsListWithoutPreProcessi }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.ListPlans(); @@ -307,7 +172,6 @@ public async Task ListPlans_WithFeatureFlagEnabled_ReturnsListWithoutPreProcessi // Assert Assert.NotNull(result); Assert.Single(result); - // Plan should remain as FamiliesAnnually when FF is enabled Assert.Equal(PlanType.FamiliesAnnually, result[0].Type); mockHttp.VerifyNoOutstandingExpectation(); } @@ -353,9 +217,6 @@ public async Task GetPlan_WhenPricingServiceReturnsNotFound_ReturnsNull() mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond(HttpStatusCode.NotFound); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -364,7 +225,7 @@ public async Task GetPlan_WhenPricingServiceReturnsNotFound_ReturnsNull() }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetPlan(PlanType.FamiliesAnnually2025); @@ -381,9 +242,6 @@ public async Task GetPlan_WhenPricingServiceReturnsError_ThrowsBillingException( mockHttp.When(HttpMethod.Get, "*/plans/organization/*") .Respond(HttpStatusCode.InternalServerError); - var featureService = Substitute.For(); - featureService.IsEnabled(FeatureFlagKeys.PM26462_Milestone_3).Returns(true); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -392,7 +250,7 @@ public async Task GetPlan_WhenPricingServiceReturnsError_ThrowsBillingException( }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act & Assert await Assert.ThrowsAsync(() => @@ -427,8 +285,6 @@ public async Task ListPlans_WhenPricingServiceReturnsError_ThrowsBillingExceptio mockHttp.When(HttpMethod.Get, "*/plans/organization") .Respond(HttpStatusCode.InternalServerError); - var featureService = Substitute.For(); - var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -437,7 +293,7 @@ public async Task ListPlans_WhenPricingServiceReturnsError_ThrowsBillingExceptio }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act & Assert await Assert.ThrowsAsync(() => @@ -461,7 +317,6 @@ public async Task ListPremiumPlans_Success_ReturnsPremiumPlans() mockHttp.When(HttpMethod.Get, "*/plans/premium") .Respond("application/json", plansJson); - var featureService = Substitute.For(); var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -470,7 +325,7 @@ public async Task ListPremiumPlans_Success_ReturnsPremiumPlans() }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.ListPremiumPlans(); @@ -514,7 +369,6 @@ public async Task ListPremiumPlans_WhenPricingServiceReturnsError_ThrowsBillingE mockHttp.When(HttpMethod.Get, "*/plans/premium") .Respond(HttpStatusCode.InternalServerError); - var featureService = Substitute.For(); var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -523,7 +377,7 @@ public async Task ListPremiumPlans_WhenPricingServiceReturnsError_ThrowsBillingE }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act & Assert await Assert.ThrowsAsync(() => @@ -547,7 +401,6 @@ public async Task GetAvailablePremiumPlan_WithAvailablePlan_ReturnsIt() mockHttp.When(HttpMethod.Get, "*/plans/premium") .Respond("application/json", plansJson); - var featureService = Substitute.For(); var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -556,7 +409,7 @@ public async Task GetAvailablePremiumPlan_WithAvailablePlan_ReturnsIt() }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act var result = await pricingClient.GetAvailablePremiumPlan(); @@ -579,7 +432,6 @@ public async Task GetAvailablePremiumPlan_WithNoAvailablePlan_ThrowsNotFoundExce mockHttp.When(HttpMethod.Get, "*/plans/premium") .Respond("application/json", plansJson); - var featureService = Substitute.For(); var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -588,7 +440,7 @@ public async Task GetAvailablePremiumPlan_WithNoAvailablePlan_ThrowsNotFoundExce }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act & Assert await Assert.ThrowsAsync(() => @@ -603,7 +455,6 @@ public async Task GetAvailablePremiumPlan_WithEmptyList_ThrowsNotFoundException( mockHttp.When(HttpMethod.Get, "*/plans/premium") .Respond("application/json", "[]"); - var featureService = Substitute.For(); var globalSettings = new GlobalSettings { SelfHosted = false }; var httpClient = new HttpClient(mockHttp) @@ -612,7 +463,7 @@ public async Task GetAvailablePremiumPlan_WithEmptyList_ThrowsNotFoundException( }; var logger = Substitute.For>(); - var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger); + var pricingClient = new PricingClient(globalSettings, httpClient, logger); // Act & Assert await Assert.ThrowsAsync(() =>