Browse Source

IdentifiedCommand replaced with DuplicateCommandBehavior

pull/654/head
domenkogler 6 years ago
parent
commit
f60d161ce7
23 changed files with 126 additions and 228 deletions
  1. +37
    -0
      src/Services/Ordering/Ordering.API/Application/Behaviors/DuplicateCommandBehavior.cs
  2. +3
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs
  3. +4
    -7
      src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs
  4. +7
    -3
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
  5. +4
    -7
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
  6. +5
    -3
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs
  7. +2
    -3
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs
  8. +30
    -0
      src/Services/Ordering/Ordering.API/Application/Commands/DuplicateCommandResponse.cs
  9. +9
    -0
      src/Services/Ordering/Ordering.API/Application/Commands/ICommand.cs
  10. +0
    -17
      src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs
  11. +0
    -64
      src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs
  12. +3
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs
  13. +4
    -7
      src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs
  14. +3
    -3
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs
  15. +1
    -0
      src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs
  16. +1
    -0
      src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs
  17. +0
    -13
      src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs
  18. +1
    -0
      src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs
  19. +6
    -4
      src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs
  20. +1
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
  21. +0
    -91
      test/Services/UnitTest/Ordering/Application/IdentifiedCommandHandlerTest.cs
  22. +1
    -0
      test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
  23. +4
    -4
      test/Services/UnitTest/Ordering/Application/OrdersWebApiTest.cs

+ 37
- 0
src/Services/Ordering/Ordering.API/Application/Behaviors/DuplicateCommandBehavior.cs View File

@ -0,0 +1,37 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
namespace Ordering.API.Infrastructure.Behaviors
{
public class DuplicateCommandBehavior<TCommand, TResponse> : IPipelineBehavior<TCommand, TResponse>
where TCommand : ICommand
{
private readonly IMediator _mediator;
private readonly IRequestManager _requestManager;
public DuplicateCommandBehavior(IMediator mediator, IRequestManager requestManager)
{
_mediator = mediator;
_requestManager = requestManager;
}
async Task<TResponse> IPipelineBehavior<TCommand, TResponse>.Handle(TCommand command, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var alreadyExists = await _requestManager.ExistAsync(command.CommandId);
if (alreadyExists)
{
var duplicateCommand = new DuplicateCommandResponse<TCommand, TResponse>.DuplicateCommand<TCommand>(command);
return await _mediator.Send(duplicateCommand, cancellationToken);
}
await _requestManager.CreateRequestForCommandAsync<TCommand>(command.CommandId);
var response = await next();
return response;
}
}
}

+ 3
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs View File

