diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/DomainEventHandlerResolverTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/DomainEventHandlerResolverTests.cs index fb1bfdc6..897b3034 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/DomainEventHandlerResolverTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/DomainEventHandlerResolverTests.cs @@ -45,7 +45,7 @@ public void DomainEventHandlerResolver_GetDomainEventHandlers_TransactionHasBeen String handlerTypeName = "TransactionProcessor.BusinessLogic.EventHandling.TransactionDomainEventHandler, TransactionProcessor.BusinessLogic"; Dictionary eventHandlerConfiguration = new Dictionary(); - TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.TransactionHasBeenCompletedEvent; + TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.DomainEvents.TransactionHasBeenCompletedEvent; eventHandlerConfiguration.Add(transactionHasBeenCompletedEvent.GetType().Name, new String[] { handlerTypeName }); @@ -67,7 +67,7 @@ public void DomainEventHandlerResolver_GetDomainEventHandlers_TransactionHasBeen String handlerTypeName = "TransactionProcessor.BusinessLogic.EventHandling.TransactionDomainEventHandler, TransactionProcessor.BusinessLogic"; Dictionary eventHandlerConfiguration = new Dictionary(); - TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.TransactionHasBeenCompletedEvent; + TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.DomainEvents.TransactionHasBeenCompletedEvent; eventHandlerConfiguration.Add("RandomEvent", new String[] { handlerTypeName }); Mock domainEventHandler = new Mock(); @@ -85,7 +85,7 @@ public void DomainEventHandlerResolver_GetDomainEventHandlers_TransactionHasBeen { Dictionary eventHandlerConfiguration = new Dictionary(); - TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.TransactionHasBeenCompletedEvent; + TransactionDomainEvents.TransactionHasBeenCompletedEvent transactionHasBeenCompletedEvent = TestData.DomainEvents.TransactionHasBeenCompletedEvent; Mock domainEventHandler = new Mock(); Func createDomainEventHandlerFunc = (type) => { return domainEventHandler.Object; }; diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantDomainEventHandlerTests.cs index 577c6b58..a65b49cc 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantDomainEventHandlerTests.cs @@ -151,7 +151,7 @@ public void MerchantDomainEventHandler_SettlementGeneratedEvent_EventIsHandled() [Fact] public void MerchantDomainEventHandler_TransactionHasBeenCompletedEvent_EventIsHandled() { - TransactionDomainEvents.TransactionHasBeenCompletedEvent domainEvent = TestData.TransactionHasBeenCompletedEvent; + TransactionDomainEvents.TransactionHasBeenCompletedEvent domainEvent = TestData.DomainEvents.TransactionHasBeenCompletedEvent; Should.NotThrow(async () => { await this.DomainEventHandler.Handle(domainEvent, CancellationToken.None); }); } diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs index d3a4f8ea..646a3eb7 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs @@ -35,24 +35,22 @@ public MerchantSettlementDomainEventHandlerTests() [Fact] public async Task MerchantSettlementDomainEventHandler_Handle_MerchantFeeSettledEvent_EventIsHandled() { - MerchantFeeSettledEvent domainEvent = TestData.DomainEvents.MerchantFeeSettledEvent; - this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) .ReturnsAsync(Result.Success()); - Result result = await this.EventHandler.Handle(domainEvent, CancellationToken.None); + Result result = await this.EventHandler.Handle(TestData.DomainEvents.MerchantFeeSettledEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); } [Fact] public async Task MerchantSettlementDomainEventHandler_Handle_MerchantFeeSettledEvent_Retry_EventIsHandled() { - MerchantFeeSettledEvent domainEvent = TestData.DomainEvents.MerchantFeeSettledEvent; - this.Mediator.SetupSequence(m => m.Send(It.IsAny(), It.IsAny())) + this.Mediator.SetupSequence(m => m.Send(It.IsAny>(), It.IsAny())) .ThrowsAsync(new WrongExpectedVersionException("Stream1", StreamRevision.None, StreamRevision.None)) .ThrowsAsync(new RpcException(new Status(StatusCode.DeadlineExceeded, "Deadline Exceeded"))) .ReturnsAsync(Result.Success()); - Result result = await this.EventHandler.Handle(domainEvent, CancellationToken.None); + Result result = await this.EventHandler.Handle(TestData.DomainEvents.MerchantFeeSettledEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); } } diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs index b5807a89..2ed29e7d 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs @@ -1,5 +1,7 @@ using System.Threading; using System.Threading.Tasks; +using EventStore.Client; +using Grpc.Core; using MediatR; using Moq; using Shared.Logger; @@ -15,33 +17,55 @@ public class MerchantStatementDomainEventHandlerTests { private Mock Mediator; - private MerchantStatementDomainEventHandler DomainEventHandler; + private MerchantStatementDomainEventHandler EventHandler; public MerchantStatementDomainEventHandlerTests() { Logger.Initialise(NullLogger.Instance); this.Mediator = new Mock(); - this.DomainEventHandler = new MerchantStatementDomainEventHandler(this.Mediator.Object); + this.EventHandler = new MerchantStatementDomainEventHandler(this.Mediator.Object); } [Fact] public async Task MerchantStatementDomainEventHandler_Handle_StatementGeneratedEvent_EventIsHandled() { - this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())).ReturnsAsync(Result.Success()); - Should.NotThrow(async () => - { - await this.DomainEventHandler.Handle(TestData.DomainEvents.StatementGeneratedEvent, CancellationToken.None); - }); - this.Mediator.Verify(m=> m.Send(It.IsAny>(), It.IsAny()), Times.Once); + this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.StatementGeneratedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } + + [Fact] + public async Task MerchantStatementDomainEventHandler_Handle_StatementGeneratedEvent_Retry_EventIsHandled() + { + this.Mediator.SetupSequence(m => m.Send(It.IsAny>(), It.IsAny())) + .ThrowsAsync(new WrongExpectedVersionException("Stream1", StreamRevision.None, StreamRevision.None)) + .ThrowsAsync(new RpcException(new Status(StatusCode.DeadlineExceeded, "Deadline Exceeded"))) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.StatementGeneratedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); } [Fact] public async Task MerchantStatementDomainEventHandler_Handle_TransactionHasBeenCompletedEvent_EventIsHandled() { - this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())).ReturnsAsync(Result.Success()); - Should.NotThrow(async () => - { - await this.DomainEventHandler.Handle(TestData.TransactionHasBeenCompletedEvent, CancellationToken.None); - }); - this.Mediator.Verify(m => m.Send(It.IsAny>(), It.IsAny()), Times.Once); + this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.TransactionHasBeenCompletedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } + + [Fact] + public async Task MerchantStatementDomainEventHandler_Handle_TransactionHasBeenCompletedEvent_Retry_EventIsHandled() + { + this.Mediator.SetupSequence(m => m.Send(It.IsAny>(), It.IsAny())) + .ThrowsAsync(new WrongExpectedVersionException("Stream1", StreamRevision.None, StreamRevision.None)) + .ThrowsAsync(new RpcException(new Status(StatusCode.DeadlineExceeded, "Deadline Exceeded"))) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.TransactionHasBeenCompletedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs index c7b223c0..500e7377 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs @@ -49,7 +49,7 @@ public async Task TransactionDomainEventHandler_EventPassedIn_EventIsHandled(Typ DomainEvent domainEvent = eventType.Name switch { nameof(FloatDomainEvents.FloatCreditPurchasedEvent) => new FloatDomainEvents.FloatCreditPurchasedEvent(TestData.FloatAggregateId, TestData.EstateId, TestData.CreditPurchasedDateTime, TestData.FloatCreditAmount, TestData.FloatCreditCostPrice), nameof(TransactionDomainEvents.TransactionCostInformationRecordedEvent) => TestData.TransactionCostInformationRecordedEvent, - nameof(TransactionDomainEvents.TransactionHasBeenCompletedEvent) => TestData.TransactionHasBeenCompletedEvent, + nameof(TransactionDomainEvents.TransactionHasBeenCompletedEvent) => TestData.DomainEvents.TransactionHasBeenCompletedEvent, nameof(TransactionDomainEvents.MerchantFeePendingSettlementAddedToTransactionEvent) => new TransactionDomainEvents.MerchantFeePendingSettlementAddedToTransactionEvent(TestData.TransactionId, TestData.EstateId, TestData.MerchantId, TestData.CalculatedFeeValue, 0, TestData.TransactionFeeId, TestData.TransactionFeeValue, TestData.TransactionFeeCalculateDateTime, TestData.TransactionFeeSettlementDueDate, TestData.TransactionDateTime), nameof(TransactionDomainEvents.SettledMerchantFeeAddedToTransactionEvent)=> TestData.SettledMerchantFeeAddedToTransactionEvent(TestData.SettlementDate), nameof(SettlementDomainEvents.MerchantFeeSettledEvent)=> new SettlementDomainEvents.MerchantFeeSettledEvent(TestData.SettlementAggregateId, TestData.EstateId, TestData.MerchantId, TestData.TransactionId, TestData.CalculatedFeeValue, 0, TestData.TransactionFeeId, TestData.TransactionFeeValue, TestData.TransactionFeeCalculateDateTime, TestData.SettlementDate), diff --git a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs index 00fdb021..28e20c0e 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs @@ -1,9 +1,11 @@ -using System.Threading; -using System.Threading.Tasks; -using MediatR; +using MediatR; +using Polly; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.EventHandling; using SimpleResults; +using System.Threading; +using System.Threading.Tasks; +using TransactionProcessor.BusinessLogic.Common; using TransactionProcessor.BusinessLogic.Requests; using TransactionProcessor.DomainEvents; @@ -49,22 +51,25 @@ public async Task Handle(IDomainEvent domainEvent, /// The cancellation token. private async Task HandleSpecificDomainEvent(MerchantStatementDomainEvents.StatementGeneratedEvent domainEvent, CancellationToken cancellationToken) { - MerchantStatementCommands.EmailMerchantStatementCommand command = new(domainEvent.EstateId, - domainEvent.MerchantId, domainEvent.MerchantStatementId); - - return await this.Mediator.Send(command, cancellationToken); + + IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(2, policyTag: "MerchantStatementDomainEventHandler - StatementGeneratedEvent"); + + return await retryPolicy.ExecuteAsync(async () => { + MerchantStatementCommands.EmailMerchantStatementCommand command = new(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.MerchantStatementId); + + return await this.Mediator.Send(command, cancellationToken); + }); } private async Task HandleSpecificDomainEvent(TransactionDomainEvents.TransactionHasBeenCompletedEvent domainEvent, CancellationToken cancellationToken) { - MerchantStatementCommands.AddTransactionToMerchantStatementCommand command = new(domainEvent.EstateId, - domainEvent.MerchantId, - domainEvent.CompletedDateTime, - domainEvent.TransactionAmount, - domainEvent.IsAuthorised, - domainEvent.TransactionId); - - return await this.Mediator.Send(command, cancellationToken); + IAsyncPolicy retryPolicy = PolicyFactory.CreatePolicy(2, policyTag: "MerchantStatementDomainEventHandler - TransactionHasBeenCompletedEvent"); + + return await retryPolicy.ExecuteAsync(async () => { + MerchantStatementCommands.AddTransactionToMerchantStatementCommand command = new(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.CompletedDateTime, domainEvent.TransactionAmount, domainEvent.IsAuthorised, domainEvent.TransactionId); + + return await this.Mediator.Send(command, cancellationToken); + }); } #endregion diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index d77f5b39..1173c251 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -1389,16 +1389,6 @@ public static TokenResponse TokenResponse() TestData.SettlementAggregateId, TestData.TransactionDateTime); - public static TransactionDomainEvents.TransactionHasBeenCompletedEvent TransactionHasBeenCompletedEvent = new TransactionDomainEvents.TransactionHasBeenCompletedEvent(TestData.TransactionId, - TestData.EstateId, - TestData.MerchantId, - TestData.ResponseCode, - TestData.ResponseMessage, - TestData.IsAuthorised, - TestData.TransactionDateTime, - TestData.TransactionAmount, - TestData.TransactionDateTime); - public static Guid TransactionFeeId = Guid.Parse("B83FCCCE-0D45-4FC2-8952-ED277A124BDB"); public static Guid TransactionFeeId2 = Guid.Parse("CA2D5119-1232-41D6-B6FD-9D84B9B5460C"); @@ -2574,6 +2564,16 @@ public static Models.Merchant.Merchant MerchantModelWithAddressesContactsDevices public static class DomainEvents { + public static TransactionDomainEvents.TransactionHasBeenCompletedEvent TransactionHasBeenCompletedEvent => new TransactionDomainEvents.TransactionHasBeenCompletedEvent(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.ResponseCode, + TestData.ResponseMessage, + TestData.IsAuthorised, + TestData.TransactionDateTime, + TestData.TransactionAmount, + TestData.TransactionDateTime); + public static SettlementDomainEvents.MerchantFeeSettledEvent MerchantFeeSettledEvent => new(SettlementId, EstateId, MerchantId, TransactionId, CalculatedFeeValue, FeeCalculationType, SettledFeeId1, FeeValue, TransactionFeeCalculateDateTime, SettlementDate); public static MerchantStatementDomainEvents.StatementCreatedEvent StatementCreatedEvent => new MerchantStatementDomainEvents.StatementCreatedEvent(TestData.MerchantStatementId, TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate);