public void when_recalculated_then_replaces_line() { this.sut.Handle(new OrderTotalsCalculated { SourceId = orderId, Lines = new[] { new SeatOrderLine { LineTotal = 20, SeatType = this.seatCreatedEvents[1].SourceId, Quantity = 2, UnitPrice = 10 }, }, Total = 20, Version = 8, }); this.dto = this.dao.FindPricedOrder(orderId); Assert.Equal(1, dto.Lines.Count); Assert.Equal(20, dto.Lines[0].LineTotal); Assert.Equal(2, dto.Lines[0].Quantity); Assert.Equal(10, dto.Lines[0].UnitPrice); Assert.Equal(20, dto.Total); Assert.Contains("Precon", dto.Lines.Select(x => x.Description)); Assert.Equal(8, dto.OrderVersion); }
private static bool WasNotAlreadyHandled(PricedOrder pricedOrder, int eventVersion) { // This assumes that events will be handled in order, but we might get the same message more than once. if (eventVersion > pricedOrder.OrderVersion) { return(true); } else if (eventVersion == pricedOrder.OrderVersion) { Trace.TraceWarning( "Ignoring duplicate priced order update message with version {1} for order id {0}", pricedOrder.OrderId, eventVersion); return(false); } else { throw new InvalidOperationException( string.Format( @"An older order update message was received with with version {1} for order id {0}, last known version {2}. This read model generator has an expectation that the EventBus will deliver messages for the same source in order.", pricedOrder.OrderId, eventVersion, pricedOrder.OrderVersion)); } }
private static bool WasNotAlreadyHandled(PricedOrder pricedOrder, int eventVersion) { // This assumes that events will be handled in order, but we might get the same message more than once. if (eventVersion > pricedOrder.OrderVersion) { return(true); } else if (eventVersion == pricedOrder.OrderVersion) { /*Trace.TraceWarning( * "Ignoring duplicate priced order update message with version {1} for order id {0}", * pricedOrder.OrderId, * eventVersion);*/ return(false); } else { /*Trace.TraceWarning( * @"Ignoring an older order update message was received with with version {1} for order id {0}, last known version {2}. * This read model generator has an expectation that the EventBus will deliver messages for the same source in order. Nevertheless, this warning can be expected in a migration scenario.", * pricedOrder.OrderId, * eventVersion, * pricedOrder.OrderVersion);*/ return(false); } }
public given_a_new_calculated_order() { this.sut.Handle(new OrderPlaced { SourceId = orderId, ReservationAutoExpiration = DateTime.UtcNow.AddMinutes(10), Version = 2, }); this.sut.Handle(new OrderTotalsCalculated { SourceId = orderId, Lines = new[] { new SeatOrderLine { LineTotal = 50, SeatType = this.seatCreatedEvents[0].SourceId, Quantity = 10, UnitPrice = 5 }, new SeatOrderLine { LineTotal = 10, SeatType = this.seatCreatedEvents[1].SourceId, Quantity = 1, UnitPrice = 10 }, }, Total = 60, IsFreeOfCharge = true, Version = 9, }); this.dto = this.dao.FindPricedOrder(orderId); }
public void ThenTheRegistrantAssignsTheseSeats(Table table) { using (var orderController = RegistrationHelper.GetOrderController(conferenceInfo.Slug)) { PricedOrder pricedOrder = null; var timeout = DateTime.Now.Add(Constants.UI.WaitTimeout); while ((pricedOrder == null || !pricedOrder.AssignmentsId.HasValue) && DateTime.Now < timeout) { pricedOrder = RegistrationHelper.GetModel <PricedOrder>(orderController.Display(draftOrder.OrderId)); } Assert.NotNull(pricedOrder); Assert.True(pricedOrder.AssignmentsId.HasValue); var orderSeats = RegistrationHelper.GetModel <OrderSeats>(orderController.AssignSeats(pricedOrder.AssignmentsId.Value)); foreach (var row in table.Rows) { var seat = orderSeats.Seats.FirstOrDefault(s => s.SeatName == row["seat type"]); Assert.NotNull(seat); seat.Attendee.FirstName = row["first name"]; seat.Attendee.LastName = row["last name"]; seat.Attendee.Email = row["email address"]; } orderController.AssignSeats(pricedOrder.AssignmentsId.Value, orderSeats.Seats.ToList()); } }
private bool WasNotAlreadyHandled(PricedOrder pricedOrder, int eventVersion) { if (eventVersion > pricedOrder.OrderVersion) { return(true); } else if (eventVersion == pricedOrder.OrderVersion) { Trace.TraceWarning( "Ignoring duplicate priced order update message with version {1} for order id {0}", pricedOrder.OrderId, eventVersion); return(false); } else { Trace.TraceWarning( @"Ignoring an older order update message was received with with version {1} for order id {0}, last known version {2}. This read model generator has an expectation that the EventBus will deliver messages for the same source in order. Nevertheless, this warning can be expected in a migration scenario.", pricedOrder.OrderId, eventVersion, pricedOrder.OrderVersion); return(false); } }
public void Setup() { this._dbName = this.GetType().Name + "-" + Guid.NewGuid(); using (var context = new ConferenceRegistrationDbContext(this._dbName)) { if (context.Database.Exists()) { context.Database.Delete(); } context.Database.Create(); } var blobStorage = new MemoryBlobStorage(); this._sut = new PricedOrderViewModelGenerator(() => new ConferenceRegistrationDbContext(_dbName)); this._dao = new OrderDao(() => new ConferenceRegistrationDbContext(_dbName), blobStorage, new JsonTextSerializer()); this._seatCreatedEvents = new[] { new SeatCreated { SourceId = Guid.NewGuid(), Name = "General" }, new SeatCreated { SourceId = Guid.NewGuid(), Name = "Precon" } }; this._sut.Handle(this._seatCreatedEvents[0]); this._sut.Handle(this._seatCreatedEvents[1]); this._orderPlaced = new OrderPlaced { SourceId = _orderId, ReservationAutoExpiration = DateTime.UtcNow.AddMinutes(10), Version = 2, }; this._sut.Handle(_orderPlaced); this._sut.Handle(new OrderTotalsCalculated { SourceId = _orderId, Lines = new[] { new SeatOrderLine { LineTotal = 50, SeatType = this._seatCreatedEvents[0].SourceId, Quantity = 10, UnitPrice = 5 }, }, Total = 50, IsFreeOfCharge = true, Version = 4, }); this._dto = this._dao.FindPricedOrder(_orderId); }
public void then_order_placed_is_idempotent() { this.sut.Handle(orderPlaced); dto = this.dao.FindPricedOrder(orderId); Assert.NotNull(dto); Assert.InRange(dto.ReservationExpirationDate.Value, orderPlaced.ReservationAutoExpiration.AddSeconds(-1), orderPlaced.ReservationAutoExpiration.AddSeconds(1)); Assert.Equal(orderPlaced.Version, dto.OrderVersion); }
public void when_expired_then_deletes_priced_order() { this.sut.Handle(new OrderExpired { SourceId = orderId }); this.dto = this.dao.FindPricedOrder(orderId); Assert.Null(dto); }
public void When_expired_Then_deletes_priced_order() { this._sut.Handle(new OrderExpired { SourceId = _orderId }); this._dto = this._dao.FindPricedOrder(_orderId); Assert.IsNull(_dto); }
public void when_confirmed_then_removes_expiration() { this.sut.Handle(new OrderConfirmed { SourceId = orderId, Version = 15 }); this.dto = this.dao.FindPricedOrder(orderId); Assert.Null(dto.ReservationExpirationDate); Assert.Equal(15, dto.OrderVersion); }
public void Handle(OrderPlaced @event) { var dto = new PricedOrder { OrderId = @event.SourceId, ReservationExpirationDate = @event.ReservationAutoExpiration, OrderVersion = @event.Version }; pricedOrderRepository.Insert(dto); }
public void then_order_placed_is_idempotent() { this._sut.Handle(_orderPlaced); _dto = this._dao.FindPricedOrder(_orderId); Assert.NotNull(_dto); Assert.IsTrue(_dto.ReservationExpirationDate.Value >= _orderPlaced.ReservationAutoExpiration.AddSeconds(-1) && _dto.ReservationExpirationDate.Value <= _orderPlaced.ReservationAutoExpiration.AddSeconds(1)); Assert.AreEqual(_orderPlaced.Version, _dto.OrderVersion); }
public void When_confirmed_Then_removes_expiration() { this._sut.Handle(new OrderConfirmed { SourceId = _orderId, Version = 15 }); this._dto = this._dao.FindPricedOrder(_orderId); Assert.IsNull(_dto.ReservationExpirationDate); Assert.AreEqual(15, _dto.OrderVersion); }
private PaymentViewModel CompleteRegistrationWithThirdPartyProcessorPayment(PricedOrder order, int orderVersion) { var paymentCommand = CreatePaymentCommand(order); this._commandBus.Send(paymentCommand); return(new PaymentViewModel { ConferenceCode = this.ConferenceAlias.Code, PaymentId = paymentCommand.PaymentId }); }
public void Expiration_is_idempotent() { this._sut.Handle(new OrderExpired { SourceId = _orderId, Version = 15 }); this._sut.Handle(new OrderExpired { SourceId = _orderId, Version = 15 }); this._dto = this._dao.FindPricedOrder(_orderId); Assert.IsNull(_dto); }
public void expiration_is_idempotent() { this.sut.Handle(new OrderExpired { SourceId = orderId, Version = 15 }); this.sut.Handle(new OrderExpired { SourceId = orderId, Version = 15 }); this.dto = this.dao.FindPricedOrder(orderId); Assert.Null(dto); }
public void When_seat_assignments_created_Then_updates_order_with_assignments_id() { var assignmentsId = Guid.NewGuid(); this._sut.Handle(new SeatAssignmentsCreated { SourceId = assignmentsId, OrderId = _orderId, }); this._dto = this._dao.FindPricedOrder(_orderId); Assert.AreEqual(assignmentsId, _dto.AssignmentsId); Assert.AreEqual(4, _dto.OrderVersion); }
public void when_seat_assignments_created_then_updates_order_with_assignments_id() { var assignmentsId = Guid.NewGuid(); sut.Handle(new SeatAssignmentsCreated { SourceId = assignmentsId, OrderId = orderId }); dto = dao.FindPricedOrder(orderId); Assert.Equal(assignmentsId, dto.AssignmentsId); Assert.Equal(4, dto.OrderVersion); }
private InitiateThirdPartyProcessorPayment CreatePaymentCommand(PricedOrder order) { var description = "Registration for " + this.ConferenceAlias.Name; var totalAmount = order.Total; var paymentCommand = new InitiateThirdPartyProcessorPayment() { PaymentId = GuidUtil.NewSequentialId(), ConferenceId = this.ConferenceAlias.Id, PaymentSourceId = order.OrderId, Description = description, TotalAmount = totalAmount }; return(paymentCommand); }
public void when_display_valid_order_then_renders_view_with_priced_order() { // Arrange var orderId = Guid.NewGuid(); var dto = new PricedOrder { OrderId = orderId, Total = 200 }; orderDao.Setup(r => r.FindPricedOrder(orderId)).Returns(dto); // Act var result = (ViewResult)sut.Display(orderId); // Assert Assert.NotNull(result); Assert.Equal(dto, result.Model); }
public void Handle(OrderPlaced @event) { using (var context = contextFactory.Invoke()) { var dto = new PricedOrder { OrderId = @event.SourceId, ReservationExpirationDate = @event.ReservationAutoExpiration, OrderVersion = @event.Version }; context.Set <PricedOrder>().Add(dto); try { context.SaveChanges(); } catch (DbUpdateException) { Trace.TraceWarning( "Ignoring OrderPlaced message with version {1} for order id {0}. This could be caused because the message was already handled and the PricedOrder entity was already created.", dto.OrderId, @event.Version); } } }
private ActionResult CompleteRegistrationWithThirdPartyProcessorPayment(PricedOrder order, int orderVersion) { var paymentCommand = CreatePaymentCommand(order); commandBus.Send(paymentCommand); var paymentAcceptedUrl = Url.Action("ThankYou", new { conferenceCode = ConferenceAlias.Code, order.OrderId }); var paymentRejectedUrl = Url.Action("SpecifyRegistrantAndPaymentDetails", new { conferenceCode = ConferenceAlias.Code, orderId = order.OrderId, orderVersion }); return(RedirectToAction( "ThirdPartyProcessorPayment", "Payment", new { conferenceCode = ConferenceAlias.Code, paymentId = paymentCommand.PaymentId, paymentAcceptedUrl, paymentRejectedUrl })); }
public given_a_calculated_order() { sut.Handle(new OrderTotalsCalculated { SourceId = orderId, Lines = new[] { new SeatOrderLine { LineTotal = 50, SeatType = seatCreatedEvents[0].SourceId, Quantity = 10, UnitPrice = 5 } }, Total = 50, IsFreeOfCharge = true, Version = 4 }); dto = dao.FindPricedOrder(orderId); }
public void Handle(OrderExpired @event) { // No need to keep this priced order alive if it is expired. using (var context = contextFactory.Invoke()) { var pricedOrder = new PricedOrder { OrderId = @event.SourceId }; var set = context.Set <PricedOrder>(); set.Attach(pricedOrder); set.Remove(pricedOrder); try { context.SaveChanges(); } catch (DbUpdateConcurrencyException) { Trace.TraceWarning( "Ignoring priced order expiration message with version {1} for order id {0}. This could be caused because the message was already handled and the entity was already deleted.", pricedOrder.OrderId, @event.Version); } } }
public async Task Consume(ConsumeContext <OrderPlaced> consumeContext) { await using var repository = this.contextFactory.Invoke(); var dto = new PricedOrder { OrderId = consumeContext.Message.SourceId, ReservationExpirationDate = consumeContext.Message.ReservationAutoExpiration, OrderVersion = consumeContext.Message.Version }; repository.Set <PricedOrder>().Add(dto); try { await repository.SaveChangesAsync(); } catch (DbUpdateException) { Trace.TraceWarning( "Ignoring OrderPlaced message with version {1} for order id {0}. This could be caused because the message was already handled and the PricedOrder entity was already created.", dto.OrderId, consumeContext.Message.Version); } }
public async Task Consume(ConsumeContext <OrderExpired> consumeContext) { // No need to keep this priced order alive if it is expired. await using var repository = this.contextFactory.Invoke(); var pricedOrder = new PricedOrder { OrderId = consumeContext.Message.SourceId }; var set = repository.Set <PricedOrder>(); set.Attach(pricedOrder); set.Remove(pricedOrder); try { await repository.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { Trace.TraceWarning( "Ignoring priced order expiration message with version {1} for order id {0}. This could be caused because the message was already handled and the entity was already deleted.", pricedOrder.OrderId, consumeContext.Message.Version); } }
public when_reading_order() { this.dto = this.dao.FindPricedOrder(orderId); }
public when_reading_order() { dto = dao.FindPricedOrder(orderId); }
public void Handle(OrderTotalsCalculated @event) { var seatTypeIds = @event.Lines.OfType <SeatOrderLine>().Select(x => x.SeatType).Distinct().ToArray(); using (var context = this.contextFactory.Invoke()) { var dto = context.Query <PricedOrder>().Include(x => x.Lines).FirstOrDefault(x => x.OrderId == @event.SourceId); if (dto == null) { dto = new PricedOrder { OrderId = @event.SourceId }; context.Set <PricedOrder>().Add(dto); } else if (WasNotAlreadyHandled(dto, @event.Version)) { var linesSet = context.Set <PricedOrderLine>(); foreach (var line in dto.Lines.ToList()) { linesSet.Remove(line); } } else { // message already handled, skip. return; } List <PricedOrderLineSeatTypeDescription> seatTypeDescriptions; if (seatTypeIds.Length != 0) { seatTypeDescriptions = context.Query <PricedOrderLineSeatTypeDescription>() .Where(x => seatTypeIds.Contains(x.SeatTypeId)) .ToList(); } else { seatTypeDescriptions = new List <PricedOrderLineSeatTypeDescription>(); } foreach (var orderLine in @event.Lines) { var line = new PricedOrderLine { LineTotal = orderLine.LineTotal }; var seatOrderLine = orderLine as SeatOrderLine; if (seatOrderLine != null) { // should we update the view model to avoid losing the SeatTypeId? line.Description = seatTypeDescriptions.Where(x => x.SeatTypeId == seatOrderLine.SeatType).Select(x => x.Name).FirstOrDefault(); line.UnitPrice = seatOrderLine.UnitPrice; line.Quantity = seatOrderLine.Quantity; } dto.Lines.Add(line); } dto.Total = @event.Total; dto.IsFreeOfCharge = @event.IsFreeOfCharge; dto.OrderVersion = @event.Version; context.SaveChanges(); } }