From a9216c2dd458dfd5c37d8e9fea09dfcc544a76b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCsters?= Date: Thu, 25 Jul 2024 17:22:15 +0200 Subject: [PATCH 1/5] feat: added retry logic to HttpRemoteCommandRunners --- .../HttpRemoteCommandRunnerTests.cs | 145 ++++++++++++++++++ .../Commands/Greeting/GreetingCommand.cs | 13 ++ .../Greeting/GreetingCommandHandler.cs | 11 ++ .../HttpRemoteCommandRunner`1.cs | 17 +- .../HttpRemoteCommandRunner`2.cs | 16 +- 5 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/RemoteCommandRunners/HttpRemoteCommandRunnerTests.cs create mode 100644 src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommand.cs create mode 100644 src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommandHandler.cs diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/RemoteCommandRunners/HttpRemoteCommandRunnerTests.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/RemoteCommandRunners/HttpRemoteCommandRunnerTests.cs new file mode 100644 index 0000000..27996a8 --- /dev/null +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/RemoteCommandRunners/HttpRemoteCommandRunnerTests.cs @@ -0,0 +1,145 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using Moq; +using RestSharp; +using Wemogy.CQRS.Common.ValueObjects; +using Wemogy.CQRS.Extensions.FastEndpoints.Common; +using Wemogy.CQRS.Extensions.FastEndpoints.RemoteCommandRunners; +using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Commands.Greeting; +using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Commands.LogTestContext; + +namespace Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.RemoteCommandRunners; + +public class HttpRemoteCommandRunnerTests +{ + [Fact] + public async Task RunAsync_ShouldRetryRequest() + { + // Arrange + var command = new LogTestContextCommand(); + var responses = new Queue(); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.OK, + + // Set IsSuccessful to true + IsSuccessStatusCode = true, + ResponseStatus = ResponseStatus.Completed + }); + + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(responses.Dequeue); + + var httpRemoteCommandRunner = new HttpRemoteCommandRunner(restClientMock.Object, string.Empty); + var request = new CommandRequest(command, new List()); + + // Act + var exception = await Record.ExceptionAsync(() => httpRemoteCommandRunner.RunAsync(request)); + + // Assert + exception.Should().BeNull(); + responses.Should().BeEmpty(); + } + + [Fact] + public async Task RunAsync_ShouldThrowAfterMaxRetryReachedRequest() + { + // Arrange + var command = new LogTestContextCommand(); + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(() => new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + + var httpRemoteCommandRunner = new HttpRemoteCommandRunner(restClientMock.Object, string.Empty); + var request = new CommandRequest(command, new List()); + + // Act + var exception = await Record.ExceptionAsync(() => httpRemoteCommandRunner.RunAsync(request)); + + // Assert + exception.Should().NotBeNull(); + } + + [Fact] + public async Task RunAsync_ShouldRetryRequestAndReturnResult() + { + // Arrange + var command = new GreetingCommand("Max"); + var resultContent = JsonSerializer.Serialize("Hello, Max!", JsonOptions.JsonSerializerOptions); + var responses = new Queue(); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.OK, + Content = resultContent, + + // Set IsSuccessful to true + IsSuccessStatusCode = true, + ResponseStatus = ResponseStatus.Completed + }); + + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(responses.Dequeue); + + var httpRemoteCommandRunner = new HttpRemoteCommandRunner(restClientMock.Object, string.Empty); + var request = new CommandRequest(command, new List()); + + // Act + var result = await httpRemoteCommandRunner.RunAsync(request); + + // Assert + result.Should().Be("Hello, Max!"); + responses.Should().BeEmpty(); + } + + [Fact] + public async Task RunAsync_ShouldThrowWithoutResultAfterMaxRetryReachedRequest() + { + // Arrange + var command = new GreetingCommand("Max"); + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(() => new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + + var httpRemoteCommandRunner = new HttpRemoteCommandRunner(restClientMock.Object, string.Empty); + var request = new CommandRequest(command, new List()); + + // Act + var exception = await Record.ExceptionAsync(() => httpRemoteCommandRunner.RunAsync(request)); + + // Assert + exception.Should().NotBeNull(); + } +} diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommand.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommand.cs new file mode 100644 index 0000000..f4c5782 --- /dev/null +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommand.cs @@ -0,0 +1,13 @@ +using Wemogy.CQRS.Commands.Abstractions; + +namespace Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Commands.Greeting; + +public class GreetingCommand : ICommand +{ + public string Name { get; } + + public GreetingCommand(string name) + { + Name = name; + } +} diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommandHandler.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommandHandler.cs new file mode 100644 index 0000000..9bc1094 --- /dev/null +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/TestApplication/Commands/Greeting/GreetingCommandHandler.cs @@ -0,0 +1,11 @@ +using Wemogy.CQRS.Commands.Abstractions; + +namespace Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Commands.Greeting; + +public class GreetingCommandHandler : ICommandHandler +{ + public Task HandleAsync(GreetingCommand command) + { + return Task.FromResult($"Hello, {command.Name}!"); + } +} diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`1.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`1.cs index 2b07771..96bf941 100644 --- a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`1.cs +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`1.cs @@ -1,3 +1,6 @@ +using Polly; +using Polly.Contrib.WaitAndRetry; +using Polly.Retry; using RestSharp; using Wemogy.Core.Errors; using Wemogy.Core.Extensions; @@ -12,17 +15,25 @@ namespace Wemogy.CQRS.Extensions.FastEndpoints.RemoteCommandRunners; public class HttpRemoteCommandRunner : IRemoteCommandRunner where TCommand : ICommandBase { - private readonly RestClient _restClient; + private readonly IRestClient _restClient; /// /// This is the sub-path of the client base path /// private readonly string _urlPath; + private readonly IAsyncPolicy _retryPolicy; - public HttpRemoteCommandRunner(RestClient restClient, string urlPath) + public HttpRemoteCommandRunner(IRestClient restClient, string urlPath) { _restClient = restClient; _urlPath = urlPath; + var retryCount = 3; + var delay = Backoff.ExponentialBackoff( + TimeSpan.FromMilliseconds(100), + retryCount); + _retryPolicy = Policy + .HandleResult(x => !x.IsSuccessful) + .WaitAndRetryAsync(delay); } public async Task RunAsync(CommandRequest command) @@ -33,7 +44,7 @@ public async Task RunAsync(CommandRequest command) try { - var response = await _restClient.ExecutePostAsync(request); + var response = await _retryPolicy.ExecuteAsync(() => _restClient.ExecutePostAsync(request)); if (!response.IsSuccessful) { diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`2.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`2.cs index 960288d..bb55de4 100644 --- a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`2.cs +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteCommandRunners/HttpRemoteCommandRunner`2.cs @@ -1,4 +1,6 @@ using System.Text.Json; +using Polly; +using Polly.Contrib.WaitAndRetry; using RestSharp; using Wemogy.Core.Errors; using Wemogy.Core.Extensions; @@ -14,17 +16,25 @@ namespace Wemogy.CQRS.Extensions.FastEndpoints.RemoteCommandRunners; public class HttpRemoteCommandRunner : IRemoteCommandRunner where TCommand : ICommand { - private readonly RestClient _restClient; + private readonly IRestClient _restClient; /// /// This is the sub-path of the client base path /// private readonly string _urlPath; + private readonly IAsyncPolicy _retryPolicy; - public HttpRemoteCommandRunner(RestClient restClient, string urlPath) + public HttpRemoteCommandRunner(IRestClient restClient, string urlPath) { _restClient = restClient; _urlPath = urlPath; + var retryCount = 3; + var delay = Backoff.ExponentialBackoff( + TimeSpan.FromMilliseconds(100), + retryCount); + _retryPolicy = Policy + .HandleResult(x => !x.IsSuccessful) + .WaitAndRetryAsync(delay); } public async Task RunAsync(CommandRequest command) @@ -33,7 +43,7 @@ public async Task RunAsync(CommandRequest command) var request = new RestRequest(_urlPath) .AddJsonBody(command); - var response = await _restClient.PostAsync(request); + var response = await _retryPolicy.ExecuteAsync(() => _restClient.PostAsync(request)); if (!response.IsSuccessful) { From 433a33dbefd7d65d125ee00a320407e658af97aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCsters?= Date: Thu, 25 Jul 2024 17:30:57 +0200 Subject: [PATCH 2/5] feat: added retry logic to HttpRemoteQueryRunner --- .../HttpRemoteQueryRunnerTests.cs | 89 +++++++++++++++++++ .../HttpRemoteQueryRunner.cs | 18 +++- 2 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/HttpRemoteQueryRunners/HttpRemoteQueryRunnerTests.cs diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/HttpRemoteQueryRunners/HttpRemoteQueryRunnerTests.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/HttpRemoteQueryRunners/HttpRemoteQueryRunnerTests.cs new file mode 100644 index 0000000..050c1fa --- /dev/null +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints.UnitTests/HttpRemoteQueryRunners/HttpRemoteQueryRunnerTests.cs @@ -0,0 +1,89 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using Moq; +using RestSharp; +using Wemogy.CQRS.Common.ValueObjects; +using Wemogy.CQRS.Extensions.FastEndpoints.Common; +using Wemogy.CQRS.Extensions.FastEndpoints.RemoteQueryRunners; +using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Queries.RequestTestContext; +using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.ValueObjects; + +namespace Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.HttpRemoteQueryRunners; + +public class HttpRemoteQueryRunnerTests +{ + [Fact] + public async Task QueryAsync_ShouldRetryRequestAndReturnResult() + { + // Arrange + var query = new RequestTestContextQuery(); + var testContext = new TestContext() + { + UserId = Guid.NewGuid().ToString() + }; + var resultContent = JsonSerializer.Serialize(testContext, JsonOptions.JsonSerializerOptions); + var responses = new Queue(); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + responses.Enqueue(new RestResponse() + { + StatusCode = HttpStatusCode.OK, + Content = resultContent, + + // Set IsSuccessful to true + IsSuccessStatusCode = true, + ResponseStatus = ResponseStatus.Completed + }); + + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(responses.Dequeue); + + var httpRemoteQueryRunner = new HttpRemoteQueryRunner(restClientMock.Object, string.Empty); + var request = new QueryRequest(query, new List()); + + // Act + var result = await httpRemoteQueryRunner.QueryAsync(request, CancellationToken.None); + + // Assert + result.Should().BeEquivalentTo(testContext); + responses.Should().BeEmpty(); + } + + [Fact] + public async Task QueryAsync_ShouldThrowWithoutResultAfterMaxRetryReachedRequest() + { + // Arrange + var query = new RequestTestContextQuery(); + var testContext = new TestContext() + { + UserId = Guid.NewGuid().ToString() + }; + var restClientMock = new Mock(); + restClientMock + .Setup( + m => m.ExecuteAsync( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(() => new RestResponse() + { + StatusCode = HttpStatusCode.ServiceUnavailable + }); + + var httpRemoteQueryRunner = new HttpRemoteQueryRunner(restClientMock.Object, string.Empty); + var request = new QueryRequest(query, new List()); + + // Act + var exception = await Record.ExceptionAsync(() => httpRemoteQueryRunner.QueryAsync(request, CancellationToken.None)); + + // Assert + exception.Should().NotBeNull(); + } +} diff --git a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteQueryRunners/HttpRemoteQueryRunner.cs b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteQueryRunners/HttpRemoteQueryRunner.cs index 12e8d06..d72a896 100644 --- a/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteQueryRunners/HttpRemoteQueryRunner.cs +++ b/src/extensions/fastEndpoints/Wemogy.CQRS.Extensions.FastEndpoints/RemoteQueryRunners/HttpRemoteQueryRunner.cs @@ -1,4 +1,6 @@ using System.Text.Json; +using Polly; +using Polly.Contrib.WaitAndRetry; using RestSharp; using Wemogy.Core.Errors; using Wemogy.Core.Extensions; @@ -14,17 +16,25 @@ namespace Wemogy.CQRS.Extensions.FastEndpoints.RemoteQueryRunners; public class HttpRemoteQueryRunner : IRemoteQueryRunner where TQuery : IQuery { - private readonly RestClient _restClient; + private readonly IRestClient _restClient; /// /// This is the sub-path of the client base path /// private readonly string _urlPath; + private readonly IAsyncPolicy _retryPolicy; - public HttpRemoteQueryRunner(RestClient restClient, string urlPath) + public HttpRemoteQueryRunner(IRestClient restClient, string urlPath) { _restClient = restClient; _urlPath = urlPath; + var retryCount = 3; + var delay = Backoff.ExponentialBackoff( + TimeSpan.FromMilliseconds(100), + retryCount); + _retryPolicy = Policy + .HandleResult(x => !x.IsSuccessful) + .WaitAndRetryAsync(delay); } public async Task QueryAsync(QueryRequest query, CancellationToken cancellationToken) @@ -32,7 +42,9 @@ public async Task QueryAsync(QueryRequest query, CancellationTo var request = new RestRequest(_urlPath) .AddJsonBody(query); - var response = await _restClient.PostAsync(request, cancellationToken: cancellationToken); + var response = await _retryPolicy.ExecuteAsync( + ct => _restClient.PostAsync(request, cancellationToken: ct), + cancellationToken); if (!response.IsSuccessful) { From dffa944c244e61856305f2859103e1969383ba0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCsters?= Date: Thu, 25 Jul 2024 17:42:43 +0200 Subject: [PATCH 3/5] fix: reset call count for unit testing --- src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs | 1 + .../Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs b/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs index 97c0aec..6ebb5b1 100644 --- a/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs +++ b/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs @@ -26,6 +26,7 @@ public async Task CallingAddCQRSMultipleTimesInDifferentAssembliesShouldWork() var helloAssemblyACommand = new PrintHelloAssemblyACommand(); var helloAssemblyBCommand = new PrintHelloAssemblyBCommand(); var trackUserLoginCommand = new TrackUserLoginCommand("test-user-id"); + TrackUserLoginCommandHandler.ResetCallCount(); // Act var trackUserLoginCommandException = await Record.ExceptionAsync(() => commands.RunAsync(trackUserLoginCommand)); diff --git a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs index 0bebf31..8b36f34 100644 --- a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs +++ b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs @@ -12,4 +12,9 @@ public Task HandleAsync(TrackUserLoginCommand command) CallCount++; return Task.CompletedTask; } + + public static void ResetCallCount() + { + CallCount = 0; + } } From ff19007c3a794cacf30a0c2cc71382eb2d32df08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCsters?= Date: Thu, 25 Jul 2024 17:43:27 +0200 Subject: [PATCH 4/5] fix: fixed member sorting order --- .../TrackUserLogin/TrackUserLoginCommandHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs index 8b36f34..804696c 100644 --- a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs +++ b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs @@ -7,14 +7,14 @@ public class TrackUserLoginCommandHandler : ICommandHandler Date: Thu, 25 Jul 2024 17:54:37 +0200 Subject: [PATCH 5/5] fix: improved TrackUserLoginCommandHandler test --- .../DependencyInjectionTests.cs | 6 +++--- .../TrackUserLoginCommandHandler.cs | 18 +++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs b/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs index 6ebb5b1..cb46544 100644 --- a/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs +++ b/src/core/Wemogy.CQRS.UnitTests/DependencyInjectionTests.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; @@ -25,8 +26,7 @@ public async Task CallingAddCQRSMultipleTimesInDifferentAssembliesShouldWork() var commands = serviceProvider.GetRequiredService(); var helloAssemblyACommand = new PrintHelloAssemblyACommand(); var helloAssemblyBCommand = new PrintHelloAssemblyBCommand(); - var trackUserLoginCommand = new TrackUserLoginCommand("test-user-id"); - TrackUserLoginCommandHandler.ResetCallCount(); + var trackUserLoginCommand = new TrackUserLoginCommand(Guid.NewGuid().ToString()); // Act var trackUserLoginCommandException = await Record.ExceptionAsync(() => commands.RunAsync(trackUserLoginCommand)); @@ -34,7 +34,7 @@ public async Task CallingAddCQRSMultipleTimesInDifferentAssembliesShouldWork() var helloAssemblyBCommandException = await Record.ExceptionAsync(() => commands.RunAsync(helloAssemblyBCommand)); // Assert - TrackUserLoginCommandHandler.CallCount.Should().Be(1); + TrackUserLoginCommandHandler.ExecutedCount[trackUserLoginCommand.UserId].Should().Be(1); trackUserLoginCommandException.Should().BeNull(); PrintHelloAssemblyACommandHandler.CallCount.Should().Be(1); helloAssemblyACommandException.Should().BeNull(); diff --git a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs index 804696c..c91f281 100644 --- a/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs +++ b/src/core/Wemogy.CQRS.UnitTests/TestApplication/Commands/TrackUserLogin/TrackUserLoginCommandHandler.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Wemogy.CQRS.Commands.Abstractions; @@ -5,16 +6,19 @@ namespace Wemogy.CQRS.UnitTests.TestApplication.Commands.TrackUserLogin; public class TrackUserLoginCommandHandler : ICommandHandler { - public static int CallCount { get; private set; } - - public static void ResetCallCount() - { - CallCount = 0; - } + public static Dictionary ExecutedCount { get; } = new (); public Task HandleAsync(TrackUserLoginCommand command) { - CallCount++; + if (ExecutedCount.TryGetValue(command.UserId, out var count)) + { + ExecutedCount[command.UserId] = count + 1; + } + else + { + ExecutedCount[command.UserId] = 1; + } + return Task.CompletedTask; } }