From be9552b1f07604068c6bd6b2b8db488d9020eeaa Mon Sep 17 00:00:00 2001 From: Young Date: Tue, 8 Oct 2024 16:41:35 +0800 Subject: [PATCH] added idempotency tests --- .../IdempotencyFilterTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/Infrastructure.Tests/IdempotencyFilterTests.cs diff --git a/src/Infrastructure.Tests/IdempotencyFilterTests.cs b/src/Infrastructure.Tests/IdempotencyFilterTests.cs new file mode 100644 index 0000000..b5f73d6 --- /dev/null +++ b/src/Infrastructure.Tests/IdempotencyFilterTests.cs @@ -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>(); + var redisMock = new Mock(); + redisMock.Setup(r => r.Exist(It.IsAny())).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(), + new Dictionary { { "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(), new object()); + return Task.FromResult(resultContext); + }); + + // Act + await filter.OnActionExecutionAsync(actionExecutingContext, next); + + // Assert + Assert.IsType(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>(); + var redisMock = new Mock(); + redisMock.Setup(r => r.Exist(It.IsAny())).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(), + new Dictionary { { "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(), 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(), 0, TimeSpan.FromSeconds(60)), Times.Once); + } +} + +public class TestController +{ + [Idempotency("id", 60, "Duplicate request")] + public void TestAction() + { + } +} \ No newline at end of file