From 2c55f22cd199b315540b5bebe0f9051d6c1e50dd Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Fri, 16 Jul 2021 09:57:20 +0100 Subject: [PATCH 1/2] Updated rule to ensure lines only processed once --- .../FileRequestHandlerTests.cs | 84 ++++++++- .../RequestHandlers/FileRequestHandler.cs | 2 +- FileProcessor.Testing/TestData.cs | 168 ++++++++++++------ FileProcessor/Program.cs | 20 +-- 4 files changed, 206 insertions(+), 68 deletions(-) diff --git a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs index 1b804dc..33ab15d 100644 --- a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs @@ -580,11 +580,13 @@ public void FileRequestHandler_SafaricomTopupRequest_FileIsEmpty_RequestIsHandle fileAggregateRepository.Verify(f => f.SaveChanges(It.IsAny(), It.IsAny()), Times.Never); } - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_RequestIsHandled() + [Theory] + [InlineData("Safaricom")] + [InlineData("Voucher")] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_RequestIsHandled(String operatorName) { Mock fileProcessorManager = new Mock(); - fileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + fileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileProfile(operatorName)); Mock> fileImportLogAggregateRepository = new Mock>(); @@ -867,12 +869,21 @@ public void FileRequestHandler_ProcessTransactionLineForFileRequest_LineInReques fileFormatHandlerResolver, fileSystem); - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest1 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 1, TestData.FileLine); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest2 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 2, TestData.FileLine); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest3 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 3, TestData.FileLine); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest4 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 4, TestData.FileLine); Should.NotThrow(async () => { - await fileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); + await fileRequestHandler.Handle(processTransactionForFileLineRequest1, CancellationToken.None); + await fileRequestHandler.Handle(processTransactionForFileLineRequest2, CancellationToken.None); + await fileRequestHandler.Handle(processTransactionForFileLineRequest3, CancellationToken.None); + await fileRequestHandler.Handle(processTransactionForFileLineRequest4, CancellationToken.None); }); } @@ -1041,6 +1052,67 @@ public void FileRequestHandler_ProcessTransactionForFileLineRequest_EmptyFileLin fileFormatHandler.Verify(f => f.ParseFileLine(It.IsAny()), Times.Never); } + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileParsingFailed_RequestIsHandled() + { + Mock fileProcessorManager = new Mock(); + fileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + Mock> fileImportLogAggregateRepository = + new Mock>(); + + Mock> fileAggregateRepository = + new Mock>(); + fileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + Mock transactionProcessorClient = new Mock(); + transactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + Mock estateClient = new Mock(); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + estateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + Mock securityServiceClient = new Mock(); + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + Mock fileFormatHandler = new Mock(); + Dictionary transactionMetadata = null; + fileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + fileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Throws(); + + Func fileFormatHandlerResolver = (format) => + { + return fileFormatHandler.Object; + }; + + MockFileSystem fileSystem = new MockFileSystem(); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + FileRequestHandler fileRequestHandler = new FileRequestHandler(fileProcessorManager.Object, + fileImportLogAggregateRepository.Object, + fileAggregateRepository.Object, + transactionProcessorClient.Object, + estateClient.Object, + securityServiceClient.Object, + fileFormatHandlerResolver, + fileSystem); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await fileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); + }); + estateClient.Verify(f => f.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + [Fact] public void FileRequestHandler_ProcessTransactionForFileLineRequest_MerchantNotFound_RequestIsHandled() { diff --git a/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs b/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs index e768bd3..7dc2a73 100644 --- a/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs +++ b/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs @@ -346,7 +346,7 @@ public async Task Handle(ProcessTransactionForFileLineRequest request, throw new NotFoundException($"File Line Number {request.LineNumber} not found in File Id {request.FileId}"); } - if (fileLine.TransactionId != Guid.Empty) + if (fileLine.ProcessingResult != ProcessingResult.NotProcessed) { // Line already processed return new Unit(); diff --git a/FileProcessor.Testing/TestData.cs b/FileProcessor.Testing/TestData.cs index 4bb6afd..02d9fed 100644 --- a/FileProcessor.Testing/TestData.cs +++ b/FileProcessor.Testing/TestData.cs @@ -93,12 +93,22 @@ public static String GetVoucherDetailLine(String voucherOperatorIdentifier, Stri public static FileProfile FileProfileNull => null; + public static FileProfile GetFileProfile(String operatorName) + { + if (operatorName == "Safaricom") + return FileProfileSafaricom; + if (operatorName == "Voucher") + return FileProfileVoucher; + + return null; + } + public static FileProfile FileProfileSafaricom => new FileProfile(TestData.SafaricomFileProfileId, TestData.SafaricomProfileName, TestData.SafaricomListeningDirectory, TestData.SafaricomRequestType, - TestData.OperatorIdentifier, + TestData.SafaricomOperatorIdentifier, TestData.SafaricomLineTerminator, TestData.SafaricomFileFormatHandler); @@ -123,37 +133,58 @@ public static String GetVoucherDetailLine(String voucherOperatorIdentifier, Stri public static String DeviceIdentifier = "testdevice1"; - public static String OperatorIdentifier = "Safaricom"; + public static String SafaricomOperatorIdentifier = "Safaricom"; - public static String VoucherOperatorIdentifier = "Healthcare Centre 1"; + public static String VoucherOperatorIdentifier = "Voucher"; public static String OperatorIdentifierNotFile = "Other Operator"; - public static Guid OperatorId = Guid.Parse("68B5745B-2D77-41F1-8310-FDAB060C0001"); + public static Guid SafaricomOperatorId = Guid.Parse("68B5745B-2D77-41F1-8310-FDAB060C0001"); + + public static Guid VoucherOperatorId = Guid.Parse("70744470-37CD-4175-8869-86A225108BED"); public static String MerchantNumber = "12345678"; public static String TerminalNumber = "00000001"; - public static Guid ContractId = Guid.Parse("835D421B-6F34-4369-AC27-6E2365B11D29"); + public static Guid SafaricomContractId = Guid.Parse("835D421B-6F34-4369-AC27-6E2365B11D29"); + public static Guid VoucherContractId = Guid.Parse("EFD4C87B-C6F8-4DF2-BFCA-0AE39E4B9511"); + + public static String SafaricomContractDescription = "Safaricom Contract"; + public static String VoucherContractDescription = "Voucher Contract"; + + public static String SafaricomContractProductWithValueName = "100 KES Topup"; + + public static Guid SafaricomContractWithValueProductId = Guid.Parse("7F8FF091-E127-429D-8D92-5B8597320AE1"); + + public static Decimal? SafaricomContractProductWithValueValue = 100.00m; - public static String ContractDescription = "Safaricom Contract"; + public static String SafaricomContractProductWithValueDisplayText = "100 KES"; - public static String ContractProductWithValueName = "100 KES Topup"; + public static String SafaricomContractProductWithNullValueName = "Custom Topup"; - public static Guid ContractWithValueProductId = Guid.Parse("7F8FF091-E127-429D-8D92-5B8597320AE1"); + public static Guid SafarciomContractWithNullValueProductId = Guid.Parse("435CBB20-1ADC-4646-8DEC-5341E877220D"); - public static Decimal? ContractProductWithValueValue = 100.00m; + public static Decimal? SafaricomContractProductWithNullValueValue = null; - public static String ContractProductWithValueDisplayText = "100 KES"; + public static String SafaricomContractProductWithNullValueDisplayText = "Custom"; - public static String ContractProductWithNullValueName = "Custom Topup"; - public static Guid ContractWithNullValueProductId = Guid.Parse("435CBB20-1ADC-4646-8DEC-5341E877220D"); + public static String VoucherContractProductWithValueName = "10 KES Voucher"; - public static Decimal? ContractProductWithNullValueValue = null; + public static Guid VoucherContractWithValueProductId = Guid.Parse("D564FFF0-04B0-41EC-AA58-2AD981D352B3"); - public static String ContractProductWithNullValueDisplayText = "Custom"; + public static Decimal? VoucherContractProductWithValueValue = 10.00m; + + public static String VoucherContractProductWithValueDisplayText = "10 KES"; + + public static String VoucherContractProductWithNullValueName = "Custom Voucher"; + + public static Guid VoucherContractWithNullValueProductId = Guid.Parse("0B648D1A-F057-4055-90E8-3B106DB391E0"); + + public static Decimal? VoucherContractProductWithNullValueValue = null; + + public static String VoucherContractProductWithNullValueDisplayText = "Custom"; public static Guid FileImportLogId = Guid.Parse("5F1149F8-0313-45E4-BE3A-3D7B07EEB414"); public static Guid FileImportLogId1 = Guid.Parse("7EF0D557-2148-4DED-83F5-2521E8422391"); @@ -183,7 +214,7 @@ public static String GetVoucherDetailLine(String voucherOperatorIdentifier, Stri { {"Amount", "100"}, {"CustomerAccountNumber", "123456789"}, - {"OperatorName", TestData.OperatorIdentifier} + {"OperatorName", TestData.SafaricomOperatorIdentifier} }; public static FileImportLogAggregate GetEmptyFileImportLogAggregate() @@ -238,7 +269,14 @@ public static FileAggregate GetFileAggregateWithLinesAlreadyProcessed() fileAggregate.CreateFile(TestData.FileImportLogId,TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FileProfileId, TestData.OriginalFileName, TestData.FileUploadedDateTime); fileAggregate.AddFileLine("D,1,2"); - fileAggregate.RecordFileLineAsSuccessful(TestData.LineNumber, TestData.TransactionId); + fileAggregate.AddFileLine("D,1,2"); + fileAggregate.AddFileLine("D,1,2"); + fileAggregate.AddFileLine("D,1,2"); + fileAggregate.RecordFileLineAsSuccessful(1, TestData.TransactionId); + fileAggregate.RecordFileLineAsRejected(2, TestData.RejectionReason); + fileAggregate.RecordFileLineAsFailed(3, TestData.TransactionId, "-1","Failed"); + fileAggregate.RecordFileLineAsIgnored(4); + return fileAggregate; } @@ -269,8 +307,8 @@ public static TokenResponse TokenResponse() { new MerchantOperatorResponse { - Name = TestData.OperatorIdentifier, - OperatorId = TestData.OperatorId, + Name = TestData.SafaricomOperatorIdentifier, + OperatorId = TestData.SafaricomOperatorId, MerchantNumber = TestData.MerchantNumber, TerminalNumber = TestData.TerminalNumber } @@ -284,32 +322,60 @@ public static List GetMerchantContractsResponse() contractResponses.Add(new ContractResponse { EstateId = TestData.EstateId, - OperatorName = TestData.OperatorIdentifier, - ContractId = TestData.ContractId, - Description = TestData.ContractDescription, - OperatorId = TestData.OperatorId, + OperatorName = TestData.SafaricomOperatorIdentifier, + ContractId = TestData.SafaricomContractId, + Description = TestData.SafaricomContractDescription, + OperatorId = TestData.SafaricomOperatorId, Products = new List { new ContractProduct { - Name = TestData.ContractProductWithValueName, - ProductId = TestData.ContractWithValueProductId, - Value = TestData.ContractProductWithValueValue, - DisplayText = TestData.ContractProductWithValueDisplayText, + Name = TestData.SafaricomContractProductWithValueName, + ProductId = TestData.SafaricomContractWithValueProductId, + Value = TestData.SafaricomContractProductWithValueValue, + DisplayText = TestData.SafaricomContractProductWithValueDisplayText, TransactionFees = null }, new ContractProduct { - Name = TestData.ContractProductWithNullValueName, - ProductId = TestData.ContractWithNullValueProductId, - Value = TestData.ContractProductWithNullValueValue, - DisplayText = TestData.ContractProductWithNullValueDisplayText, + Name = TestData.SafaricomContractProductWithNullValueName, + ProductId = TestData.SafarciomContractWithNullValueProductId, + Value = TestData.SafaricomContractProductWithNullValueValue, + DisplayText = TestData.SafaricomContractProductWithNullValueDisplayText, TransactionFees = null } } }); - + + contractResponses.Add(new ContractResponse + { + EstateId = TestData.EstateId, + OperatorName = TestData.VoucherOperatorIdentifier, + ContractId = TestData.VoucherContractId, + Description = TestData.VoucherContractDescription, + OperatorId = TestData.VoucherOperatorId, + Products = new List + { + new ContractProduct + { + Name = TestData.VoucherContractProductWithValueName, + ProductId = TestData.VoucherContractWithValueProductId, + Value = TestData.VoucherContractProductWithValueValue, + DisplayText = TestData.VoucherContractProductWithValueDisplayText, + TransactionFees = null + }, + new ContractProduct + { + Name = TestData.VoucherContractProductWithNullValueName, + ProductId = TestData.VoucherContractWithNullValueProductId, + Value = TestData.VoucherContractProductWithNullValueValue, + DisplayText = TestData.VoucherContractProductWithNullValueDisplayText, + TransactionFees = null + } + } + }); + return contractResponses; } @@ -321,25 +387,25 @@ public static List GetMerchantContractsNoFileOperatorResponse( { EstateId = TestData.EstateId, OperatorName = TestData.OperatorIdentifierNotFile, - ContractId = TestData.ContractId, - Description = TestData.ContractDescription, - OperatorId = TestData.OperatorId, + ContractId = TestData.SafaricomContractId, + Description = TestData.SafaricomContractDescription, + OperatorId = TestData.SafaricomOperatorId, Products = new List { new ContractProduct { - Name = TestData.ContractProductWithValueName, - ProductId = TestData.ContractWithValueProductId, - Value = TestData.ContractProductWithValueValue, - DisplayText = TestData.ContractProductWithValueDisplayText, + Name = TestData.SafaricomContractProductWithValueName, + ProductId = TestData.SafaricomContractWithValueProductId, + Value = TestData.SafaricomContractProductWithValueValue, + DisplayText = TestData.SafaricomContractProductWithValueDisplayText, TransactionFees = null }, new ContractProduct { - Name = TestData.ContractProductWithNullValueName, - ProductId = TestData.ContractWithNullValueProductId, - Value = TestData.ContractProductWithNullValueValue, - DisplayText = TestData.ContractProductWithNullValueDisplayText, + Name = TestData.SafaricomContractProductWithNullValueName, + ProductId = TestData.SafarciomContractWithNullValueProductId, + Value = TestData.SafaricomContractProductWithNullValueValue, + DisplayText = TestData.SafaricomContractProductWithNullValueDisplayText, TransactionFees = null } @@ -356,18 +422,18 @@ public static List GetMerchantContractsResponseNoNullValueProd contractResponses.Add(new ContractResponse { EstateId = TestData.EstateId, - OperatorName = TestData.OperatorIdentifier, - ContractId = TestData.ContractId, - Description = TestData.ContractDescription, - OperatorId = TestData.OperatorId, + OperatorName = TestData.SafaricomOperatorIdentifier, + ContractId = TestData.SafaricomContractId, + Description = TestData.SafaricomContractDescription, + OperatorId = TestData.SafaricomOperatorId, Products = new List { new ContractProduct { - Name = TestData.ContractProductWithValueName, - ProductId = TestData.ContractWithValueProductId, - Value = TestData.ContractProductWithValueValue, - DisplayText = TestData.ContractProductWithValueDisplayText, + Name = TestData.SafaricomContractProductWithValueName, + ProductId = TestData.SafaricomContractWithValueProductId, + Value = TestData.SafaricomContractProductWithValueValue, + DisplayText = TestData.SafaricomContractProductWithValueDisplayText, TransactionFees = null } } @@ -430,7 +496,7 @@ public static List GetMerchantContractsResponseNoNullValueProd TestData.VoucherFileFormatHandler); public static Guid VoucherFileProfileId = Guid.Parse("079F1FF5-F51E-4BE0-AF4F-2D4862E6D34F"); - public static String VoucherProfileName = "Safaricom Profile"; + public static String VoucherProfileName = "Voucher Profile"; public static String VoucherListeningDirectory = "/home/txnproc/bulkfiles/voucher"; diff --git a/FileProcessor/Program.cs b/FileProcessor/Program.cs index 30e39c3..7484921 100644 --- a/FileProcessor/Program.cs +++ b/FileProcessor/Program.cs @@ -80,16 +80,16 @@ public static IHostBuilder CreateHostBuilder(string[] args) FileLineAddedEvent fileLineAddedEvent = new FileLineAddedEvent(Guid.Empty, Guid.Empty, 0, String.Empty); - services.AddHostedService(provider => - { - IDomainEventHandlerResolver r = - provider.GetRequiredService(); - EventStorePersistentSubscriptionsClient p = provider.GetRequiredService(); - HttpClient h = provider.GetRequiredService(); - SubscriptionWorker worker = new SubscriptionWorker(r, p, h); - worker.TraceGenerated += Worker_TraceGenerated; - return worker; - }); + //services.AddHostedService(provider => + // { + // IDomainEventHandlerResolver r = + // provider.GetRequiredService(); + // EventStorePersistentSubscriptionsClient p = provider.GetRequiredService(); + // HttpClient h = provider.GetRequiredService(); + // SubscriptionWorker worker = new SubscriptionWorker(r, p, h); + // worker.TraceGenerated += Worker_TraceGenerated; + // return worker; + // }); }); return hostBuilder; From 60dffba0c8d810b0a263eea067ab86e472ff20b5 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Fri, 16 Jul 2021 11:34:52 +0100 Subject: [PATCH 2/2] :| --- FileProcessor/Program.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/FileProcessor/Program.cs b/FileProcessor/Program.cs index 7484921..30e39c3 100644 --- a/FileProcessor/Program.cs +++ b/FileProcessor/Program.cs @@ -80,16 +80,16 @@ public static IHostBuilder CreateHostBuilder(string[] args) FileLineAddedEvent fileLineAddedEvent = new FileLineAddedEvent(Guid.Empty, Guid.Empty, 0, String.Empty); - //services.AddHostedService(provider => - // { - // IDomainEventHandlerResolver r = - // provider.GetRequiredService(); - // EventStorePersistentSubscriptionsClient p = provider.GetRequiredService(); - // HttpClient h = provider.GetRequiredService(); - // SubscriptionWorker worker = new SubscriptionWorker(r, p, h); - // worker.TraceGenerated += Worker_TraceGenerated; - // return worker; - // }); + services.AddHostedService(provider => + { + IDomainEventHandlerResolver r = + provider.GetRequiredService(); + EventStorePersistentSubscriptionsClient p = provider.GetRequiredService(); + HttpClient h = provider.GetRequiredService(); + SubscriptionWorker worker = new SubscriptionWorker(r, p, h); + worker.TraceGenerated += Worker_TraceGenerated; + return worker; + }); }); return hostBuilder;