diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs index d5e70d8f2..cd7b25dee 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs @@ -22,8 +22,8 @@ public void CantAddProductItemToNotExistingShoppingCart() => Spec.Given([]) .When(state => Handle( - FakeProductPriceCalculator.Returning(price), - new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + FakeProductPriceCalculator.Returning(Price), + new AddProductItemToShoppingCart(shoppingCartId, ProductItem, now), state ) ) @@ -36,15 +36,15 @@ public void AddsProductItemToEmptyShoppingCart() => ]) .When(state => Handle( - FakeProductPriceCalculator.Returning(price), - new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + FakeProductPriceCalculator.Returning(Price), + new AddProductItemToShoppingCart(shoppingCartId, ProductItem, now), state ) ) .Then( new ProductItemAddedToShoppingCart( shoppingCartId, - new PricedProductItem(productItem.ProductId, productItem.Quantity, price), + new PricedProductItem(ProductItem.ProductId, ProductItem.Quantity, Price), now ) ); @@ -58,15 +58,15 @@ public void AddsProductItemToNonEmptyShoppingCart() => ]) .When(state => Handle( - FakeProductPriceCalculator.Returning(otherPrice), - new AddProductItemToShoppingCart(shoppingCartId, otherProductItem, now), + FakeProductPriceCalculator.Returning(OtherPrice), + new AddProductItemToShoppingCart(shoppingCartId, OtherProductItem, now), state ) ) .Then( new ProductItemAddedToShoppingCart( shoppingCartId, - new PricedProductItem(otherProductItem.ProductId, otherProductItem.Quantity, otherPrice), + new PricedProductItem(OtherProductItem.ProductId, OtherProductItem.Quantity, OtherPrice), now ) ); @@ -80,8 +80,8 @@ public void CantAddProductItemToConfirmedShoppingCart() => ]) .When(state => Handle( - FakeProductPriceCalculator.Returning(price), - new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + FakeProductPriceCalculator.Returning(Price), + new AddProductItemToShoppingCart(shoppingCartId, ProductItem, now), state ) ) @@ -96,8 +96,8 @@ public void CantAddProductItemToCanceledShoppingCart() => ]) .When(state => Handle( - FakeProductPriceCalculator.Returning(price), - new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + FakeProductPriceCalculator.Returning(Price), + new AddProductItemToShoppingCart(shoppingCartId, ProductItem, now), state ) ) @@ -136,7 +136,7 @@ public void RemovesExistingProductItemFromShoppingCart() => ); [Fact] - public void CantRemoveNonExistingProductItemFromEmptyShoppingCart() => + public void CantRemoveNonExistingProductItemFromNonEmptyShoppingCart() => Spec.Given([ new ShoppingCartOpened(shoppingCartId, clientId, now), new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), @@ -298,16 +298,16 @@ public void CantCancelConfirmedShoppingCart() => private readonly DateTimeOffset now = DateTimeOffset.Now; private readonly Guid clientId = Guid.NewGuid(); private readonly Guid shoppingCartId = Guid.NewGuid(); - private readonly ProductItem productItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); - private readonly ProductItem otherProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); - private static readonly int price = Random.Shared.Next(1, 1000); - private static readonly int otherPrice = Random.Shared.Next(1, 1000); - private readonly PricedProductItem pricedProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200), price); - + private static readonly ProductItem ProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); + private static readonly ProductItem OtherProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); + private static readonly int Price = Random.Shared.Next(1, 1000); + private static readonly int OtherPrice = Random.Shared.Next(1, 1000); + private readonly PricedProductItem pricedProductItem = + new(ProductItem.ProductId, ProductItem.Quantity, Price); private readonly PricedProductItem otherPricedProductItem = - new(Guid.NewGuid(), Random.Shared.Next(1, 200), otherPrice); + new(OtherProductItem.ProductId, OtherProductItem.Quantity, Price); private readonly HandlerSpecification Spec = - Specification.For(ShoppingCart.Evolve, ShoppingCart.Default); + Specification.For(ShoppingCart.Evolve, ShoppingCart.Initial); } diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs index 0be318c82..96951b426 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs @@ -72,7 +72,7 @@ public bool HasEnough(PricedProductItem productItem) return currentQuantity >= quantity; } - public static ShoppingCart Default() => + public static ShoppingCart Initial() => new (default, default, default, [], default); public static ShoppingCart Evolve(ShoppingCart shoppingCart, ShoppingCartEvent @event) diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs index cbfb2ec64..8ad2ecb64 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs @@ -96,7 +96,7 @@ public static ShoppingCartConfirmed Handle(ConfirmShoppingCart command, Shopping throw new InvalidOperationException($"Confirming cart in '{shoppingCart.Status}' status is not allowed."); if(shoppingCart.ProductItems.Length == 0) - throw new InvalidOperationException($"Cannot confirm empty shopping cart"); + throw new InvalidOperationException("Cannot confirm empty shopping cart"); return new ShoppingCartConfirmed( shoppingCart.Id, diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs index 926c0d29e..6c85f004e 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs @@ -1,98 +1,324 @@ -using FluentAssertions; -using IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; +using Ogooreck.BusinessLogic; using Xunit; namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mixed; using static ShoppingCartEvent; -public static class ShoppingCartExtensions -{ - public static ShoppingCart GetShoppingCart(this EventStore eventStore, Guid shoppingCartId) => - eventStore.ReadStream(shoppingCartId) - .Aggregate(ShoppingCart.Initial(), (shoppingCart, @event) => - { - shoppingCart.Evolve(@event); - return shoppingCart; - }); -} - public class BusinessLogicTests { + // Open + [Fact] + public void OpensShoppingCart() => + Spec.Given([]) + .When(() => ShoppingCart.Open(shoppingCartId, clientId, now).Item1) + .Then(new ShoppingCartOpened(shoppingCartId, clientId, now)); + + // Add + [Fact] + public void CantAddProductItemToNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + state.AddProduct( + FakeProductPriceCalculator.Returning(Price), + ProductItem(), + now + ) + ) + .ThenThrows(); + + [Fact] + public void AddsProductItemToEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now) + ]) + .When(state => + state.AddProduct( + FakeProductPriceCalculator.Returning(Price), + ProductItem(), + now + ) + ) + .Then( + new ProductItemAddedToShoppingCart( + shoppingCartId, + new PricedProductItem + { + ProductId = ProductItem().ProductId, Quantity = ProductItem().Quantity, UnitPrice = Price + }, + now + ) + ); + + + [Fact] + public void AddsProductItemToNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + ]) + .When(state => + state.AddProduct( + FakeProductPriceCalculator.Returning(OtherPrice), + OtherProductItem(), + now + ) + ) + .Then( + new ProductItemAddedToShoppingCart( + shoppingCartId, + new PricedProductItem + { + ProductId = OtherProductItem().ProductId, + Quantity = OtherProductItem().Quantity, + UnitPrice = OtherPrice + }, + now + ) + ); + + [Fact] + public void CantAddProductItemToConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + state.AddProduct( + FakeProductPriceCalculator.Returning(Price), + ProductItem(), + now + ) + ) + .ThenThrows(); + + [Fact] + public void CantAddProductItemToCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + state.AddProduct( + FakeProductPriceCalculator.Returning(Price), + ProductItem(), + now + ) + ) + .ThenThrows(); + + // Remove + [Fact] + public void CantRemoveProductItemFromNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + state.RemoveProduct(PricedProductItem(), now) + ) + .ThenThrows(); + + [Fact] + public void RemovesExistingProductItemFromShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + ]) + .When(state => + state.RemoveProduct(PricedProductItemWithQuantity(1), now) + ) + .Then( + new ProductItemRemovedFromShoppingCart( + shoppingCartId, + PricedProductItemWithQuantity(1), + now + ) + ); + + [Fact] + public void CantRemoveNonExistingProductItemFromNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, OtherPricedProductItem(), now), + ]) + .When(state => + state.RemoveProduct(PricedProductItemWithQuantity(1), now) + ) + .ThenThrows(); + + [Fact] + public void CantRemoveExistingProductItemFromCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + state.RemoveProduct(PricedProductItemWithQuantity(1), now) + ) + .ThenThrows(); + + [Fact] + public void CantRemoveExistingProductItemFromConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + state.RemoveProduct(PricedProductItemWithQuantity(1), now) + ) + .ThenThrows(); + + // Confirm + [Fact] + public void CantConfirmNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + state.Confirm(now) + ) + .ThenThrows(); + + [Fact] + [Trait("Category", "SkipCI")] + public void CantConfirmEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + ]) + .When(state => + state.Confirm(now) + ) + .ThenThrows(); + + [Fact] + public void ConfirmsNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + ]) + .When(state => + state.Confirm(now) + ) + .Then( + new ShoppingCartConfirmed(shoppingCartId, now) + ); + + [Fact] + public void CantConfirmAlreadyConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + state.Confirm(now) + ) + .ThenThrows(); + [Fact] - public void RunningSequenceOfBusinessLogic_ShouldGenerateSequenceOfEvents() + public void CantConfirmCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + state.Confirm(now) + ) + .ThenThrows(); + + // Cancel + [Fact] + [Trait("Category", "SkipCI")] + public void CantCancelNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + state.Cancel(now) + ) + .ThenThrows(); + + [Fact] + public void CancelsEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + ]) + .When(state => + state.Cancel(now) + ) + .ThenThrows(); + + [Fact] + public void CancelsNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + ]) + .When(state => + state.Cancel(now) + ) + .Then( + new ShoppingCartCanceled(shoppingCartId, now) + ); + + [Fact] + public void CantCancelAlreadyCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + state.Cancel(now) + ) + .ThenThrows(); + + [Fact] + public void CantCancelConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, PricedProductItem(), now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + state.Cancel(now) + ) + .ThenThrows(); + + private readonly DateTimeOffset now = DateTimeOffset.Now; + private readonly Guid clientId = Guid.NewGuid(); + private readonly Guid shoppingCartId = Guid.NewGuid(); + private static readonly Guid ProductId = Guid.NewGuid(); + private static readonly Guid OtherProductId = Guid.NewGuid(); + private static readonly int Quantity = Random.Shared.Next(1, 1000); + private static readonly int OtherQuantity = Random.Shared.Next(1, 1000); + private static readonly int Price = Random.Shared.Next(1, 1000); + private static readonly int OtherPrice = Random.Shared.Next(1, 1000); + + private static readonly Func ProductItem = () => + new ProductItem { ProductId = ProductId, Quantity = Quantity }; + + private static readonly Func OtherProductItem = () => + new ProductItem { ProductId = OtherProductId, Quantity = OtherQuantity }; + + private static readonly Func PricedProductItem = () => new PricedProductItem + { + ProductId = ProductId, Quantity = Quantity, UnitPrice = Price + }; + + private static readonly Func PricedProductItemWithQuantity = + quantity => new PricedProductItem { ProductId = ProductId, Quantity = quantity, UnitPrice = Price }; + + private static readonly Func OtherPricedProductItem = () => new PricedProductItem + { + ProductId = OtherProductId, Quantity = OtherQuantity, UnitPrice = Price + }; + + private readonly HandlerSpecification Spec = + Specification.For(Evolve, ShoppingCart.Initial); + + private static readonly Func Evolve = (cart, @event) => { - var shoppingCartId = Guid.NewGuid(); - var clientId = Guid.NewGuid(); - var shoesId = Guid.NewGuid(); - var tShirtId = Guid.NewGuid(); - var twoPairsOfShoes = new ProductItem { ProductId = shoesId, Quantity = 2 }; - var pairOfShoes = new ProductItem { ProductId = shoesId, Quantity = 1 }; - var tShirt = new ProductItem { ProductId = tShirtId, Quantity = 1 }; - - var shoesPrice = 100; - var tShirtPrice = 50; - - var pricedPairOfShoes = new PricedProductItem { ProductId = shoesId, Quantity = 1, UnitPrice = shoesPrice }; - var pricedTShirt = new PricedProductItem { ProductId = tShirtId, Quantity = 1, UnitPrice = tShirtPrice }; - - var eventStore = new EventStore(); - - // Open - var (opened,_) = ShoppingCart.Open(shoppingCartId, clientId); - eventStore.AppendToStream(shoppingCartId, [opened]); - - // Add Two Pair of Shoes - var shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - ShoppingCartEvent result = shoppingCart.AddProduct( - FakeProductPriceCalculator.Returning(shoesPrice), - twoPairsOfShoes - ); - eventStore.AppendToStream(shoppingCartId, [result]); - - // Add T-Shirt - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - result = shoppingCart.AddProduct( - FakeProductPriceCalculator.Returning(tShirtPrice), - tShirt - ); - eventStore.AppendToStream(shoppingCartId, [result]); - - // Remove a pair of shoes - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - result = shoppingCart.RemoveProduct(pricedPairOfShoes); - eventStore.AppendToStream(shoppingCartId, [result]); - - // Confirm - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - result = shoppingCart.Confirm(); - eventStore.AppendToStream(shoppingCartId, [result]); - - // Try Cancel - var exception = Record.Exception(() => - { - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - result = shoppingCart.Cancel(); - eventStore.AppendToStream(shoppingCartId, [result]); - }); - exception.Should().BeOfType(); - - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - - shoppingCart.Id.Should().Be(shoppingCartId); - shoppingCart.ClientId.Should().Be(clientId); - shoppingCart.ProductItems.Should().HaveCount(2); - shoppingCart.Status.Should().Be(ShoppingCartStatus.Confirmed); - - shoppingCart.ProductItems[0].Should().BeEquivalentTo(pricedPairOfShoes); - shoppingCart.ProductItems[1].Should().BeEquivalentTo(pricedTShirt); - - var events = eventStore.ReadStream(shoppingCartId); - events.Should().HaveCount(5); - events[0].Should().BeOfType(); - events[1].Should().BeOfType(); - events[2].Should().BeOfType(); - events[3].Should().BeOfType(); - events[4].Should().BeOfType(); - } + cart.Evolve(@event); + return cart; + }; } diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs index 1360276ef..45be6aa9e 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs @@ -7,27 +7,30 @@ public abstract record ShoppingCartEvent { public record ShoppingCartOpened( Guid ShoppingCartId, - Guid ClientId + Guid ClientId, + DateTimeOffset OpenedAt ): ShoppingCartEvent; public record ProductItemAddedToShoppingCart( Guid ShoppingCartId, - PricedProductItem ProductItem + PricedProductItem ProductItem, + DateTimeOffset AddedAt ): ShoppingCartEvent; public record ProductItemRemovedFromShoppingCart( Guid ShoppingCartId, - PricedProductItem ProductItem + PricedProductItem ProductItem, + DateTimeOffset RemovedAt ): ShoppingCartEvent; public record ShoppingCartConfirmed( Guid ShoppingCartId, - DateTime ConfirmedAt + DateTimeOffset ConfirmedAt ): ShoppingCartEvent; public record ShoppingCartCanceled( Guid ShoppingCartId, - DateTime CanceledAt + DateTimeOffset CanceledAt ): ShoppingCartEvent; // This won't allow external inheritance @@ -52,12 +55,13 @@ public class ProductItem // ENTITY public class ShoppingCart: IAggregate { - public Guid Id { get; private set; } + public Guid Id { get; private set; } public Guid ClientId { get; private set; } public ShoppingCartStatus Status { get; private set; } public IList ProductItems { get; } = new List(); - public DateTime? ConfirmedAt { get; private set; } - public DateTime? CanceledAt { get; private set; } + public DateTimeOffset OpenedAt { get; private set; } + public DateTimeOffset? ConfirmedAt { get; private set; } + public DateTimeOffset? CanceledAt { get; private set; } public bool IsClosed => ShoppingCartStatus.Closed.HasFlag(Status); @@ -85,12 +89,11 @@ public void Evolve(object @event) public static (ShoppingCartOpened, ShoppingCart) Open( Guid cartId, - Guid clientId) + Guid clientId, + DateTimeOffset now + ) { - var @event = new ShoppingCartOpened( - cartId, - clientId - ); + var @event = new ShoppingCartOpened(cartId, clientId, now); return (@event, new ShoppingCart(@event)); } @@ -112,7 +115,9 @@ private void Apply(ShoppingCartOpened opened) public ProductItemAddedToShoppingCart AddProduct( IProductPriceCalculator productPriceCalculator, - ProductItem productItem) + ProductItem productItem, + DateTimeOffset now + ) { if (IsClosed) throw new InvalidOperationException( @@ -120,7 +125,7 @@ public ProductItemAddedToShoppingCart AddProduct( var pricedProductItem = productPriceCalculator.Calculate(productItem); - var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem); + var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem, now); Apply(@event); @@ -129,7 +134,7 @@ public ProductItemAddedToShoppingCart AddProduct( private void Apply(ProductItemAddedToShoppingCart productItemAdded) { - var (_, pricedProductItem) = productItemAdded; + var pricedProductItem = productItemAdded.ProductItem; var productId = pricedProductItem.ProductId; var quantityToAdd = pricedProductItem.Quantity; @@ -140,10 +145,16 @@ private void Apply(ProductItemAddedToShoppingCart productItemAdded) if (current == null) ProductItems.Add(pricedProductItem); else - ProductItems[ProductItems.IndexOf(current)].Quantity += quantityToAdd; + ProductItems[ProductItems.IndexOf(current)] = new PricedProductItem + { + Quantity = current.Quantity + quantityToAdd, ProductId = productId, UnitPrice = current.UnitPrice + }; } - public ProductItemRemovedFromShoppingCart RemoveProduct(PricedProductItem productItemToBeRemoved) + public ProductItemRemovedFromShoppingCart RemoveProduct( + PricedProductItem productItemToBeRemoved, + DateTimeOffset now + ) { if (IsClosed) throw new InvalidOperationException( @@ -152,7 +163,7 @@ public ProductItemRemovedFromShoppingCart RemoveProduct(PricedProductItem produc if (!HasEnough(productItemToBeRemoved)) throw new InvalidOperationException("Not enough product items to remove"); - var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved); + var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved, now); Apply(@event); @@ -170,7 +181,7 @@ private bool HasEnough(PricedProductItem productItem) private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) { - var (_, pricedProductItem) = productItemRemoved; + var pricedProductItem = productItemRemoved.ProductItem; var productId = pricedProductItem.ProductId; var quantityToRemove = pricedProductItem.Quantity; @@ -181,19 +192,22 @@ private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) if (current.Quantity == quantityToRemove) ProductItems.Remove(current); else - ProductItems[ProductItems.IndexOf(current)].Quantity -= quantityToRemove; + ProductItems[ProductItems.IndexOf(current)] = new PricedProductItem + { + Quantity = current.Quantity - quantityToRemove, ProductId = productId, UnitPrice = current.UnitPrice + }; } - public ShoppingCartConfirmed Confirm() + public ShoppingCartConfirmed Confirm(DateTimeOffset now) { if (IsClosed) throw new InvalidOperationException( $"Confirming cart in '{Status}' status is not allowed."); - if(ProductItems.Count == 0) - throw new InvalidOperationException($"Cannot confirm empty shopping cart"); + if (ProductItems.Count == 0) + throw new InvalidOperationException("Cannot confirm empty shopping cart"); - var @event = new ShoppingCartConfirmed(Id, DateTime.UtcNow); + var @event = new ShoppingCartConfirmed(Id, now); Apply(@event); @@ -206,13 +220,13 @@ private void Apply(ShoppingCartConfirmed confirmed) ConfirmedAt = confirmed.ConfirmedAt; } - public ShoppingCartCanceled Cancel() + public ShoppingCartCanceled Cancel(DateTimeOffset now) { if (IsClosed) throw new InvalidOperationException( $"Canceling cart in '{Status}' status is not allowed."); - var @event = new ShoppingCartCanceled(Id, DateTime.UtcNow); + var @event = new ShoppingCartCanceled(Id, now); Apply(@event); diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs index 2068c5de1..f71825467 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs @@ -1,98 +1,314 @@ -using FluentAssertions; -using IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; +using IntroductionToEventSourcing.BusinessLogic.Slimmed.Immutable; +using Ogooreck.BusinessLogic; using Xunit; namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mutable; -using static ShoppingCartEvent; - -public static class ShoppingCartExtensions -{ - public static ShoppingCart GetShoppingCart(this EventStore eventStore, Guid shoppingCartId) => - eventStore.ReadStream(shoppingCartId) - .Aggregate(ShoppingCart.Initial(), (shoppingCart, @event) => - { - shoppingCart.Evolve(@event); - return shoppingCart; - }); -} +using static Immutable.ShoppingCartEvent; +using static ShoppingCartCommand; +using static ShoppingCartService; public class BusinessLogicTests { + // Open + [Fact] + public void OpensShoppingCart() => + Spec.Given([]) + .When(() => Handle(new OpenShoppingCart(shoppingCartId, clientId, now))) + .Then(new ShoppingCartOpened(shoppingCartId, clientId, now)); + + // Add + [Fact] + public void CantAddProductItemToNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + Handle( + Immutable.FakeProductPriceCalculator.Returning(price), + new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + state + ) + ) + .ThenThrows(); + [Fact] - public void RunningSequenceOfBusinessLogic_ShouldGenerateSequenceOfEvents() - { - var shoppingCartId = Guid.NewGuid(); - var clientId = Guid.NewGuid(); - var shoesId = Guid.NewGuid(); - var tShirtId = Guid.NewGuid(); - var twoPairsOfShoes = new ProductItem { ProductId = shoesId, Quantity = 2 }; - var pairOfShoes = new ProductItem { ProductId = shoesId, Quantity = 1 }; - var tShirt = new ProductItem { ProductId = tShirtId, Quantity = 1 }; - - var shoesPrice = 100; - var tShirtPrice = 50; - - var pricedPairOfShoes = new PricedProductItem { ProductId = shoesId, Quantity = 1, UnitPrice = shoesPrice }; - var pricedTShirt = new PricedProductItem { ProductId = tShirtId, Quantity = 1, UnitPrice = tShirtPrice }; - - var eventStore = new EventStore(); - - // Open - var shoppingCart = ShoppingCart.Open(shoppingCartId, clientId); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - - // Add Two Pair of Shoes - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - shoppingCart.AddProduct( - FakeProductPriceCalculator.Returning(shoesPrice), - twoPairsOfShoes - ); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - - // Add T-Shirt - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - shoppingCart.AddProduct( - FakeProductPriceCalculator.Returning(tShirtPrice), - tShirt - ); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - - // Remove a pair of shoes - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - shoppingCart.RemoveProduct(pricedPairOfShoes); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - - // Confirm - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - shoppingCart.Confirm(); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - - // Try Cancel - var exception = Record.Exception(() => - { - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - shoppingCart.Cancel(); - eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); - }); - exception.Should().BeOfType(); - - shoppingCart = eventStore.GetShoppingCart(shoppingCartId); - - shoppingCart.Id.Should().Be(shoppingCartId); - shoppingCart.ClientId.Should().Be(clientId); - shoppingCart.ProductItems.Should().HaveCount(2); - shoppingCart.Status.Should().Be(ShoppingCartStatus.Confirmed); - - shoppingCart.ProductItems[0].Should().BeEquivalentTo(pricedPairOfShoes); - shoppingCart.ProductItems[1].Should().BeEquivalentTo(pricedTShirt); - - var events = eventStore.ReadStream(shoppingCartId); - events.Should().HaveCount(5); - events[0].Should().BeOfType(); - events[1].Should().BeOfType(); - events[2].Should().BeOfType(); - events[3].Should().BeOfType(); - events[4].Should().BeOfType(); - } + public void AddsProductItemToEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now) + ]) + .When(state => + Handle( + Immutable.FakeProductPriceCalculator.Returning(price), + new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + state + ) + ) + .Then( + new ProductItemAddedToShoppingCart( + shoppingCartId, + new Immutable.PricedProductItem(productItem.ProductId, productItem.Quantity, price), + now + ) + ); + + + [Fact] + public void AddsProductItemToNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + ]) + .When(state => + Handle( + Immutable.FakeProductPriceCalculator.Returning(otherPrice), + new AddProductItemToShoppingCart(shoppingCartId, otherProductItem, now), + state + ) + ) + .Then( + new ProductItemAddedToShoppingCart( + shoppingCartId, + new Immutable.PricedProductItem(otherProductItem.ProductId, otherProductItem.Quantity, otherPrice), + now + ) + ); + + [Fact] + public void CantAddProductItemToConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + Handle( + Immutable.FakeProductPriceCalculator.Returning(price), + new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + state + ) + ) + .ThenThrows(); + + [Fact] + public void CantAddProductItemToCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + Handle( + Immutable.FakeProductPriceCalculator.Returning(price), + new AddProductItemToShoppingCart(shoppingCartId, productItem, now), + state + ) + ) + .ThenThrows(); + + // Remove + [Fact] + public void CantRemoveProductItemFromNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, pricedProductItem, now), + state + ) + ) + .ThenThrows(); + + [Fact] + public void RemovesExistingProductItemFromShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + ]) + .When(state => + Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, pricedProductItem with { Quantity = 1 }, now), + state + ) + ) + .Then( + new ProductItemRemovedFromShoppingCart( + shoppingCartId, + pricedProductItem with { Quantity = 1 }, + now + ) + ); + + [Fact] + public void CantRemoveNonExistingProductItemFromNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + ]) + .When(state => + Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, otherPricedProductItem with { Quantity = 1 }, + now), + state + ) + ) + .ThenThrows(); + + [Fact] + public void CantRemoveExistingProductItemFromCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, pricedProductItem with { Quantity = 1 }, now), + state + ) + ) + .ThenThrows(); + + [Fact] + public void CantRemoveExistingProductItemFromConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, pricedProductItem with { Quantity = 1 }, now), + state + ) + ) + .ThenThrows(); + + // Confirm + + [Fact] + public void CantConfirmNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + Handle(new ConfirmShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + [Trait("Category", "SkipCI")] + public void CantConfirmEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + ]) + .When(state => + Handle(new ConfirmShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + public void ConfirmsNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + ]) + .When(state => + Handle(new ConfirmShoppingCart(shoppingCartId, now), state) + ) + .Then( + new ShoppingCartConfirmed(shoppingCartId, now) + ); + + [Fact] + public void CantConfirmAlreadyConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + Handle(new ConfirmShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + public void CantConfirmCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + Handle(new ConfirmShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + // Cancel + [Fact] + [Trait("Category", "SkipCI")] + public void CantCancelNotExistingShoppingCart() => + Spec.Given([]) + .When(state => + Handle(new CancelShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + public void CancelsEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + ]) + .When(state => + Handle(new CancelShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + public void CancelsNonEmptyShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + ]) + .When(state => + Handle(new CancelShoppingCart(shoppingCartId, now), state) + ) + .Then( + new ShoppingCartCanceled(shoppingCartId, now) + ); + + [Fact] + public void CantCancelAlreadyCanceledShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartCanceled(shoppingCartId, now) + ]) + .When(state => + Handle(new CancelShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + [Fact] + public void CantCancelConfirmedShoppingCart() => + Spec.Given([ + new ShoppingCartOpened(shoppingCartId, clientId, now), + new ProductItemAddedToShoppingCart(shoppingCartId, pricedProductItem, now), + new ShoppingCartConfirmed(shoppingCartId, now) + ]) + .When(state => + Handle(new CancelShoppingCart(shoppingCartId, now), state) + ) + .ThenThrows(); + + private readonly DateTimeOffset now = DateTimeOffset.Now; + private readonly Guid clientId = Guid.NewGuid(); + private readonly Guid shoppingCartId = Guid.NewGuid(); + private readonly Immutable.ProductItem productItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); + private readonly Immutable.ProductItem otherProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200)); + private static readonly int price = Random.Shared.Next(1, 1000); + private static readonly int otherPrice = Random.Shared.Next(1, 1000); + private readonly Immutable.PricedProductItem pricedProductItem = new(Guid.NewGuid(), Random.Shared.Next(1, 200), price); + + private readonly Immutable.PricedProductItem otherPricedProductItem = + new(Guid.NewGuid(), Random.Shared.Next(1, 200), otherPrice); + + + private readonly HandlerSpecification Spec = + Specification.For(Immutable.ShoppingCart.Evolve, Immutable.ShoppingCart.Initial); } diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs index ffccdf71c..fae67b5e3 100644 --- a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs @@ -7,27 +7,30 @@ public abstract record ShoppingCartEvent { public record ShoppingCartOpened( Guid ShoppingCartId, - Guid ClientId + Guid ClientId, + DateTimeOffset OpenedAt ): ShoppingCartEvent; public record ProductItemAddedToShoppingCart( Guid ShoppingCartId, - PricedProductItem ProductItem + PricedProductItem ProductItem, + DateTimeOffset AddedAt ): ShoppingCartEvent; public record ProductItemRemovedFromShoppingCart( Guid ShoppingCartId, - PricedProductItem ProductItem + PricedProductItem ProductItem, + DateTimeOffset RemovedAt ): ShoppingCartEvent; public record ShoppingCartConfirmed( Guid ShoppingCartId, - DateTime ConfirmedAt + DateTimeOffset ConfirmedAt ): ShoppingCartEvent; public record ShoppingCartCanceled( Guid ShoppingCartId, - DateTime CanceledAt + DateTimeOffset CanceledAt ): ShoppingCartEvent; // This won't allow external inheritance @@ -55,8 +58,9 @@ public class ShoppingCart: Aggregate public Guid ClientId { get; private set; } public ShoppingCartStatus Status { get; private set; } public IList ProductItems { get; } = new List(); - public DateTime? ConfirmedAt { get; private set; } - public DateTime? CanceledAt { get; private set; } + public DateTimeOffset OpenedAt { get; private set; } + public DateTimeOffset? ConfirmedAt { get; private set; } + public DateTimeOffset? CanceledAt { get; private set; } public bool IsClosed => ShoppingCartStatus.Closed.HasFlag(Status); @@ -82,23 +86,16 @@ public override void Evolve(object @event) } } - public static ShoppingCart Open( - Guid cartId, - Guid clientId) + public static ShoppingCart Open(Guid cartId, Guid clientId, DateTimeOffset now) { - return new ShoppingCart(cartId, clientId); + return new ShoppingCart(cartId, clientId, now); } public static ShoppingCart Initial() => new(); - private ShoppingCart( - Guid id, - Guid clientId) + private ShoppingCart(Guid id, Guid clientId, DateTimeOffset now) { - var @event = new ShoppingCartOpened( - id, - clientId - ); + var @event = new ShoppingCartOpened(id, clientId, now); Enqueue(@event); Apply(@event); @@ -116,7 +113,9 @@ private void Apply(ShoppingCartOpened opened) public void AddProduct( IProductPriceCalculator productPriceCalculator, - ProductItem productItem) + ProductItem productItem, + DateTimeOffset now + ) { if (IsClosed) throw new InvalidOperationException( @@ -124,7 +123,7 @@ public void AddProduct( var pricedProductItem = productPriceCalculator.Calculate(productItem); - var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem); + var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem, now); Enqueue(@event); Apply(@event); @@ -132,7 +131,7 @@ public void AddProduct( private void Apply(ProductItemAddedToShoppingCart productItemAdded) { - var (_, pricedProductItem) = productItemAdded; + var pricedProductItem = productItemAdded.ProductItem; var productId = pricedProductItem.ProductId; var quantityToAdd = pricedProductItem.Quantity; @@ -143,10 +142,13 @@ private void Apply(ProductItemAddedToShoppingCart productItemAdded) if (current == null) ProductItems.Add(pricedProductItem); else - current.Quantity += quantityToAdd; + ProductItems[ProductItems.IndexOf(current)] = new PricedProductItem + { + Quantity = current.Quantity + quantityToAdd, ProductId = productId, UnitPrice = current.UnitPrice + }; } - public void RemoveProduct(PricedProductItem productItemToBeRemoved) + public void RemoveProduct(PricedProductItem productItemToBeRemoved, DateTimeOffset now) { if (IsClosed) throw new InvalidOperationException( @@ -155,7 +157,7 @@ public void RemoveProduct(PricedProductItem productItemToBeRemoved) if (!HasEnough(productItemToBeRemoved)) throw new InvalidOperationException("Not enough product items to remove"); - var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved); + var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved, now); Enqueue(@event); Apply(@event); @@ -172,7 +174,7 @@ private bool HasEnough(PricedProductItem productItem) private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) { - var (_, pricedProductItem) = productItemRemoved; + var pricedProductItem = productItemRemoved.ProductItem; var productId = pricedProductItem.ProductId; var quantityToRemove = pricedProductItem.Quantity; @@ -182,20 +184,22 @@ private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) if (current.Quantity == quantityToRemove) ProductItems.Remove(current); - else - current.Quantity -= quantityToRemove; + else ProductItems[ProductItems.IndexOf(current)] = new PricedProductItem + { + Quantity = current.Quantity - quantityToRemove, ProductId = productId, UnitPrice = current.UnitPrice + }; } - public void Confirm() + public void Confirm(DateTimeOffset now) { if (IsClosed) throw new InvalidOperationException( $"Confirming cart in '{Status}' status is not allowed."); - if(ProductItems.Count == 0) + if (ProductItems.Count == 0) throw new InvalidOperationException($"Cannot confirm empty shopping cart"); - var @event = new ShoppingCartConfirmed(Id, DateTime.UtcNow); + var @event = new ShoppingCartConfirmed(Id, now); Enqueue(@event); Apply(@event); @@ -207,13 +211,13 @@ private void Apply(ShoppingCartConfirmed confirmed) ConfirmedAt = confirmed.ConfirmedAt; } - public void Cancel() + public void Cancel(DateTimeOffset now) { if (IsClosed) throw new InvalidOperationException( $"Canceling cart in '{Status}' status is not allowed."); - var @event = new ShoppingCartCanceled(Id, DateTime.UtcNow); + var @event = new ShoppingCartCanceled(Id, now); Enqueue(@event); Apply(@event); diff --git a/Workshops/IntroductionToEventSourcing/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index 08845d7d0..4226ef3d7 100644 --- a/Workshops/IntroductionToEventSourcing/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -62,7 +62,7 @@ public Task RemovesExistingProductItemFromShoppingCart(string apiPrefix) => [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem) diff --git a/Workshops/IntroductionToEventSourcing/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index e0ebd6d55..144054c09 100644 --- a/Workshops/IntroductionToEventSourcing/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -62,7 +62,7 @@ public Task RemovesExistingProductItemFromShoppingCart(string apiPrefix) => [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem) diff --git a/Workshops/IntroductionToEventSourcing/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index 19af93c5e..b278f48e1 100644 --- a/Workshops/IntroductionToEventSourcing/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -96,7 +96,7 @@ public Task CantRemoveExistingProductItemFromShoppingCartWithoutETag(string apiP [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem, 1) diff --git a/Workshops/IntroductionToEventSourcing/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index e56adfe8a..81d887d1b 100644 --- a/Workshops/IntroductionToEventSourcing/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -90,7 +90,7 @@ public Task CantRemoveExistingProductItemFromShoppingCartWithoutETag(string apiP [Trait("Category", "SkipCI")][InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem, 0) diff --git a/Workshops/IntroductionToEventSourcing/Solved/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/Solved/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index a9e51fd42..1d82becb8 100644 --- a/Workshops/IntroductionToEventSourcing/Solved/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/Solved/08-ApplicationLogic.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -54,7 +54,7 @@ public Task RemovesExistingProductItemFromShoppingCart(string apiPrefix) => [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem) diff --git a/Workshops/IntroductionToEventSourcing/Solved/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/Solved/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index b7bb4ff73..cac2838bc 100644 --- a/Workshops/IntroductionToEventSourcing/Solved/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/Solved/09-ApplicationLogic.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -54,7 +54,7 @@ public Task RemovesExistingProductItemFromShoppingCart(string apiPrefix) => [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem) diff --git a/Workshops/IntroductionToEventSourcing/Solved/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/Solved/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index e304d1f46..d3063a145 100644 --- a/Workshops/IntroductionToEventSourcing/Solved/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/Solved/10-OptimisticConcurrency.Marten.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -90,7 +90,7 @@ public Task CantRemoveExistingProductItemFromShoppingCartWithoutETag(string apiP [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem, 1) diff --git a/Workshops/IntroductionToEventSourcing/Solved/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs b/Workshops/IntroductionToEventSourcing/Solved/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs index ea0d68680..af899c7be 100644 --- a/Workshops/IntroductionToEventSourcing/Solved/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs +++ b/Workshops/IntroductionToEventSourcing/Solved/11-OptimisticConcurrency.EventStoreDB.Tests/ShoppingCarts/RemoveProductItemFromShoppingCartTests.cs @@ -90,7 +90,7 @@ public Task CantRemoveExistingProductItemFromShoppingCartWithoutETag(string apiP [InlineData("immutable")] [InlineData("mutable")] [InlineData("mixed")] - public Task CantRemoveNonExistingProductItemFromEmptyShoppingCart(string apiPrefix) => + public Task CantRemoveNonExistingProductItemFromNonEmptyShoppingCart(string apiPrefix) => api.Given( OpenedShoppingCart(apiPrefix, ClientId), WithProductItem(apiPrefix, ClientId, ProductItem, 0)