using After; namespace BeforeAfter.Tests; /// /// Unit tests for : /// barrier semantics (strictly greater), integer-percent units, half-up /// rounding of the discount amount, input validation, and the guarantee that /// the calculation never mutates the order. /// public class DiscountCalculatorTests { // --------------------------------------------------------------------- // The barrier: only a total strictly greater than it is discounted. // --------------------------------------------------------------------- [Fact] public void A_total_below_the_barrier_gets_no_discount() { var order = new Order { Id = Guid.NewGuid(), Description = "small", TotalInCents = 50_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 10); Assert.Equal(0, discounted.DiscountPercentage); Assert.Equal(50_00, discounted.DiscountedTotalInCents); Assert.Equal(50_00, discounted.TotalInCents); } [Fact] public void A_total_exactly_at_the_barrier_gets_no_discount() { var order = new Order { Id = Guid.NewGuid(), TotalInCents = 100_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 10); Assert.Equal(0, discounted.DiscountPercentage); Assert.Equal(100_00, discounted.DiscountedTotalInCents); } // --------------------------------------------------------------------- // Above the barrier: percentage applied, total reduced. // --------------------------------------------------------------------- [Fact] public void A_total_above_the_barrier_gets_the_percentage_discount() { var order = new Order { Id = Guid.NewGuid(), TotalInCents = 150_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 10); Assert.Equal(10, discounted.DiscountPercentage); Assert.Equal(135_00, discounted.DiscountedTotalInCents); // 10 % of 150 = 15 off Assert.Equal(150_00, discounted.TotalInCents); } [Fact] public void A_zero_percentage_discounts_nothing_even_above_the_barrier() { var order = new Order { Id = Guid.NewGuid(), TotalInCents = 150_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 0); Assert.Equal(0, discounted.DiscountPercentage); Assert.Equal(150_00, discounted.DiscountedTotalInCents); } [Fact] public void A_hundred_percent_discount_leaves_nothing() { var order = new Order { Id = Guid.NewGuid(), TotalInCents = 150_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 100); Assert.Equal(100, discounted.DiscountPercentage); Assert.Equal(0, discounted.DiscountedTotalInCents); } // --------------------------------------------------------------------- // Half-up rounding of the discount amount (integer math, no float). // --------------------------------------------------------------------- [Theory] [InlineData(155, 16, 139)] // 15.5 rounds UP to 16 [InlineData(145, 15, 130)] // 14.5 rounds UP to 15 [InlineData(154, 15, 139)] // 15.4 rounds down to 15 [InlineData(151, 15, 136)] // 15.1 rounds down to 15 public void The_discount_amount_is_rounded_half_up(int totalInCents, int expectedDiscountCents, int expectedDiscountedTotal) { var order = new Order { Id = Guid.NewGuid(), TotalInCents = totalInCents }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100, discountPercentage: 10); Assert.Equal(totalInCents - expectedDiscountCents, discounted.DiscountedTotalInCents); Assert.Equal(expectedDiscountedTotal, discounted.DiscountedTotalInCents); } // --------------------------------------------------------------------- // Correlation and purity. // --------------------------------------------------------------------- [Fact] public void The_result_is_correlated_to_the_order_by_id() { var id = Guid.NewGuid(); var order = new Order { Id = id, TotalInCents = 150_00 }; var discounted = DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 10); Assert.Equal(id, discounted.OrderId); } [Fact] public void Calculating_does_not_mutate_the_order() { var order = new Order { Id = Guid.NewGuid(), Description = "one", TotalInCents = 150_00 }; DiscountCalculator.CalculateDiscount(order, discountBarrierCents: 100_00, discountPercentage: 10); Assert.Equal("one", order.Description); Assert.Equal(150_00, order.TotalInCents); } // --------------------------------------------------------------------- // Input validation: invalid arguments throw (loudly, not silently). // --------------------------------------------------------------------- [Theory] [InlineData(-1, 100_00, 10)] // negative total [InlineData(150_00, -1, 10)] // negative barrier [InlineData(150_00, 100_00, -1)]// percentage below 0 [InlineData(150_00, 100_00, 101)]// percentage above 100 public void Invalid_arguments_throw(int totalInCents, int discountBarrierCents, int discountPercentage) { var order = new Order { Id = Guid.NewGuid(), TotalInCents = totalInCents }; Assert.Throws( () => DiscountCalculator.CalculateDiscount(order, discountBarrierCents, discountPercentage)); } [Fact] public void A_null_order_throws() { Assert.Throws( () => DiscountCalculator.CalculateDiscount(null!, discountBarrierCents: 100_00, discountPercentage: 10)); } }