Skip to content

Commit 71a8116

Browse files
[PM-29089] Remove FF: pm-26793-fetch-premium-price-from-pricing-service - Logic (#6989)
* refactor: [PM-39087] remove PM-26793 feature flag from PricingClient * test: add ListPremiumPlans and GetAvailablePremiumPlan coverage to PricingClientTests
1 parent 1004439 commit 71a8116

File tree

2 files changed

+204
-18
lines changed

2 files changed

+204
-18
lines changed

src/Core/Billing/Pricing/PricingClient.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Net;
22
using System.Net.Http.Json;
3-
using Bit.Core.Billing.Constants;
43
using Bit.Core.Billing.Enums;
54
using Bit.Core.Billing.Pricing.Organizations;
65
using Bit.Core.Exceptions;
@@ -12,7 +11,6 @@ namespace Bit.Core.Billing.Pricing;
1211

1312
using OrganizationPlan = Bit.Core.Models.StaticStore.Plan;
1413
using PremiumPlan = Premium.Plan;
15-
using Purchasable = Premium.Purchasable;
1614

1715
public class PricingClient(
1816
IFeatureService featureService,
@@ -99,14 +97,6 @@ public async Task<List<PremiumPlan>> ListPremiumPlans()
9997
return [];
10098
}
10199

102-
var fetchPremiumPriceFromPricingService =
103-
featureService.IsEnabled(FeatureFlagKeys.PM26793_FetchPremiumPriceFromPricingService);
104-
105-
if (!fetchPremiumPriceFromPricingService)
106-
{
107-
return [CurrentPremiumPlan];
108-
}
109-
110100
var response = await httpClient.GetAsync("plans/premium");
111101

112102
if (response.IsSuccessStatusCode)
@@ -164,12 +154,4 @@ private Plan PreProcessFamiliesPreMigrationPlan(Plan plan)
164154
return plan;
165155
}
166156

167-
private static PremiumPlan CurrentPremiumPlan => new()
168-
{
169-
Name = "Premium",
170-
Available = true,
171-
LegacyYear = null,
172-
Seat = new Purchasable { Price = 10M, StripePriceId = StripeConstants.Prices.PremiumAnnually },
173-
Storage = new Purchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal, Provided = 1 }
174-
};
175157
}

