using CartWise.Domain.Entities; using CartWise.Domain.Enums; namespace CartWise.Domain.Tests; public class PurchaseTests { private static readonly DateTime Now = new(2026, 8, 10, 12, 0, 0, DateTimeKind.Utc); [Fact] public void Constructor_DefaultsToManualSourceAndNoStore() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); Assert.Equal(PurchaseSourceType.Manual, purchase.SourceType); Assert.Null(purchase.StoreLocationId); Assert.Empty(purchase.Items); Assert.False(purchase.IsCompleted); } [Fact] public void Constructor_ThrowsWhenHouseholdIdIsEmpty() { Assert.Throws(() => new Purchase(Guid.Empty, "user-1", Now, Now)); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void Constructor_ThrowsWhenPurchasedByUserIdIsMissing(string? userId) { Assert.Throws(() => new Purchase(Guid.NewGuid(), userId!, Now, Now)); } [Fact] public void AddItem_DefaultsQuantityToOne() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); var item = purchase.AddItem("Milk", 3.99m, Now); Assert.Equal(1m, item.Quantity); Assert.Equal(3.99m, item.LinePrice); Assert.Single(purchase.Items); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void AddItem_ThrowsWhenDescriptionIsMissing(string? description) { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); Assert.Throws(() => purchase.AddItem(description!, 3.99m, Now)); } [Fact] public void AddItem_ThrowsWhenQuantityIsZeroOrNegative() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); Assert.Throws(() => purchase.AddItem("Milk", 3.99m, Now, quantity: 0)); Assert.Throws(() => purchase.AddItem("Milk", 3.99m, Now, quantity: -1)); } [Fact] public void AddItem_ThrowsWhenLinePriceIsNegative() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); Assert.Throws(() => purchase.AddItem("Milk", -1m, Now)); } [Fact] public void AddItem_ThrowsOnCompletedPurchase() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); purchase.AddItem("Milk", 3.99m, Now); purchase.Complete(); Assert.Throws(() => purchase.AddItem("Bread", 2.5m, Now)); } [Fact] public void Complete_SumsLinePricesIntoSubtotalAndTotal() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); purchase.AddItem("Milk", 3.99m, Now); purchase.AddItem("Bread", 2.50m, Now); purchase.Complete(); Assert.Equal(6.49m, purchase.Subtotal); Assert.Equal(6.49m, purchase.Total); Assert.Null(purchase.Tax); Assert.True(purchase.IsCompleted); } [Fact] public void Complete_AddsTaxToTotalWhenProvided() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); purchase.AddItem("Milk", 10m, Now); purchase.Complete(tax: 0.80m); Assert.Equal(10m, purchase.Subtotal); Assert.Equal(0.80m, purchase.Tax); Assert.Equal(10.80m, purchase.Total); } [Fact] public void Complete_ThrowsWhenAlreadyCompleted() { var purchase = new Purchase(Guid.NewGuid(), "user-1", Now, Now); purchase.AddItem("Milk", 3.99m, Now); purchase.Complete(); Assert.Throws(() => purchase.Complete()); } }