diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs index a83c7bfa..92b20273 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs @@ -82,6 +82,106 @@ public async Task SettlementDomainService_ProcessSettlement_SettlementIsProcesse result.Data.ShouldNotBe(Guid.Empty); } + [Fact] + public async Task SettlementDomainService_ProcessSettlement_RunOutOfRetries_SettlementIsNotProcessed() + { + this.AggregateService.Setup(s => s.GetLatest(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(TestData.GetSettlementAggregateWithPendingMerchantFees(10))); + this.AggregateService.SetupSequence(s => s.GetLatest(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(0)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(1)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(2)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(3)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(4)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(5)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(6)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(7)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(8)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(9)))); + this.AggregateService.SetupSequence(s => s.Save(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })) + .ReturnsAsync(Result.Failure(new List { "WrongExpectedVersion" })); + this.AggregateService.SetupSequence(s => s.Save(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()); + + this.AggregateService.Setup(e => e.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.Aggregates.CreatedMerchantAggregate()); + + SettlementCommands.ProcessSettlementCommand command = + new(TestData.SettlementDate, TestData.MerchantId, + TestData.EstateId); + + Result result = await settlementDomainService.ProcessSettlement(command, CancellationToken.None); + + result.IsSuccess.ShouldBeFalse(); + this.AggregateService.Verify(s => s.Save(It.IsAny(), It.IsAny()), Times.Exactly(11)); + } + + [Fact] + public async Task SettlementDomainService_ProcessSettlement_RetryOnWrongExpected_SettlementIsProcessed() + { + this.AggregateService.Setup(s => s.GetLatest(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(TestData.GetSettlementAggregateWithPendingMerchantFees(10))); + this.AggregateService.SetupSequence(s => s.GetLatest(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(0)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(1)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(2)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(3)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(4)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(5)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(6)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(7)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(8)))) + .ReturnsAsync(Result.Success(TestData.GetCompletedAuthorisedSaleTransactionAggregateWithPendingFee(TestData.FeeIds.GetValueOrDefault(9)))); + this.AggregateService.SetupSequence(s => s.Save(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Failure(new List{ "WrongExpectedVersion" })) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()); + this.AggregateService.SetupSequence(s => s.Save(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()) + .ReturnsAsync(Result.Success()); + + this.AggregateService.Setup(e => e.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.Aggregates.CreatedMerchantAggregate()); + + SettlementCommands.ProcessSettlementCommand command = + new(TestData.SettlementDate, TestData.MerchantId, + TestData.EstateId); + + Result result = await settlementDomainService.ProcessSettlement(command, CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBe(Guid.Empty); + this.AggregateService.Verify(s => s.Save(It.IsAny(), It.IsAny()), Times.Exactly(4)); + } + [Fact] public async Task SettlementDomainService_ProcessSettlement_MerchantWithImmediateSettlement_SettlementIsProcessed() { diff --git a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs index ffecebaa..7146fa8f 100644 --- a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs @@ -100,6 +100,11 @@ private async Task ApplyTransactionUpdates(Func> ProcessSettlement(SettlementCommands.ProcessSettlementCommand command, CancellationToken cancellationToken) { + + IAsyncPolicy> retryPolicy = PolicyFactory.CreatePolicy(policyTag: "SettlementDomainService - ProcessSettlement"); + + return await PolicyFactory.ExecuteWithPolicyAsync(async () => { + Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId); List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = new(); @@ -161,6 +166,8 @@ public async Task> ProcessSettlement(SettlementCommands.ProcessSett } return Result.Success(settlementAggregateId); + }, retryPolicy, "SettlementDomainService - ProcessSettlement"); + } public async Task AddMerchantFeePendingSettlement(SettlementCommands.AddMerchantFeePendingSettlementCommand command,