public void Constructor_commands_cannot_be_used_on_aggregates_that_have_prior_events() { var order = new Order(); order.Apply(new AddItem { Price = 1, ProductName = Any.CamelCaseName() }); Action apply = () => order.Apply(new CreateOrder(Any.CamelCaseName())); apply.ShouldThrow<ConcurrencyException>(); }
public void The_clock_set_in_the_CommandContext_is_used_by_resulting_events() { var created = DateTimeOffset.Parse("2014-05-15 00:00:00"); var addItem = new AddItem { ProductName = "Widget", Price = 3.99m }; Order order; using (CommandContext.Establish(addItem, Clock.Create(() => created))) { order = new Order(new CreateOrder(Any.FullName())); order.Apply(addItem); } order.Events() .Count() .Should() .Be(3); order.Events() .Should() .OnlyContain(e => e.Timestamp == created); }
public void Command_applicability_can_be_validated_by_the_command_class() { var cancel = new Cancel(); var order = new Order(); order.IsValidTo(cancel).Should().Be(true); order.Apply(new Deliver()); order.IsValidTo(cancel).Should().Be(false); }
public void The_event_Actor_is_set_from_the_Command_Principal() { // arrange var customer = new Customer(Any.FullName()); var serviceRepresentative = new CustomerServiceRepresentative { Name = Any.FullName() }; var command = new SpecifyShippingInfo { Principal = serviceRepresentative }; var order = new Order(new CreateOrder(customer.Name) { Principal = customer }); // act order.Apply(command); // assert order.Events() .OfType<Order.Created>() .Single() .Actor() .Should() .Be(customer.Name); order.Events() .OfType<Order.ShippingMethodSelected>() .Single() .Actor() .Should() .Be(serviceRepresentative.Name); }
public async Task When_a_command_is_scheduled_but_the_aggregate_it_applies_to_is_not_found_then_the_command_is_retried() { // create and cancel an order for a nonexistent customer account var customerId = Any.Guid(); Console.WriteLine(new { customerId }); var order = new Order(new CreateOrder(Any.FullName()) { CustomerId = customerId }); order.Apply(new Cancel()); await orderRepository.Save(order); using (var db = new CommandSchedulerDbContext()) { db.ScheduledCommands .Where(c => c.AggregateId == customerId) .Should() .ContainSingle(c => c.AppliedTime == null); } // act // now save the customer and advance the clock await accountRepository.Save(new CustomerAccount(customerId).Apply(new ChangeEmailAddress(Any.Email()))); await clockTrigger.AdvanceClock(clockName, TimeSpan.FromMinutes(2)); using (var db = new CommandSchedulerDbContext()) { db.ScheduledCommands .Where(c => c.AggregateId == customerId) .Should() .ContainSingle(c => c.AppliedTime != null); var customer = await accountRepository.GetLatest(customerId); customer.Events() .Last() .Should().BeOfType<CustomerAccount.OrderCancelationConfirmationEmailSent>(); } }
public async Task When_a_command_is_scheduled_but_the_event_that_triggered_it_was_not_successfully_written_then_the_command_is_not_applied() { VirtualClock.Start(); // create a customer account var customer = new CustomerAccount(Any.Guid()) .Apply(new ChangeEmailAddress(Any.Email())); await accountRepository.Save(customer); var order = new Order(new CreateOrder(Any.FullName()) { CustomerId = customer.Id }); // act // cancel the order but don't save it order.Apply(new Cancel()); // assert // verify that the customer did not receive the scheduled NotifyOrderCanceled command customer = await accountRepository.GetLatest(customer.Id); customer.Events().Last().Should().BeOfType<CustomerAccount.EmailAddressChanged>(); using (var db = new CommandSchedulerDbContext()) { db.ScheduledCommands .Where(c => c.AggregateId == customer.Id) .Should() .ContainSingle(c => c.AppliedTime == null && c.DueTime == null); } }
public async Task When_a_command_is_scheduled_but_an_exception_is_thrown_in_a_handler_then_an_error_is_recorded() { Configuration.Current.UseDependency(_ => new CustomerAccount.OrderEmailConfirmer { SendOrderConfirmationEmail = x => { throw new Exception("drat!"); } }); // create a customer account var customer = new CustomerAccount(Any.Guid()) .Apply(new ChangeEmailAddress(Any.Email())); await accountRepository.Save(customer); var order = new Order(new CreateOrder(Any.FullName()) { CustomerId = customer.Id }); await orderRepository.Save(order); // act order.Apply(new Cancel()); await orderRepository.Save(order); await clockTrigger.AdvanceClock(clockName, TimeSpan.FromMinutes(1.2)); using (var db = new CommandSchedulerDbContext()) { db.Errors .Where(e => e.ScheduledCommand.AggregateId == customer.Id) .Should() .Contain(e => e.Error.Contains("drat!")); } }
public void When_a_command_is_applied_its_updates_are_applied_to_the_state_of_the_aggregate() { var order = new Order(); order.IsCancelled.Should().Be(false); order.Apply(new Cancel()); order.IsCancelled.Should().Be(true); }
public void Aggregates_can_be_re_sourced_in_memory_to_older_versions() { var originalName = Any.FullName(); var order = new Order(new CreateOrder(originalName)); order.Apply(new ChangeCustomerInfo { CustomerName = Any.FullName() }); var orderAtOlderVersion = order.AsOfVersion(1); orderAtOlderVersion.CustomerName.Should().Be(originalName); }
public void When_there_are_gaps_in_the_event_sequence_then_new_events_have_the_correct_sequence_numbers_after_save() { // arrange var id = Guid.NewGuid(); var order = new Order(id, new Order.Created { AggregateId = id, SequenceNumber = 1, CustomerId = Any.Guid() }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 4, ProductName = "foo", Price = 1 }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 8, ProductName = "foo", Price = 1 }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 103, ProductName = "foo", Price = 1 } ); // act order.Apply(new Cancel()); order.ConfirmSave(); // assert order.EventHistory.Last().Should().BeOfType<Order.Cancelled>(); order.EventHistory.Last().SequenceNumber.Should().Be(104); }
public void When_there_are_gaps_in_the_event_sequence_then_new_events_have_the_correct_sequence_numbers_prior_to_save() { // arrange var id = Guid.NewGuid(); var order = new Order(id, new Order.Created { AggregateId = id, SequenceNumber = 1, CustomerId = Any.Guid() }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 4, ProductName = "foo", Price = 1 }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 8, ProductName = "foo", Price = 1 }, new Order.ItemAdded { AggregateId = id, SequenceNumber = 103, ProductName = "foo", Price = 1 } ); // act order.Apply(new Cancel()); // assert order.Version.Should().Be(104); order.PendingEvents.Last().SequenceNumber.Should().Be(104); }