@ -4,11 +4,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
namespace Ordering.API.Application.Commands namespace Ordering.API.Application.Commands
{ {
public class CancelOrderCommand : IRequest<bool>
public class CancelOrderCommand : ICommand, IRequest<bool>
{ {
public Guid CommandId { get; set; }
[DataMember] [DataMember]
public int OrderNumber { get; private set; } public int OrderNumber { get; private set; }


+ 4
- 7
src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs View File

@ -38,15 +38,12 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process // Use for Idempotency in Command process
public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool>
public class CancelOrderDuplicateCommand : DuplicateCommandResponse<CancelOrderCommand, bool>
{ {
public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
protected override Task<bool> CreateResponseForDuplicateCommand(CancelOrderCommand command)
{ {
}
protected override bool CreateResultForDuplicateRequest()
{
return true; // Ignore duplicate requests for processing order.
// Ignore duplicate requests for processing order.
return Task.FromResult(true);
} }
} }
} }

+ 7
- 3
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs View File

@ -18,9 +18,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
// https://msdn.microsoft.com/en-us/library/bb383979.aspx // https://msdn.microsoft.com/en-us/library/bb383979.aspx
[DataContract] [DataContract]
public class CreateOrderCommand
: IRequest<bool>
public class CreateOrderCommand : ICommand, IRequest<bool>
{ {
[DataMember]
public Guid CommandId { get; private set; }
[DataMember] [DataMember]
private readonly List<OrderItemDTO> _orderItems; private readonly List<OrderItemDTO> _orderItems;
@ -68,10 +71,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
_orderItems = new List<OrderItemDTO>(); _orderItems = new List<OrderItemDTO>();
} }
public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
public CreateOrderCommand(Guid id, List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
string cardNumber, string cardHolderName, DateTime cardExpiration, string cardNumber, string cardHolderName, DateTime cardExpiration,
string cardSecurityNumber, int cardTypeId) : this() string cardSecurityNumber, int cardTypeId) : this()
{ {
CommandId = id;
_orderItems = basketItems.ToOrderItemsDTO().ToList(); _orderItems = basketItems.ToOrderItemsDTO().ToList();
UserId = userId; UserId = userId;
UserName = userName; UserName = userName;


+ 4
- 7
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs View File

@ -47,15 +47,12 @@
// Use for Idempotency in Command process // Use for Idempotency in Command process
public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool>
public class CreateOrderDuplicateCommand : DuplicateCommandResponse<CreateOrderCommand, bool>
{ {
public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
protected override Task<bool> CreateResponseForDuplicateCommand(CreateOrderCommand command)
{ {
}
protected override bool CreateResultForDuplicateRequest()
{
return true; // Ignore duplicate requests for creating order.
// Ignore duplicate requests for creating order.
return Task.FromResult(true);
} }
} }
} }

+ 5
- 3
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs View File

@ -10,15 +10,17 @@ using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Comma
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{ {
public class CreateOrderDraftCommand : IRequest<OrderDraftDTO>
public class CreateOrderDraftCommand : ICommand, IRequest<OrderDraftDTO>
{ {
public Guid CommandId { get; }
public string BuyerId { get; private set; } public string BuyerId { get; private set; }
public IEnumerable<BasketItem> Items { get; private set; } public IEnumerable<BasketItem> Items { get; private set; }
public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> items)
public CreateOrderDraftCommand(Guid id, string buyerId, IEnumerable<BasketItem> items)
{ {
CommandId = id;
BuyerId = buyerId; BuyerId = buyerId;
Items = items; Items = items;
} }


+ 2
- 3
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs View File

@ -10,7 +10,6 @@
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
// Regular CommandHandler // Regular CommandHandler
public class CreateOrderDraftCommandHandler public class CreateOrderDraftCommandHandler
@ -44,14 +43,14 @@
public class OrderDraftDTO public class OrderDraftDTO
{ {
public IEnumerable<OrderItemDTO> OrderItems { get; set; }
public IEnumerable<CreateOrderCommand.OrderItemDTO> OrderItems { get; set; }
public decimal Total { get; set; } public decimal Total { get; set; }
public static OrderDraftDTO FromOrder(Order order) public static OrderDraftDTO FromOrder(Order order)
{ {
return new OrderDraftDTO() return new OrderDraftDTO()
{ {
OrderItems = order.OrderItems.Select(oi => new OrderItemDTO
OrderItems = order.OrderItems.Select(oi => new CreateOrderCommand.OrderItemDTO
{ {
Discount = oi.GetCurrentDiscount(), Discount = oi.GetCurrentDiscount(),
ProductId = oi.ProductId, ProductId = oi.ProductId,


+ 30
- 0
src/Services/Ordering/Ordering.API/Application/Commands/DuplicateCommandResponse.cs View File

@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
public abstract class DuplicateCommandResponse<TCommand, TResponse> : IRequestHandler<DuplicateCommandResponse<TCommand, TResponse>.DuplicateCommand<TCommand>, TResponse>
{
public class DuplicateCommand<TCommand> : IRequest<TResponse>
{
public DuplicateCommand(TCommand command)
{
Command = command;
}
public TCommand Command { get; }
}
/// <summary>
/// Creates the result value to return if a previous request was found
/// </summary>
/// <returns></returns>
protected abstract Task<TResponse> CreateResponseForDuplicateCommand(TCommand command);
Task<TResponse> IRequestHandler<DuplicateCommand<TCommand>, TResponse>.Handle(DuplicateCommand<TCommand> request, CancellationToken cancellationToken)
{
return CreateResponseForDuplicateCommand(request.Command);
}
}
}

+ 9
- 0
src/Services/Ordering/Ordering.API/Application/Commands/ICommand.cs View File

@ -0,0 +1,9 @@
using System;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
public interface ICommand
{
Guid CommandId { get; }
}
}

+ 0
- 17
src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs View File

@ -1,17 +0,0 @@
using MediatR;
using System;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
public class IdentifiedCommand<T, R> : IRequest<R>
where T : IRequest<R>
{
public T Command { get; }
public Guid Id { get; }
public IdentifiedCommand(T command, Guid id)
{
Command = command;
Id = id;
}
}
}

+ 0
- 64
src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs View File

@ -1,64 +0,0 @@
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
/// <summary>
/// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where
/// a requestid sent by client is used to detect duplicate requests.
/// </summary>
/// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam>
/// <typeparam name="R">Return value of the inner command handler</typeparam>
public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R>
where T : IRequest<R>
{
private readonly IMediator _mediator;
private readonly IRequestManager _requestManager;
public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager)
{
_mediator = mediator;
_requestManager = requestManager;
}
/// <summary>
/// Creates the result value to return if a previous request was found
/// </summary>
/// <returns></returns>
protected virtual R CreateResultForDuplicateRequest()
{
return default(R);
}
/// <summary>
/// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case
/// just enqueues the original inner command.
/// </summary>
/// <param name="message">IdentifiedCommand which contains both original command & request ID</param>
/// <returns>Return value of inner command or default value if request same ID was found</returns>
public async Task<R> Handle(IdentifiedCommand<T, R> message, CancellationToken cancellationToken)
{
var alreadyExists = await _requestManager.ExistAsync(message.Id);
if (alreadyExists)
{
return CreateResultForDuplicateRequest();
}
else
{
await _requestManager.CreateRequestForCommandAsync<T>(message.Id);
try
{
// Send the embeded business command to mediator so it runs its related CommandHandler
var result = await _mediator.Send(message.Command);
return result;
}
catch
{
return default(R);
}
}
}
}
}

+ 3
- 1
src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs View File

@ -4,11 +4,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
namespace Ordering.API.Application.Commands namespace Ordering.API.Application.Commands
{ {
public class ShipOrderCommand : IRequest<bool>
public class ShipOrderCommand : ICommand, IRequest<bool>
{ {
public Guid CommandId { get; set; }
[DataMember] [DataMember]
public int OrderNumber { get; private set; } public int OrderNumber { get; private set; }


+ 4
- 7
src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs View File

@ -38,15 +38,12 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process // Use for Idempotency in Command process
public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool>
public class ShipOrderDuplicateCommand : DuplicateCommandResponse<ShipOrderCommand, bool>
{ {
public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
protected override Task<bool> CreateResponseForDuplicateCommand(ShipOrderCommand command)
{ {
}
protected override bool CreateResultForDuplicateRequest()
{
return true; // Ignore duplicate requests for processing order.
// Ignore duplicate requests for creating order.
return Task.FromResult(true);
} }
} }
} }

+ 3
- 3
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs View File

@ -41,13 +41,13 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
if (eventMsg.RequestId != Guid.Empty) if (eventMsg.RequestId != Guid.Empty)
{ {
var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street,
var createOrderCommand = new CreateOrderCommand(eventMsg.RequestId, eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street,
eventMsg.State, eventMsg.Country, eventMsg.ZipCode, eventMsg.State, eventMsg.Country, eventMsg.ZipCode,
eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration, eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration,
eventMsg.CardSecurityNumber, eventMsg.CardTypeId); eventMsg.CardSecurityNumber, eventMsg.CardTypeId);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
result = await _mediator.Send(requestCreateOrder);
//var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
result = await _mediator.Send(createOrderCommand);
} }
_logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler)) _logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler))


+ 1
- 0
src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs View File

@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
{ {
public CancelOrderCommandValidator() public CancelOrderCommandValidator()
{ {
RuleFor(command => command.CommandId).NotEmpty();
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
} }
} }


+ 1
- 0
src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs View File

@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
{ {
public CreateOrderCommandValidator() public CreateOrderCommandValidator()
{ {
RuleFor(command => command.CommandId).NotEmpty();
RuleFor(command => command.City).NotEmpty(); RuleFor(command => command.City).NotEmpty();
RuleFor(command => command.Street).NotEmpty(); RuleFor(command => command.Street).NotEmpty();
RuleFor(command => command.State).NotEmpty(); RuleFor(command => command.State).NotEmpty();


+ 0
- 13
src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs View File

@ -1,13 +0,0 @@
using FluentValidation;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
namespace Ordering.API.Application.Validations
{
public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>>
{
public IdentifiedCommandValidator()
{
RuleFor(command => command.Id).NotEmpty();
}
}
}

+ 1
- 0
src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs View File

@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
{ {
public ShipOrderCommandValidator() public ShipOrderCommandValidator()
{ {
RuleFor(command => command.CommandId).NotEmpty();
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
} }
} }


+ 6
- 4
src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs View File

@ -38,8 +38,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
bool commandResult = false; bool commandResult = false;
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
{ {
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
commandResult = await _mediator.Send(requestCancelOrder);
command.CommandId = guid;
//var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
commandResult = await _mediator.Send(command);
} }
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest(); return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
@ -55,8 +56,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
bool commandResult = false; bool commandResult = false;
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
{ {
var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
commandResult = await _mediator.Send(requestShipOrder);
command.CommandId = guid;
//var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
commandResult = await _mediator.Send(command);
} }
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest(); return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();


+ 1
- 0
src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs View File

@ -42,6 +42,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(DuplicateCommandBehavior<,>)).As(typeof(IPipelineBehavior<,>));
} }
} }


+ 0
- 91
test/Services/UnitTest/Ordering/Application/IdentifiedCommandHandlerTest.cs View File

@ -1,91 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UnitTest.Ordering.Application
{
using global::Ordering.API.Application.Models;
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Moq;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
public class IdentifiedCommandHandlerTest
{
private readonly Mock<IRequestManager> _requestManager;
private readonly Mock<IMediator> _mediator;
public IdentifiedCommandHandlerTest()
{
_requestManager = new Mock<IRequestManager>();
_mediator = new Mock<IMediator>();
}
[Fact]
public async Task Handler_sends_command_when_order_no_exists()
{
// Arrange
var fakeGuid = Guid.NewGuid();
var fakeOrderCmd = new IdentifiedCommand<CreateOrderCommand, bool>(FakeOrderRequest(), fakeGuid);
_requestManager.Setup(x => x.ExistAsync(It.IsAny<Guid>()))
.Returns(Task.FromResult(false));
_mediator.Setup(x => x.Send(It.IsAny<IRequest<bool>>(),default(System.Threading.CancellationToken)))
.Returns(Task.FromResult(true));
//Act
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
var cltToken = new System.Threading.CancellationToken();
var result = await handler.Handle(fakeOrderCmd, cltToken);
//Assert
Assert.True(result);
_mediator.Verify(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)), Times.Once());
}
[Fact]
public async Task Handler_sends_no_command_when_order_already_exists()
{
// Arrange
var fakeGuid = Guid.NewGuid();
var fakeOrderCmd = new IdentifiedCommand<CreateOrderCommand, bool>(FakeOrderRequest(), fakeGuid);
_requestManager.Setup(x => x.ExistAsync(It.IsAny<Guid>()))
.Returns(Task.FromResult(true));
_mediator.Setup(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)))
.Returns(Task.FromResult(true));
//Act
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
var cltToken = new System.Threading.CancellationToken();
var result = await handler.Handle(fakeOrderCmd, cltToken);
//Assert
Assert.False(result);
_mediator.Verify(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)), Times.Never());
}
private CreateOrderCommand FakeOrderRequest(Dictionary<string, object> args = null)
{
return new CreateOrderCommand(
new List<BasketItem>(),
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null,
city: args != null && args.ContainsKey("city") ? (string)args["city"] : null,
street: args != null && args.ContainsKey("street") ? (string)args["street"] : null,
state: args != null && args.ContainsKey("state") ? (string)args["state"] : null,
country: args != null && args.ContainsKey("country") ? (string)args["country"] : null,
zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null,
cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234",
cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue,
cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123",
cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX",
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0);
}
}
}

