using Bit.Core.Models.Business; using Stripe; using Xunit; namespace Bit.Core.Test.Models.Business; public class SubscriptionInfoTests { [Fact] public void BillingSubscriptionItem_NullPlan_HandlesGracefully() { // Arrange - SubscriptionItem with null Plan var subscriptionItem = new SubscriptionItem { Plan = null, Quantity = 1 }; // Act var result = new SubscriptionInfo.BillingSubscription.BillingSubscriptionItem(subscriptionItem); // Assert - Should handle null Plan gracefully Assert.Null(result.ProductId); Assert.Null(result.Name); Assert.Equal(0m, result.Amount); // Defaults to 0 when Plan is null Assert.Null(result.Interval); Assert.Equal(1, result.Quantity); Assert.False(result.SponsoredSubscriptionItem); Assert.False(result.AddonSubscriptionItem); } [Fact] public void BillingSubscriptionItem_NullAmount_SetsToZero() { // Arrange - SubscriptionItem with Plan but null Amount var subscriptionItem = new SubscriptionItem { Plan = new Plan { ProductId = "prod_test", Nickname = "Test Plan", Amount = null, // Null amount Interval = "month" }, Quantity = 1 }; // Act var result = new SubscriptionInfo.BillingSubscription.BillingSubscriptionItem(subscriptionItem); // Assert - Should default to 0 when Amount is null Assert.Equal("prod_test", result.ProductId); Assert.Equal("Test Plan", result.Name); Assert.Equal(0m, result.Amount); // Business rule: defaults to 0 when null Assert.Equal("month", result.Interval); Assert.Equal(1, result.Quantity); } [Fact] public void BillingSubscriptionItem_ZeroAmount_PreservesZero() { // Arrange - SubscriptionItem with Plan and zero Amount var subscriptionItem = new SubscriptionItem { Plan = new Plan { ProductId = "prod_test", Nickname = "Test Plan", Amount = 0, // Zero amount (0 cents) Interval = "month" }, Quantity = 1 }; // Act var result = new SubscriptionInfo.BillingSubscription.BillingSubscriptionItem(subscriptionItem); // Assert - Should preserve zero amount Assert.Equal("prod_test", result.ProductId); Assert.Equal("Test Plan", result.Name); Assert.Equal(0m, result.Amount); // Zero amount preserved Assert.Equal("month", result.Interval); } [Fact] public void BillingUpcomingInvoice_ZeroAmountDue_ConvertsToZero() { // Arrange - Invoice with zero AmountDue // Note: Stripe's Invoice.AmountDue is non-nullable long, so we test with 0 // The null-coalescing operator (?? 0) in the constructor handles the case where // ConvertFromStripeMinorUnits returns null, but since AmountDue is non-nullable, // this test verifies the conversion path works correctly for zero values var invoice = new Invoice { AmountDue = 0, // Zero amount due (0 cents) Created = DateTime.UtcNow }; // Act var result = new SubscriptionInfo.BillingUpcomingInvoice(invoice); // Assert - Should convert zero correctly Assert.Equal(0m, result.Amount); Assert.NotNull(result.Date); } [Fact] public void BillingUpcomingInvoice_ValidAmountDue_ConvertsCorrectly() { // Arrange - Invoice with valid AmountDue var invoice = new Invoice { AmountDue = 2500, // 2500 cents = $25.00 Created = DateTime.UtcNow }; // Act var result = new SubscriptionInfo.BillingUpcomingInvoice(invoice); // Assert - Should convert correctly Assert.Equal(25.00m, result.Amount); // Converted from cents Assert.NotNull(result.Date); } }