|
|
|
@ -0,0 +1,108 @@
|
|
|
|
|
using Infrastructure.Attributes;
|
|
|
|
|
using Infrastructure.Filters;
|
|
|
|
|
using Infrastructure.Repository;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
|
|
|
using Microsoft.AspNetCore.Routing;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Moq;
|
|
|
|
|
|
|
|
|
|
namespace Infrastructure.Tests;
|
|
|
|
|
|
|
|
|
|
public class IdempotencyFilterTests
|
|
|
|
|
{
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task OnActionExecutionAsync_IdempotencyAttributeExists_RequestIsDuplicate()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var loggerMock = new Mock<ILogger<IdempotencyFilter>>();
|
|
|
|
|
var redisMock = new Mock<IRedisBasketRepository>();
|
|
|
|
|
redisMock.Setup(r => r.Exist(It.IsAny<string>())).ReturnsAsync(true);
|
|
|
|
|
|
|
|
|
|
var filter = new IdempotencyFilter(loggerMock.Object, redisMock.Object);
|
|
|
|
|
|
|
|
|
|
var actionContext = new ActionContext
|
|
|
|
|
{
|
|
|
|
|
HttpContext = new DefaultHttpContext(),
|
|
|
|
|
RouteData = new RouteData(),
|
|
|
|
|
ActionDescriptor = new ControllerActionDescriptor()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var actionExecutingContext = new ActionExecutingContext(
|
|
|
|
|
actionContext,
|
|
|
|
|
new List<IFilterMetadata>(),
|
|
|
|
|
new Dictionary<string, object> { { "id", "123" } }!,
|
|
|
|
|
new object());
|
|
|
|
|
|
|
|
|
|
((ControllerActionDescriptor)actionExecutingContext.ActionDescriptor).MethodInfo =
|
|
|
|
|
typeof(TestController).GetMethod(nameof(TestController.TestAction)) ?? throw new InvalidOperationException();
|
|
|
|
|
|
|
|
|
|
var next = new ActionExecutionDelegate(() =>
|
|
|
|
|
{
|
|
|
|
|
var resultContext = new ActionExecutedContext(actionContext, new List<IFilterMetadata>(), new object());
|
|
|
|
|
return Task.FromResult(resultContext);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
await filter.OnActionExecutionAsync(actionExecutingContext, next);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.IsType<ObjectResult>(actionExecutingContext.Result);
|
|
|
|
|
var objectResult = (ObjectResult)actionExecutingContext.Result;
|
|
|
|
|
Assert.Equal(200, objectResult.StatusCode);
|
|
|
|
|
Assert.Contains("Duplicate request", ((MessageData)objectResult.Value).Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task OnActionExecutionAsync_IdempotencyAttributeExists_RequestIsNotDuplicate()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var loggerMock = new Mock<ILogger<IdempotencyFilter>>();
|
|
|
|
|
var redisMock = new Mock<IRedisBasketRepository>();
|
|
|
|
|
redisMock.Setup(r => r.Exist(It.IsAny<string>())).ReturnsAsync(false);
|
|
|
|
|
|
|
|
|
|
var filter = new IdempotencyFilter(loggerMock.Object, redisMock.Object);
|
|
|
|
|
|
|
|
|
|
var actionContext = new ActionContext
|
|
|
|
|
{
|
|
|
|
|
HttpContext = new DefaultHttpContext(),
|
|
|
|
|
RouteData = new RouteData(),
|
|
|
|
|
ActionDescriptor = new ControllerActionDescriptor()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var actionExecutingContext = new ActionExecutingContext(
|
|
|
|
|
actionContext,
|
|
|
|
|
new List<IFilterMetadata>(),
|
|
|
|
|
new Dictionary<string, object> { { "id", "123" } }!,
|
|
|
|
|
new object());
|
|
|
|
|
|
|
|
|
|
((ControllerActionDescriptor)actionExecutingContext.ActionDescriptor).MethodInfo =
|
|
|
|
|
typeof(TestController).GetMethod(nameof(TestController.TestAction)) ?? throw new InvalidOperationException();
|
|
|
|
|
|
|
|
|
|
var nextCalled = false;
|
|
|
|
|
var next = new ActionExecutionDelegate(() =>
|
|
|
|
|
{
|
|
|
|
|
nextCalled = true;
|
|
|
|
|
var resultContext = new ActionExecutedContext(actionContext, new List<IFilterMetadata>(), new object());
|
|
|
|
|
return Task.FromResult(resultContext);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
await filter.OnActionExecutionAsync(actionExecutingContext, next);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.Null(actionExecutingContext.Result);
|
|
|
|
|
Assert.True(nextCalled);
|
|
|
|
|
redisMock.Verify(r => r.Set(It.IsAny<string>(), 0, TimeSpan.FromSeconds(60)), Times.Once);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TestController
|
|
|
|
|
{
|
|
|
|
|
[Idempotency("id", 60, "Duplicate request")]
|
|
|
|
|
public void TestAction()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|