+ 1
- 0
test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs View File

@ -76,6 +76,7 @@ namespace UnitTest.Ordering.Application
private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null) private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
{ {
return new CreateOrderCommand( return new CreateOrderCommand(
Guid.NewGuid(),
new List<BasketItem>(), new List<BasketItem>(),
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null, userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null, userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null,


+ 4
- 4
test/Services/UnitTest/Ordering/Application/OrdersWebApiTest.cs View File

@ -31,7 +31,7 @@ namespace UnitTest.Ordering.Application
public async Task Create_order_with_requestId_success() public async Task Create_order_with_requestId_success()
{ {
//Arrange //Arrange
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
_mediatorMock.Setup(x => x.Send(It.IsAny<CancelOrderCommand>(), default(CancellationToken)))
.Returns(Task.FromResult(true)); .Returns(Task.FromResult(true));
//Act //Act
@ -47,7 +47,7 @@ namespace UnitTest.Ordering.Application
public async Task Cancel_order_bad_request() public async Task Cancel_order_bad_request()
{ {
//Arrange //Arrange
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
_mediatorMock.Setup(x => x.Send(It.IsAny<CancelOrderCommand>(), default(CancellationToken)))
.Returns(Task.FromResult(true)); .Returns(Task.FromResult(true));
//Act //Act
@ -62,7 +62,7 @@ namespace UnitTest.Ordering.Application
public async Task Ship_order_with_requestId_success() public async Task Ship_order_with_requestId_success()
{ {
//Arrange //Arrange
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<ShipOrderCommand, bool>>(), default(System.Threading.CancellationToken)))
_mediatorMock.Setup(x => x.Send(It.IsAny<ShipOrderCommand>(), default(System.Threading.CancellationToken)))
.Returns(Task.FromResult(true)); .Returns(Task.FromResult(true));
//Act //Act
@ -78,7 +78,7 @@ namespace UnitTest.Ordering.Application
public async Task Ship_order_bad_request() public async Task Ship_order_bad_request()
{ {
//Arrange //Arrange
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CreateOrderCommand, bool>>(), default(System.Threading.CancellationToken)))
_mediatorMock.Setup(x => x.Send(It.IsAny<CreateOrderCommand>(), default(System.Threading.CancellationToken)))
.Returns(Task.FromResult(true)); .Returns(Task.FromResult(true));
//Act //Act


Loading…
Cancel
Save