test/Core.Test/Billing/Pricing/PricingClientTests.cs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Bit.Core.Billing;
33
using Bit.Core.Billing.Enums;
44
using Bit.Core.Billing.Pricing;
5+
using Bit.Core.Exceptions;
56
using Bit.Core.Services;
67
using Bit.Test.Common.AutoFixture;
78
using Bit.Test.Common.AutoFixture.Attributes;
@@ -445,6 +446,181 @@ await Assert.ThrowsAsync<BillingException>(() =>
445446

446447
#endregion
447448

449+
#region ListPremiumPlans Tests
450+
451+
[Fact]
452+
public async Task ListPremiumPlans_Success_ReturnsPremiumPlans()
453+
{
454+
// Arrange
455+
var mockHttp = new MockHttpMessageHandler();
456+
var plansJson = $@"[
457+
{CreatePremiumPlanJson("Premium", true, null, 10M, "price_premium", 4M, "price_storage", 1)},
458+
{CreatePremiumPlanJson("Premium Legacy", false, 2019, 10M, "price_premium_legacy", 4M, "price_storage_legacy", 1)}
459+
]";
460+
461+
mockHttp.When(HttpMethod.Get, "*/plans/premium")
462+
.Respond("application/json", plansJson);
463+
464+
var featureService = Substitute.For<IFeatureService>();
465+
var globalSettings = new GlobalSettings { SelfHosted = false };
466+
467+
var httpClient = new HttpClient(mockHttp)
468+
{
469+
BaseAddress = new Uri("https://test.com/")
470+
};
471+
472+
var logger = Substitute.For<ILogger<PricingClient>>();
473+
var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger);
474+
475+
// Act
476+
var result = await pricingClient.ListPremiumPlans();
477+
478+
// Assert
479+
Assert.NotNull(result);
480+
Assert.Equal(2, result.Count);
481+
Assert.Equal("Premium", result[0].Name);
482+
Assert.True(result[0].Available);
483+
Assert.Null(result[0].LegacyYear);
484+
Assert.Equal(10M, result[0].Seat.Price);
485+
Assert.Equal("price_premium", result[0].Seat.StripePriceId);
486+
Assert.Equal(4M, result[0].Storage.Price);
487+
Assert.Equal("price_storage", result[0].Storage.StripePriceId);
488+
Assert.Equal(1, result[0].Storage.Provided);
489+
Assert.Equal("Premium Legacy", result[1].Name);
490+
Assert.False(result[1].Available);
491+
Assert.Equal(2019, result[1].LegacyYear);
492+
}
493+
494+
[Theory, BitAutoData]
495+
public async Task ListPremiumPlans_WhenSelfHosted_ReturnsEmptyList(
496+
SutProvider<PricingClient> sutProvider)
497+
{
498+
// Arrange
499+
sutProvider.GetDependency<GlobalSettings>().SelfHosted = true;
500+
501+
// Act
502+
var result = await sutProvider.Sut.ListPremiumPlans();
503+
504+
// Assert
505+
Assert.NotNull(result);
506+
Assert.Empty(result);
507+
}
508+
509+
[Fact]
510+
public async Task ListPremiumPlans_WhenPricingServiceReturnsError_ThrowsBillingException()
511+
{
512+
// Arrange
513+
var mockHttp = new MockHttpMessageHandler();
514+
mockHttp.When(HttpMethod.Get, "*/plans/premium")
515+
.Respond(HttpStatusCode.InternalServerError);
516+
517+
var featureService = Substitute.For<IFeatureService>();
518+
var globalSettings = new GlobalSettings { SelfHosted = false };
519+
520+
var httpClient = new HttpClient(mockHttp)
521+
{
522+
BaseAddress = new Uri("https://test.com/")
523+
};
524+
525+
var logger = Substitute.For<ILogger<PricingClient>>();
526+
var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger);
527+
528+
// Act & Assert
529+
await Assert.ThrowsAsync<BillingException>(() =>
530+
pricingClient.ListPremiumPlans());
531+
}
532+
533+
#endregion
534+
535+
#region GetAvailablePremiumPlan Tests
536+
537+
[Fact]
538+
public async Task GetAvailablePremiumPlan_WithAvailablePlan_ReturnsIt()
539+
{
540+
// Arrange
541+
var mockHttp = new MockHttpMessageHandler();
542+
var plansJson = $@"[
543+
{CreatePremiumPlanJson("Premium Legacy", false, 2019, 10M, "price_legacy", 4M, "price_storage_legacy", 1)},
544+
{CreatePremiumPlanJson("Premium", true, null, 10M, "price_premium", 4M, "price_storage", 1)}
545+
]";
546+
547+
mockHttp.When(HttpMethod.Get, "*/plans/premium")
548+
.Respond("application/json", plansJson);
549+
550+
var featureService = Substitute.For<IFeatureService>();
551+
var globalSettings = new GlobalSettings { SelfHosted = false };
552+
553+
var httpClient = new HttpClient(mockHttp)
554+
{
555+
BaseAddress = new Uri("https://test.com/")
556+
};
557+
558+
var logger = Substitute.For<ILogger<PricingClient>>();
559+
var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger);
560+
561+
// Act
562+
var result = await pricingClient.GetAvailablePremiumPlan();
563+
564+
// Assert
565+
Assert.NotNull(result);
566+
Assert.Equal("Premium", result.Name);
567+
Assert.True(result.Available);
568+
}
569+
570+
[Fact]
571+
public async Task GetAvailablePremiumPlan_WithNoAvailablePlan_ThrowsNotFoundException()
572+
{
573+
// Arrange
574+
var mockHttp = new MockHttpMessageHandler();
575+
var plansJson = $@"[
576+
{CreatePremiumPlanJson("Premium Legacy", false, 2019, 10M, "price_legacy", 4M, "price_storage_legacy", 1)}
577+
]";
578+
579+
mockHttp.When(HttpMethod.Get, "*/plans/premium")
580+
.Respond("application/json", plansJson);
581+
582+
var featureService = Substitute.For<IFeatureService>();
583+
var globalSettings = new GlobalSettings { SelfHosted = false };
584+
585+
var httpClient = new HttpClient(mockHttp)
586+
{
587+
BaseAddress = new Uri("https://test.com/")
588+
};
589+
590+
var logger = Substitute.For<ILogger<PricingClient>>();
591+
var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger);
592+
593+
// Act & Assert
594+
await Assert.ThrowsAsync<NotFoundException>(() =>
595+
pricingClient.GetAvailablePremiumPlan());
596+
}
597+
598+
[Fact]
599+
public async Task GetAvailablePremiumPlan_WithEmptyList_ThrowsNotFoundException()
600+
{
601+
// Arrange
602+
var mockHttp = new MockHttpMessageHandler();
603+
mockHttp.When(HttpMethod.Get, "*/plans/premium")
604+
.Respond("application/json", "[]");
605+
606+
var featureService = Substitute.For<IFeatureService>();
607+
var globalSettings = new GlobalSettings { SelfHosted = false };
608+
609+
var httpClient = new HttpClient(mockHttp)
610+
{
611+
BaseAddress = new Uri("https://test.com/")
612+
};
613+
614+
var logger = Substitute.For<ILogger<PricingClient>>();
615+
var pricingClient = new PricingClient(featureService, globalSettings, httpClient, logger);
616+
617+
// Act & Assert
618+
await Assert.ThrowsAsync<NotFoundException>(() =>
619+
pricingClient.GetAvailablePremiumPlan());
620+
}
621+
622+
#endregion
623+
448624
private static string CreatePlanJson(
449625
string lookupKey,
450626
string name,
@@ -471,4 +647,32 @@ private static string CreatePlanJson(
471647
}}
472648
}}";
473649
}
650+
651+
private static string CreatePremiumPlanJson(
652+
string name,
653+
bool available,
654+
int? legacyYear,
655+
decimal seatPrice,
656+
string seatStripePriceId,
657+
decimal storagePrice,
658+
string storageStripePriceId,
659+
int storageProvided)
660+
{
661+
var legacyYearJson = legacyYear.HasValue ? legacyYear.Value.ToString() : "null";
662+
return $@"{{
663+
""name"": ""{name}"",
664+
""available"": {available.ToString().ToLower()},
665+
""legacyYear"": {legacyYearJson},
666+
""seat"": {{
667+
""stripePriceId"": ""{seatStripePriceId}"",
668+
""price"": {seatPrice},
669+
""provided"": 0
670+
}},
671+
""storage"": {{
672+
""stripePriceId"": ""{storageStripePriceId}"",
673+
""price"": {storagePrice},
674+
""provided"": {storageProvided}
675+
}}
676+
}}";
677+
}
474678
}

0 commit comments

Comments
 (0)