From 2aeea429a78b10c836a0511012a6f1ae021bb808 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 2 Jan 2023 08:03:40 +0000 Subject: [PATCH 1/4] Fix bootstrapper test and add mediator tests --- .../DummyFileProcessorDomainService.cs | 31 + .../FileProcessor.BusinessLogic.Tests.csproj | 1 + .../FileProcessorDomainServiceTests.cs | 955 ++++++++++++++++++ .../FileRequestHandlerTests.cs | 942 +---------------- .../MediatorTests.cs | 108 ++ .../RequestTests.cs | 2 +- .../Common/GuidCalculator.cs | 2 + .../RequestHandlers/FileRequestHandler.cs | 538 +--------- .../Services/FileProcessorDomainService.cs | 464 +++++++++ .../Services/IFileProcessorDomainService.cs | 26 + .../FileProcessor.Testing.csproj | 2 +- FileProcessor.Testing/TestData.cs | 16 + FileProcessor.Tests/BootstrapperTests.cs | 3 +- .../FileProcessor.Tests.csproj | 2 +- FileProcessor.sln | 9 - FileProcessor/Bootstrapper/ClientRegistry.cs | 6 +- .../DomainEventHandlerRegistry.cs | 6 +- FileProcessor/Bootstrapper/FileRegistry.cs | 6 +- .../Bootstrapper/MediatorRegistry.cs | 6 +- .../Bootstrapper/MiddlewareRegistry.cs | 6 +- FileProcessor/Bootstrapper/MiscRegistry.cs | 8 +- .../Bootstrapper/RepositoryRegistry.cs | 7 +- FileProcessor/Common/Extensions.cs | 144 +++ .../Common/SubscriptionWorkerConfig.cs | 18 + .../Common/SubscriptionWorkersRoot.cs | 14 + FileProcessor/Startup.cs | 182 +--- 26 files changed, 1870 insertions(+), 1634 deletions(-) create mode 100644 FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs create mode 100644 FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs create mode 100644 FileProcessor.BusinessLogic.Tests/MediatorTests.cs create mode 100644 FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs create mode 100644 FileProcessor.BusinessLogic/Services/IFileProcessorDomainService.cs create mode 100644 FileProcessor/Common/Extensions.cs create mode 100644 FileProcessor/Common/SubscriptionWorkerConfig.cs create mode 100644 FileProcessor/Common/SubscriptionWorkersRoot.cs diff --git a/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs new file mode 100644 index 0000000..9b90a2a --- /dev/null +++ b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs @@ -0,0 +1,31 @@ +namespace FileProcessor.BusinessLogic.Tests; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Requests; +using Services; + +public class DummyFileProcessorDomainService : IFileProcessorDomainService +{ + public async Task UploadFile(UploadFileRequest request, + CancellationToken cancellationToken) { + return Guid.NewGuid(); + } + + public async Task ProcessUploadedFile(ProcessUploadedFileRequest request, + CancellationToken cancellationToken) { + } + + public async Task ProcessSafaricomTopup(SafaricomTopupRequest request, + CancellationToken cancellationToken) { + } + + public async Task ProcessVoucher(VoucherRequest request, + CancellationToken cancellationToken) { + } + + public async Task ProcessTransactionForFileLine(ProcessTransactionForFileLineRequest request, + CancellationToken cancellationToken) { + } +} \ No newline at end of file diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessor.BusinessLogic.Tests.csproj b/FileProcessor.BusinessLogic.Tests/FileProcessor.BusinessLogic.Tests.csproj index a722793..7473757 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProcessor.BusinessLogic.Tests.csproj +++ b/FileProcessor.BusinessLogic.Tests/FileProcessor.BusinessLogic.Tests.csproj @@ -27,6 +27,7 @@ + diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs new file mode 100644 index 0000000..1db48ee --- /dev/null +++ b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs @@ -0,0 +1,955 @@ +namespace FileProcessor.BusinessLogic.Tests; + +using System; +using System.IO; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using System.Threading; +using System.Threading.Tasks; +using EstateManagement.Client; +using FileAggregate; +using FileFormatHandlers; +using FileImportLogAggregate; +using Managers; +using Microsoft.Extensions.Configuration; +using Moq; +using Requests; +using SecurityService.Client; +using Services; +using Shared.DomainDrivenDesign.EventSourcing; +using Shared.EventStore.Aggregate; +using Shared.Exceptions; +using Shared.General; +using Shared.Logger; +using Shouldly; +using Testing; +using TransactionProcessor.Client; +using TransactionProcessor.DataTransferObjects; +using Xunit; + +public class FileProcessorDomainServiceTests +{ + private Mock FileProcessorManager; + + private Mock> FileImportLogAggregateRepository; + + private Mock> FileAggregateRepository; + + private Mock TransactionProcessorClient; + + private Mock EstateClient; + + private Mock SecurityServiceClient; + + private Mock FileFormatHandler; + + private FileProcessorDomainService FileProcessorDomainService; + + private MockFileSystem FileSystem; + public FileProcessorDomainServiceTests() + { + this.FileProcessorManager = new Mock(); + this.FileImportLogAggregateRepository = + new Mock>(); + this.FileAggregateRepository = + new Mock>(); + this.TransactionProcessorClient = new Mock(); + this.EstateClient = new Mock(); + this.SecurityServiceClient = new Mock(); + this.FileFormatHandler = new Mock(); + this.FileSystem = new MockFileSystem(); + + Func fileFormatHandlerResolver = (format) => + { + return this.FileFormatHandler.Object; + }; + + this.FileProcessorDomainService = new FileProcessorDomainService(this.FileProcessorManager.Object, + this.FileImportLogAggregateRepository.Object, + this.FileAggregateRepository.Object, + this.TransactionProcessorClient.Object, + this.EstateClient.Object, + this.SecurityServiceClient.Object, + fileFormatHandlerResolver, + this.FileSystem); + Logger.Initialise(NullLogger.Instance); + } + + [Fact] + public async Task FileRequestHandler_UploadFileRequest_RequestIsHandled() + { + + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); + + + + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom"); + + UploadFileRequest uploadFileRequest = + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.UploadFile(uploadFileRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_UploadFileRequest_ImportLogAlreadyCreated_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom"); + + UploadFileRequest uploadFileRequest = + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.UploadFile(uploadFileRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_UploadFileRequest_NoFileProfiles_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); + + UploadFileRequest uploadFileRequest = + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.UploadFile(uploadFileRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_UploadFileRequest_FileNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); + + UploadFileRequest uploadFileRequest = + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.UploadFile(uploadFileRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_UploadFileRequest_DestinationDirectoryNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + Logger.Initialise(NullLogger.Instance); + + UploadFileRequest uploadFileRequest = + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.UploadFile(uploadFileRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessUploadedFileRequest_RequestIsHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); + + ProcessUploadedFileRequest processUploadedFileRequest = + new ProcessUploadedFileRequest(TestData.EstateId, TestData.MerchantId, TestData.FileImportLogId, TestData.FileId, TestData.UserId, TestData.FilePath, TestData.FileProfileId, TestData.FileUploadedDateTime); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessUploadedFile(processUploadedFileRequest, CancellationToken.None); + }); + } + + + [Fact] + public void FileRequestHandler_SafaricomTopupRequest_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); + } + + + + [Fact] + public void FileRequestHandler_SafaricomTopupRequest_FileAggregateNotCreated_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/failed"); + } + + [Fact] + public void FileRequestHandler_SafaricomTopupRequest_FileNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); + + Logger.Initialise(NullLogger.Instance); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_SafaricomTopupRequest_NoFileProfiles_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + Logger.Initialise(NullLogger.Instance); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_SafaricomTopupRequest_ProcessedDirectoryNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); + } + + [Fact] + public async Task FileRequestHandler_SafaricomTopupRequest_FailedDirectoryNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); + } + + [Fact] + public void FileRequestHandler_SafaricomTopupRequest_FileIsEmpty_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData(String.Empty)); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); + + SafaricomTopupRequest safaricomTopupRequest = + new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessSafaricomTopup(safaricomTopupRequest, CancellationToken.None); + }); + + this.FileAggregateRepository.Verify(f => f.SaveChanges(It.IsAny(), It.IsAny()), Times.Never); + this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); + } + + [Theory] + [InlineData("Safaricom")] + [InlineData("Voucher")] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_RequestIsHandled(String operatorName) + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileProfile(operatorName)); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_WithOperatorName_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadataWithOperatorName); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionLineForFileRequest_FileAggregateNotFound_RequestHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionLineForFileRequest_FileAggregateWithNoLines_RequestHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionLineForFileRequest_LineInRequestNotFoundInFileAggregate_RequestHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.NotFoundLineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionLineForFileRequest_LineInRequestAlreadyProcessed_RequestHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLinesAlreadyProcessed); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest1 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 1, TestData.FileLine1); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest2 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 1, TestData.FileLine2); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest3 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 3, TestData.FileLine3); + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest4 = + new ProcessTransactionForFileLineRequest(TestData.FileId, 4, TestData.FileLine4); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest1, CancellationToken.None); + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest2, CancellationToken.None); + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest3, CancellationToken.None); + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest4, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileProfileNotFound_RequestIsHandled() + { + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileLineIgnored_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(true); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + this.FileFormatHandler.Verify(f => f.ParseFileLine(It.IsAny()), Times.Never); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_EmptyFileLineIgnored_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithBlankLine); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + this.FileFormatHandler.Verify(f => f.ParseFileLine(It.IsAny()), Times.Never); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileParsingFailed_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + //Dictionary transactionMetadata = null; + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Throws(); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + this.EstateClient.Verify(f => f.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_MerchantNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_NoMerchantContractsFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_NoMerchantContractForFileOperatorFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsNoFileOperatorResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_MerchantContractProductNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponseNoNullValueProduct); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_ProcessTransactionForFileLineRequest_TransactionNotSuccessful_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); + + this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.SerialisedMessageResponseFailedSale); + + this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + + this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantContractsResponse); + + this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); + + this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); + this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); + + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + Logger.Initialise(NullLogger.Instance); + + ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessTransactionForFileLine(processTransactionForFileLineRequest, CancellationToken.None); + }); + } + + [Fact] + public void FileRequestHandler_VoucherRequest_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); + } + + + + [Fact] + public void FileRequestHandler_VoucherRequest_FileAggregateNotCreated_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/failed"); + } + + [Fact] + public void FileRequestHandler_VoucherRequest_FileNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); + + Logger.Initialise(NullLogger.Instance); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_VoucherRequest_NoFileProfiles_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); + + this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + Logger.Initialise(NullLogger.Instance); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.Throw(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + } + + [Fact] + public async Task FileRequestHandler_VoucherRequest_ProcessedDirectoryNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); + } + + [Fact] + public async Task FileRequestHandler_VoucherRequest_FailedDirectoryNotFound_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); + } + + [Fact] + public void FileRequestHandler_VoucherRequest_FileIsEmpty_RequestIsHandled() + { + this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); + + this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); + + this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData(String.Empty)); + + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); + this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); + + VoucherRequest voucherRequest = + new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); + + Should.NotThrow(async () => + { + await this.FileProcessorDomainService.ProcessVoucher(voucherRequest, CancellationToken.None); + }); + + this.FileAggregateRepository.Verify(f => f.SaveChanges(It.IsAny(), It.IsAny()), Times.Never); + this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); + } + + private void VerifyFileProcessing(String filePath) + { + IDirectoryInfo directoryInfo = this.FileSystem.DirectoryInfo.FromDirectoryName(filePath); + directoryInfo.GetFiles("*.*").Length.ShouldBe(1); + } +} \ No newline at end of file diff --git a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs index ef89ae0..fb304e4 100644 --- a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -7,955 +6,60 @@ namespace FileProcessor.BusinessLogic.Tests { - using System.IO; - using System.IO.Abstractions; - using System.IO.Abstractions.TestingHelpers; using System.Threading; - using EstateManagement.Client; - using FileAggregate; - using FileFormatHandlers; - using FileImportLogAggregate; - using Managers; - using Microsoft.Extensions.Configuration; using Moq; using RequestHandlers; - using Requests; - using SecurityService.Client; - using Shared.DomainDrivenDesign.EventSourcing; - using Shared.EventStore.Aggregate; - using Shared.Exceptions; - using Shared.General; - using Shared.Logger; + using Services; using Shouldly; using Testing; - using TransactionProcessor.Client; - using TransactionProcessor.DataTransferObjects; - using Xunit; public class FileRequestHandlerTests { - private Mock FileProcessorManager; - - private Mock> FileImportLogAggregateRepository; - - private Mock> FileAggregateRepository; - - private Mock TransactionProcessorClient; - - private Mock EstateClient; - - private Mock SecurityServiceClient; - - private Mock FileFormatHandler; - + private Mock FileProcessorDomainService; private FileRequestHandler FileRequestHandler; - private MockFileSystem FileSystem; - public FileRequestHandlerTests() - { - this.FileProcessorManager = new Mock(); - this.FileImportLogAggregateRepository = - new Mock>(); - this.FileAggregateRepository = - new Mock>(); - this.TransactionProcessorClient = new Mock(); - this.EstateClient = new Mock(); - this.SecurityServiceClient = new Mock(); - this.FileFormatHandler = new Mock(); - this.FileSystem = new MockFileSystem(); - - Func fileFormatHandlerResolver = (format) => - { - return FileFormatHandler.Object; - }; - - this.FileRequestHandler = new FileRequestHandler(FileProcessorManager.Object, - FileImportLogAggregateRepository.Object, - FileAggregateRepository.Object, - TransactionProcessorClient.Object, - EstateClient.Object, - SecurityServiceClient.Object, - fileFormatHandlerResolver, - FileSystem); - Logger.Initialise(NullLogger.Instance); + public FileRequestHandlerTests() { + this.FileProcessorDomainService = new Mock(); + this.FileRequestHandler = new FileRequestHandler(this.FileProcessorDomainService.Object); } - [Fact] - public async Task FileRequestHandler_UploadFileRequest_RequestIsHandled() - { - - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); - - - - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom"); + public async Task FileRequestHandler_HandleUploadFileRequest_RequestHandled() { - UploadFileRequest uploadFileRequest = - new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(uploadFileRequest, CancellationToken.None); + Should.NotThrow(async () => { + await this.FileRequestHandler.Handle(TestData.UploadFileRequest, CancellationToken.None); }); } - [Fact] - public async Task FileRequestHandler_UploadFileRequest_ImportLogAlreadyCreated_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom"); - - UploadFileRequest uploadFileRequest = - new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(uploadFileRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_UploadFileRequest_NoFileProfiles_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); - - UploadFileRequest uploadFileRequest = - new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(uploadFileRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_UploadFileRequest_FileNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); - - UploadFileRequest uploadFileRequest = - new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(uploadFileRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_UploadFileRequest_DestinationDirectoryNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileImportLogAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - Logger.Initialise(NullLogger.Instance); - - UploadFileRequest uploadFileRequest = - new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePathWithName, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(uploadFileRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessUploadedFileRequest_RequestIsHandled() - { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); - - ProcessUploadedFileRequest processUploadedFileRequest = - new ProcessUploadedFileRequest(TestData.EstateId, TestData.MerchantId, TestData.FileImportLogId, TestData.FileId, TestData.UserId, TestData.FilePath, TestData.FileProfileId, TestData.FileUploadedDateTime); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processUploadedFileRequest, CancellationToken.None); - }); - } - - - [Fact] - public void FileRequestHandler_SafaricomTopupRequest_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); - } - - - - [Fact] - public void FileRequestHandler_SafaricomTopupRequest_FileAggregateNotCreated_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/failed"); - } - - [Fact] - public void FileRequestHandler_SafaricomTopupRequest_FileNotFound_RequestIsHandled() + public async Task FileRequestHandler_ProcessUploadedFileRequest_RequestHandled() { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); - - Logger.Initialise(NullLogger.Instance); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); + Should.NotThrow(async () => { + await this.FileRequestHandler.Handle(TestData.ProcessUploadedFileRequest, CancellationToken.None); }); } - [Fact] - public async Task FileRequestHandler_SafaricomTopupRequest_NoFileProfiles_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - Logger.Initialise(NullLogger.Instance); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_SafaricomTopupRequest_ProcessedDirectoryNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); - } - - [Fact] - public async Task FileRequestHandler_SafaricomTopupRequest_FailedDirectoryNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); - } - - [Fact] - public void FileRequestHandler_SafaricomTopupRequest_FileIsEmpty_RequestIsHandled() + public async Task FileRequestHandler_SafaricomTopupRequest_RequestHandled() { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData(String.Empty)); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom/failed"); - - SafaricomTopupRequest safaricomTopupRequest = - new SafaricomTopupRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(safaricomTopupRequest, CancellationToken.None); - }); - - this.FileAggregateRepository.Verify(f => f.SaveChanges(It.IsAny(), It.IsAny()), Times.Never); - this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); - } - - [Theory] - [InlineData("Safaricom")] - [InlineData("Voucher")] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_RequestIsHandled(String operatorName) - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileProfile(operatorName)); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); + Should.NotThrow(async () => { + await this.FileRequestHandler.Handle(TestData.SafaricomTopupRequest, CancellationToken.None); }); } - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_WithOperatorName_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadataWithOperatorName); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionLineForFileRequest_FileAggregateNotFound_RequestHandled() - { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionLineForFileRequest_FileAggregateWithNoLines_RequestHandled() - { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionLineForFileRequest_LineInRequestNotFoundInFileAggregate_RequestHandled() - { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.NotFoundLineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionLineForFileRequest_LineInRequestAlreadyProcessed_RequestHandled() + public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_RequestHandled() { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLinesAlreadyProcessed); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest1 = - new ProcessTransactionForFileLineRequest(TestData.FileId, 1, TestData.FileLine1); - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest2 = - new ProcessTransactionForFileLineRequest(TestData.FileId, 1, TestData.FileLine2); - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest3 = - new ProcessTransactionForFileLineRequest(TestData.FileId, 3, TestData.FileLine3); - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest4 = - new ProcessTransactionForFileLineRequest(TestData.FileId, 4, TestData.FileLine4); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest1, CancellationToken.None); - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest2, CancellationToken.None); - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest3, CancellationToken.None); - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest4, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileProfileNotFound_RequestIsHandled() - { - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileLineIgnored_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(true); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - this.FileFormatHandler.Verify(f => f.ParseFileLine(It.IsAny()), Times.Never); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_EmptyFileLineIgnored_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithBlankLine); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - this.FileFormatHandler.Verify(f => f.ParseFileLine(It.IsAny()), Times.Never); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_FileParsingFailed_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - //Dictionary transactionMetadata = null; - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Throws(); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - this.EstateClient.Verify(f => f.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_MerchantNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_NoMerchantContractsFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetEmptyMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_NoMerchantContractForFileOperatorFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsNoFileOperatorResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_MerchantContractProductNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponseNoNullValueProduct); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_ProcessTransactionForFileLineRequest_TransactionNotSuccessful_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetFileAggregateWithLines); - - this.TransactionProcessorClient.Setup(t => t.PerformTransaction(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.SerialisedMessageResponseFailedSale); - - this.EstateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponseWithOperator); - - this.EstateClient.Setup(e => e.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantContractsResponse); - - this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse()); - - this.FileFormatHandler.Setup(f => f.FileLineCanBeIgnored(It.IsAny())).Returns(false); - this.FileFormatHandler.Setup(f => f.ParseFileLine(It.IsAny())).Returns(TestData.TransactionMetadata); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); - ConfigurationReader.Initialise(configurationRoot); - Logger.Initialise(NullLogger.Instance); - - ProcessTransactionForFileLineRequest processTransactionForFileLineRequest = - new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(processTransactionForFileLineRequest, CancellationToken.None); - }); - } - - [Fact] - public void FileRequestHandler_VoucherRequest_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); - } - - - - [Fact] - public void FileRequestHandler_VoucherRequest_FileAggregateNotCreated_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/failed"); - } - - [Fact] - public void FileRequestHandler_VoucherRequest_FileNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); - - Logger.Initialise(NullLogger.Instance); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_VoucherRequest_NoFileProfiles_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileNull); - - this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - Logger.Initialise(NullLogger.Instance); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.Throw(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - } - - [Fact] - public async Task FileRequestHandler_VoucherRequest_ProcessedDirectoryNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); - } - - [Fact] - public async Task FileRequestHandler_VoucherRequest_FailedDirectoryNotFound_RequestIsHandled() - { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); + Should.NotThrow(async () => { + await this.FileRequestHandler.Handle(TestData.ProcessTransactionForFileLineRequest, CancellationToken.None); + }); } - [Fact] - public void FileRequestHandler_VoucherRequest_FileIsEmpty_RequestIsHandled() + public async Task FileRequestHandler_VoucherRequest_RequestHandled() { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileVoucher); - - this.FileAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetCreatedFileAggregate); - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData(String.Empty)); - - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/inprogress"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/processed"); - this.FileSystem.AddDirectory("home/txnproc/bulkfiles/voucher/failed"); - - VoucherRequest voucherRequest = - new VoucherRequest(TestData.FileId, TestData.FilePathWithName, TestData.FileProfileId); - - Should.NotThrow(async () => - { - await this.FileRequestHandler.Handle(voucherRequest, CancellationToken.None); - }); - - this.FileAggregateRepository.Verify(f => f.SaveChanges(It.IsAny(), It.IsAny()), Times.Never); - this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); - } - - private void VerifyFileProcessing(String filePath) - { - IDirectoryInfo directoryInfo = this.FileSystem.DirectoryInfo.FromDirectoryName(filePath); - directoryInfo.GetFiles("*.*").Length.ShouldBe(1); + Should.NotThrow(async () => { + await this.FileRequestHandler.Handle(TestData.VoucherRequest, CancellationToken.None); + }); } } } diff --git a/FileProcessor.BusinessLogic.Tests/MediatorTests.cs b/FileProcessor.BusinessLogic.Tests/MediatorTests.cs new file mode 100644 index 0000000..b57a84e --- /dev/null +++ b/FileProcessor.BusinessLogic.Tests/MediatorTests.cs @@ -0,0 +1,108 @@ +using FileProcessor.Testing; +using MediatR; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Moq; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace FileProcessor.BusinessLogic.Tests +{ + using Lamar; + using Microsoft.Extensions.DependencyInjection; + using Services; + + public class MediatorTests + { + private List Requests = new List(); + + public MediatorTests() + { + this.Requests.Add(TestData.UploadFileRequest); + this.Requests.Add(TestData.ProcessUploadedFileRequest); + this.Requests.Add(TestData.SafaricomTopupRequest); + this.Requests.Add(TestData.VoucherRequest); + this.Requests.Add(TestData.ProcessTransactionForFileLineRequest); + } + + [Fact] + public async Task Mediator_Send_RequestHandled() + { + Mock hostingEnvironment = new Mock(); + hostingEnvironment.Setup(he => he.EnvironmentName).Returns("Development"); + hostingEnvironment.Setup(he => he.ContentRootPath).Returns("/home"); + hostingEnvironment.Setup(he => he.ApplicationName).Returns("Test Application"); + + ServiceRegistry services = new ServiceRegistry(); + Startup s = new Startup(hostingEnvironment.Object); + Startup.Configuration = this.SetupMemoryConfiguration(); + + this.AddTestRegistrations(services, hostingEnvironment.Object); + s.ConfigureContainer(services); + Startup.Container.AssertConfigurationIsValid(AssertMode.Full); + + List errors = new List(); + IMediator mediator = Startup.Container.GetService(); + foreach (IBaseRequest baseRequest in this.Requests) + { + try + { + await mediator.Send(baseRequest); + } + catch (Exception ex) + { + errors.Add(ex.Message); + } + } + + if (errors.Any() == true) + { + String errorMessage = String.Join(Environment.NewLine, errors); + throw new Exception(errorMessage); + } + } + + private IConfigurationRoot SetupMemoryConfiguration() + { + IConfigurationBuilder builder = new ConfigurationBuilder(); + + //configuration.Add("ConnectionStrings:HealthCheck", "HeathCheckConnString"); + //configuration.Add("SecurityConfiguration:Authority", "https://127.0.0.1"); + //configuration.Add("EventStoreSettings:ConnectionString", "https://127.0.0.1:2113"); + //configuration.Add("EventStoreSettings:ConnectionName", "UnitTestConnection"); + //configuration.Add("EventStoreSettings:UserName", "admin"); + //configuration.Add("EventStoreSettings:Password", "changeit"); + //configuration.Add("AppSettings:UseConnectionStringConfig", "false"); + //configuration.Add("AppSettings:SecurityService", "http://127.0.0.1"); + //configuration.Add("AppSettings:MessagingServiceApi", "http://127.0.0.1"); + //configuration.Add("AppSettings:TransactionProcessorApi", "http://127.0.0.1"); + //configuration.Add("AppSettings:DatabaseEngine", "SqlServer"); + //configuration.Add("AppSettings:EmailProxy", "UnitTest"); + //configuration.Add("AppSettings:SMSProxy", "UnitTest"); + + builder.AddInMemoryCollection(TestData.DefaultAppSettings); + + return builder.Build(); + } + + private void AddTestRegistrations(ServiceRegistry services, + IWebHostEnvironment hostingEnvironment) + { + services.AddLogging(); + DiagnosticListener diagnosticSource = new DiagnosticListener(hostingEnvironment.ApplicationName); + services.AddSingleton(diagnosticSource); + services.AddSingleton(diagnosticSource); + services.AddSingleton(hostingEnvironment); + services.AddSingleton(hostingEnvironment); + services.AddSingleton(Startup.Configuration); + + services.OverrideServices(s => { s.AddSingleton(); }); + } + } +} diff --git a/FileProcessor.BusinessLogic.Tests/RequestTests.cs b/FileProcessor.BusinessLogic.Tests/RequestTests.cs index a0e8ee3..4a296d1 100644 --- a/FileProcessor.BusinessLogic.Tests/RequestTests.cs +++ b/FileProcessor.BusinessLogic.Tests/RequestTests.cs @@ -12,7 +12,7 @@ public class RequestTests [Fact] public void UploadFileRequest_CanBeCreated_IsCreated() { - UploadFileRequest uploadFileRequest = + UploadFileRequest uploadFileRequest = new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePath, TestData.FileProfileId, TestData.FileUploadedDateTime); uploadFileRequest.EstateId.ShouldBe(TestData.EstateId); diff --git a/FileProcessor.BusinessLogic/Common/GuidCalculator.cs b/FileProcessor.BusinessLogic/Common/GuidCalculator.cs index b1d25a9..7438760 100644 --- a/FileProcessor.BusinessLogic/Common/GuidCalculator.cs +++ b/FileProcessor.BusinessLogic/Common/GuidCalculator.cs @@ -1,7 +1,9 @@ namespace FileProcessor.BusinessLogic.Common { using System; + using System.Diagnostics.CodeAnalysis; + [ExcludeFromCodeCoverage] public static class GuidCalculator { #region Methods diff --git a/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs b/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs index 709ba19..2d98054 100644 --- a/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs +++ b/FileProcessor.BusinessLogic/RequestHandlers/FileRequestHandler.cs @@ -25,6 +25,7 @@ namespace FileProcessor.BusinessLogic.RequestHandlers using Requests; using SecurityService.Client; using SecurityService.DataTransferObjects.Responses; + using Services; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.Aggregate; using Shared.Exceptions; @@ -46,546 +47,43 @@ public class FileRequestHandler : IRequestHandler, IRequestHandler, IRequestHandler { - /// - /// The file processor manager - /// - private readonly IFileProcessorManager FileProcessorManager; + private readonly IFileProcessorDomainService DomainService; - private readonly IAggregateRepository FileImportLogAggregateRepository; - - /// - /// The file aggregate repository - /// - private readonly IAggregateRepository FileAggregateRepository; - - /// - /// The transaction processor client - /// - private readonly ITransactionProcessorClient TransactionProcessorClient; - - /// - /// The estate client - /// - private readonly IEstateClient EstateClient; - - /// - /// The security service client - /// - private readonly ISecurityServiceClient SecurityServiceClient; - - /// - /// The file format handler resolver - /// - private readonly Func FileFormatHandlerResolver; - - /// - /// The file system - /// - private readonly IFileSystem FileSystem; - - /// - /// Initializes a new instance of the class. - /// - /// The file processor manager. - /// The file aggregate repository. - /// The transaction processor client. - /// The estate client. - /// The security service client. - /// The file format handler resolver. - /// The file system. - public FileRequestHandler(IFileProcessorManager fileProcessorManager, - IAggregateRepository fileImportLogAggregateRepository, - IAggregateRepository fileAggregateRepository, - ITransactionProcessorClient transactionProcessorClient, - IEstateClient estateClient, - ISecurityServiceClient securityServiceClient, - Func fileFormatHandlerResolver, - IFileSystem fileSystem) + public FileRequestHandler(IFileProcessorDomainService domainService) { - this.FileProcessorManager = fileProcessorManager; - this.FileImportLogAggregateRepository = fileImportLogAggregateRepository; - this.FileAggregateRepository = fileAggregateRepository; - this.TransactionProcessorClient = transactionProcessorClient; - this.EstateClient = estateClient; - this.SecurityServiceClient = securityServiceClient; - this.FileFormatHandlerResolver = fileFormatHandlerResolver; - this.FileSystem = fileSystem; + this.DomainService = domainService; } - - // TODO: Create a domain service - - /// - /// Handles the specified request. - /// - /// The request. - /// The cancellation token. - /// - /// No file profile found with Id {request.FileProfileId} - /// File {file.FullName} not found - /// Directory {fileProfile.ListeningDirectory} not found + public async Task Handle(UploadFileRequest request, - CancellationToken cancellationToken) - { - DateTime importLogDateTime = request.FileUploadedDateTime; - - // This will now create the import log and add an event for the file being uploaded - Guid importLogId = Helpers.CalculateFileImportLogAggregateId(importLogDateTime.Date, request.EstateId); - - // Get the import log - FileImportLogAggregate fileImportLogAggregate = await this.FileImportLogAggregateRepository.GetLatestVersion(importLogId, cancellationToken); - - if (fileImportLogAggregate.IsCreated == false) - { - // First file of the day so create - fileImportLogAggregate.CreateImportLog(request.EstateId, importLogDateTime); - } - - // Move the file - FileProfile fileProfile = await this.FileProcessorManager.GetFileProfile(request.FileProfileId, cancellationToken); - - if (fileProfile == null) - { - throw new NotFoundException($"No file profile found with Id {request.FileProfileId}"); - } - - // Copy file from the temp location to file processing listening directory - IFileInfo file = this.FileSystem.FileInfo.FromFileName(request.FilePath); - if (file.Exists == false) - { - throw new FileNotFoundException($"File {file.FullName} not found"); - } - String originalName = file.Name; - - if (this.FileSystem.Directory.Exists(fileProfile.ListeningDirectory) == false) - { - throw new DirectoryNotFoundException($"Directory {fileProfile.ListeningDirectory} not found"); - } - - // Read the file data - String fileContent = null; - //Open file for Read\Write - using (Stream fs = file.Open(FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) - { - //Create object of StreamReader by passing FileStream object on which it needs to operates on - using (StreamReader sr = new StreamReader(fs)) - { - //Use ReadToEnd method to read all the content from file - fileContent = await sr.ReadToEndAsync(); - } - } - - Guid fileId = this.CreateGuidFromFileData(fileContent); - - String fileDestination = $"{fileProfile.ListeningDirectory}//{request.EstateId:N}-{fileId:N}"; - file.MoveTo(fileDestination, overwrite: true); - - // Update Import log aggregate - fileImportLogAggregate.AddImportedFile(fileId, request.MerchantId, request.UserId, request.FileProfileId, originalName, fileDestination, request.FileUploadedDateTime); - - // Save changes - await this.FileImportLogAggregateRepository.SaveChanges(fileImportLogAggregate, cancellationToken); - - return fileId; - } - - private Guid CreateGuidFromFileData(String fileContents) - { - using (SHA256 sha256Hash = SHA256.Create()) - { - //Generate hash from the key - Byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(fileContents)); - - Byte[] j = bytes.Skip(Math.Max(0, bytes.Count() - 16)).ToArray(); //Take last 16 - - //Create our Guid. - return new Guid(j); - } + CancellationToken cancellationToken) { + return await this.DomainService.UploadFile(request, cancellationToken); } public async Task Handle(ProcessUploadedFileRequest request, CancellationToken cancellationToken) { - // TODO: Should the file id be generated from the file uploaded to protect against duplicate files??? - FileAggregate fileAggregate = await this.FileAggregateRepository.GetLatestVersion(request.FileId, cancellationToken); - - fileAggregate.CreateFile(request.FileImportLogId, request.EstateId, request.MerchantId, request.UserId, request.FileProfileId, request.FilePath, request.FileUploadedDateTime); - - await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + await this.DomainService.ProcessUploadedFile(request, cancellationToken); return new Unit(); } - - /// - /// Handles the specified request. - /// - /// The request. - /// The cancellation token. - /// - /// File {file.FullName} not found - /// No file profile found with Id {request.FileProfileId} - /// - /// Directory {inProgressFolder} not found - /// or - /// Directory {fileProfile.ProcessedDirectory} not found - /// or - /// Directory {fileProfile.FailedDirectory} not found - /// public async Task Handle(SafaricomTopupRequest request, - CancellationToken cancellationToken) - { - return await this.ProcessFile(request.FileId,request.FileProfileId,request.FileName, cancellationToken); - } - - private async Task ProcessFile(Guid fileId, - Guid fileProfileId, - String fileName, - CancellationToken cancellationToken) - { - IFileInfo inProgressFile = null; - FileProfile fileProfile = null; - try - { - fileProfile = await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); - - if (fileProfile == null) - { - throw new NotFoundException($"No file profile found with Id {fileProfileId}"); - } - - // Check the processed/failed directories exist - if (this.FileSystem.Directory.Exists(fileProfile.ProcessedDirectory) == false) - { - Logger.LogWarning($"Creating Directory {fileProfile.ProcessedDirectory} as not found"); - this.FileSystem.Directory.CreateDirectory(fileProfile.ProcessedDirectory); - } - - if (this.FileSystem.Directory.Exists(fileProfile.FailedDirectory) == false) - { - Logger.LogWarning($"Creating Directory {fileProfile.FailedDirectory} as not found"); - this.FileSystem.Directory.CreateDirectory(fileProfile.FailedDirectory); - } - - inProgressFile = this.FileSystem.FileInfo.FromFileName(fileName); - - if (inProgressFile.Exists == false) - { - throw new FileNotFoundException($"File {inProgressFile.FullName} not found"); - } - - FileAggregate fileAggregate = - await this.FileAggregateRepository.GetLatestVersion(fileId, cancellationToken); - - if (fileAggregate.IsCreated == false) - { - throw new InvalidOperationException($"File with Id {fileId} not created"); - } - - String fileContent = null; - //Open file for Read\Write - using (Stream fs = inProgressFile.Open(FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) - { - //Create object of StreamReader by passing FileStream object on which it needs to operates on - using (StreamReader sr = new StreamReader(fs)) - { - //Use ReadToEnd method to read all the content from file - fileContent = await sr.ReadToEndAsync(); - } - } - - if (String.IsNullOrEmpty(fileContent) == false) - { - String[] fileLines = fileContent.Split(fileProfile.LineTerminator); - - foreach (String fileLine in fileLines) - { - fileAggregate.AddFileLine(fileLine.Trim()); - } - - await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); - } - - Logger.LogInformation( - $"About to move file {inProgressFile.Name} to [{fileProfile.ProcessedDirectory}]"); - - // TODO: Move file now - inProgressFile.MoveTo($"{fileProfile.ProcessedDirectory}/{inProgressFile.Name}"); + CancellationToken cancellationToken) { + await this.DomainService.ProcessSafaricomTopup(request, cancellationToken); - return new Unit(); - } - catch (Exception e) - { - if (inProgressFile != null && fileProfile != null) - inProgressFile.MoveTo($"{fileProfile.FailedDirectory}/{inProgressFile.Name}"); - - Logger.LogError(e); - throw; - } + return new Unit(); } - - /// - /// The transaction number - /// - private static Int32 TransactionNumber = 0; - - /// - /// Handles the specified request. - /// - /// The request. - /// The cancellation token. - /// - /// File Id [{request.FileId}] has no lines added - /// - /// File Line Number {request.LineNumber} not found in File Id {request.FileId} - /// or - /// No file profile found with Id {fileDetails.FileProfileId} - /// or - /// Merchant not found with Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId} - /// or - /// No contracts found for Merchant Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId} - /// or - /// No merchant contract for operator Id {fileProfile.OperatorName} found for Merchant Id {merchant.MerchantId} - /// or - /// No variable value product found on the merchant contract for operator Id {fileProfile.OperatorName} and Merchant Id {merchant.MerchantId} - /// + public async Task Handle(ProcessTransactionForFileLineRequest request, - CancellationToken cancellationToken) - { - // Get the file aggregate, this tells us the file profile information - FileAggregate fileAggregate = await this.FileAggregateRepository.GetLatestVersion(request.FileId, cancellationToken); - - FileDetails fileDetails = fileAggregate.GetFile(); - - if (fileDetails.FileLines.Any() == false) - { - throw new NotSupportedException($"File Id [{request.FileId}] has no lines added"); - } - - FileLine fileLine = fileDetails.FileLines.SingleOrDefault(f => f.LineNumber == request.LineNumber); - - if (fileLine == null) - { - throw new NotFoundException($"File Line Number {request.LineNumber} not found in File Id {request.FileId}"); - } - - if (fileLine.ProcessingResult != ProcessingResult.NotProcessed) - { - // Line already processed - return new Unit(); - } - - FileProfile fileProfile = await this.FileProcessorManager.GetFileProfile(fileDetails.FileProfileId, cancellationToken); - - if (fileProfile == null) - { - throw new NotFoundException($"No file profile found with Id {fileDetails.FileProfileId}"); - } - - // Determine if we need to actually process this file line - if (this.FileLineCanBeIgnored(fileLine.LineData, fileProfile.FileFormatHandler)) - { - // Write something to aggregate to say line was explicity ignored - fileAggregate.RecordFileLineAsIgnored(fileLine.LineNumber); - await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); - return new Unit(); - } - - // need to now parse the line (based on the file format), this builds the metadata - Dictionary transactionMetadata = this.ParseFileLine(fileLine.LineData, fileProfile.FileFormatHandler); - - if (transactionMetadata == null) - { - // Line failed to parse so record this - fileAggregate.RecordFileLineAsRejected(fileLine.LineNumber, "Invalid Format"); - await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); - return new Unit(); - } - - // Add the file data to the request metadata - transactionMetadata.Add("FileId", request.FileId.ToString()); - transactionMetadata.Add("FileLineNumber", fileLine.LineNumber.ToString()); - - String operatorName = fileProfile.OperatorName; - if (transactionMetadata.ContainsKey("OperatorName")) - { - // extract the value - operatorName = transactionMetadata["OperatorName"]; - transactionMetadata = transactionMetadata.Where(x => x.Key != "OperatorName").ToDictionary(x => x.Key, x => x.Value); - } - - this.TokenResponse = await this.GetToken(cancellationToken); - - Interlocked.Increment(ref FileRequestHandler.TransactionNumber); - - // Get the merchant details - MerchantResponse merchant = await this.EstateClient.GetMerchant(this.TokenResponse.AccessToken, fileDetails.EstateId, fileDetails.MerchantId, cancellationToken); - if (merchant == null) - { - throw new NotFoundException($"Merchant not found with Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId}"); - } - List contracts = await this.EstateClient.GetMerchantContracts(this.TokenResponse.AccessToken, fileDetails.EstateId, fileDetails.MerchantId, cancellationToken); - - if (contracts.Any() == false) - { - throw new NotFoundException($"No contracts found for Merchant Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId}"); - } - - ContractResponse? contract = null; - if (fileProfile.OperatorName == "Voucher") - { - contract = contracts.SingleOrDefault(c => c.Description.Contains(operatorName)); - } - else - { - contract = contracts.SingleOrDefault(c => c.OperatorName == operatorName); - } - - - if (contract == null) - { - throw new NotFoundException($"No merchant contract for operator Id {operatorName} found for Merchant Id {merchant.MerchantId}"); - } - - ContractProduct? product = contract.Products.SingleOrDefault(p => p.Value == null); // TODO: Is this enough or should the name be used and stored in file profile?? - - if (product == null) - { - throw new NotFoundException($"No variable value product found on the merchant contract for operator Id {fileProfile.OperatorName} and Merchant Id {merchant.MerchantId}"); - } - - // Build a transaction request message - SaleTransactionRequest saleTransactionRequest = new SaleTransactionRequest - { - - EstateId = fileDetails.EstateId, - MerchantId = fileDetails.MerchantId, - TransactionDateTime = fileDetails.FileReceivedDateTime, - TransactionNumber = FileRequestHandler.TransactionNumber.ToString(), - TransactionType = "Sale", - ContractId = contract.ContractId, - DeviceIdentifier = merchant.Devices.First().Value, - OperatorIdentifier = contract.OperatorName, - ProductId = product.ProductId, - AdditionalTransactionMetadata = transactionMetadata, - TransactionSource = 2 // File based transaction - }; - - SerialisedMessage serialisedRequestMessage = new SerialisedMessage - { - Metadata = new Dictionary - { - {"estate_id", fileDetails.EstateId.ToString()}, - {"merchant_id", fileDetails.MerchantId.ToString()} - }, - SerialisedData = JsonConvert.SerializeObject(saleTransactionRequest, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }) - }; - - Logger.LogInformation(serialisedRequestMessage.SerialisedData); - - // Send request to transaction processor - SerialisedMessage serialisedResponseMessage = await this.TransactionProcessorClient.PerformTransaction(this.TokenResponse.AccessToken, serialisedRequestMessage, cancellationToken); - - // Get the sale transaction response - SaleTransactionResponse saleTransactionResponse = JsonConvert.DeserializeObject(serialisedResponseMessage.SerialisedData); - - if (saleTransactionResponse.ResponseCode == "0000") - { - // record response against file line in file aggregate - fileAggregate.RecordFileLineAsSuccessful(request.LineNumber, saleTransactionResponse.TransactionId); - } - else - { - fileAggregate.RecordFileLineAsFailed(request.LineNumber, saleTransactionResponse.TransactionId, saleTransactionResponse.ResponseCode, saleTransactionResponse.ResponseMessage); - } - - // Save changes to file aggregate - // TODO: Add retry round this save (maybe 3 retries) - await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); - + CancellationToken cancellationToken) { + await this.DomainService.ProcessTransactionForFileLine(request, cancellationToken); return new Unit(); } - - /// - /// Files the line can be ignored. - /// - /// The domain event file line. - /// The file profile file format handler. - /// - private Boolean FileLineCanBeIgnored(String domainEventFileLine, - String fileProfileFileFormatHandler) - { - // Ignore empty files - if (String.IsNullOrEmpty(domainEventFileLine)) - return true; - - IFileFormatHandler fileFormatHandler = this.FileFormatHandlerResolver(fileProfileFileFormatHandler); - - return fileFormatHandler.FileLineCanBeIgnored(domainEventFileLine); - } - - /// - /// Parses the file line. - /// - /// The domain event file line. - /// The file profile file format handler. - /// - private Dictionary ParseFileLine(String domainEventFileLine, - String fileProfileFileFormatHandler) - { - try - { - IFileFormatHandler fileFormatHandler = this.FileFormatHandlerResolver(fileProfileFileFormatHandler); - - return fileFormatHandler.ParseFileLine(domainEventFileLine); - } - catch(InvalidDataException iex) - { - Logger.LogWarning(iex.Message); - return null; - } - } - - /// - /// The token response - /// - private TokenResponse TokenResponse; - - /// - /// Gets the token. - /// - /// The cancellation token. - /// - private async Task GetToken(CancellationToken cancellationToken) - { - // Get a token to talk to the estate service - String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); - String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); - Logger.LogInformation($"Client Id is {clientId}"); - Logger.LogInformation($"Client Secret is {clientSecret}"); - - if (this.TokenResponse == null) - { - TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); - Logger.LogInformation($"Token is {token.AccessToken}"); - return token; - } - - if (this.TokenResponse.Expires.UtcDateTime.Subtract(DateTime.UtcNow) < TimeSpan.FromMinutes(2)) - { - Logger.LogInformation($"Token is about to expire at {this.TokenResponse.Expires.DateTime:O}"); - TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); - Logger.LogInformation($"Token is {token.AccessToken}"); - return token; - } - - return this.TokenResponse; - } - + public async Task Handle(VoucherRequest request, CancellationToken cancellationToken) { - return await this.ProcessFile(request.FileId, request.FileProfileId, request.FileName, cancellationToken); + await this.DomainService.ProcessVoucher(request, cancellationToken); + + return new Unit(); } } } diff --git a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs new file mode 100644 index 0000000..2b33e24 --- /dev/null +++ b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs @@ -0,0 +1,464 @@ +namespace FileProcessor.BusinessLogic.Services; + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Common; +using EstateManagement.Client; +using EstateManagement.DataTransferObjects.Responses; +using FileAggregate; +using FileFormatHandlers; +using FileImportLogAggregate; +using FIleProcessor.Models; +using Managers; +using Newtonsoft.Json; +using Requests; +using SecurityService.Client; +using SecurityService.DataTransferObjects.Responses; +using Shared.DomainDrivenDesign.EventSourcing; +using Shared.EventStore.Aggregate; +using Shared.Exceptions; +using Shared.General; +using Shared.Logger; +using TransactionProcessor.Client; +using TransactionProcessor.DataTransferObjects; + +public class FileProcessorDomainService : IFileProcessorDomainService +{ + private readonly IFileProcessorManager FileProcessorManager; + + private readonly IAggregateRepository FileImportLogAggregateRepository; + + private readonly IAggregateRepository FileAggregateRepository; + + private readonly ITransactionProcessorClient TransactionProcessorClient; + + private readonly IEstateClient EstateClient; + + private readonly ISecurityServiceClient SecurityServiceClient; + + private readonly Func FileFormatHandlerResolver; + + private readonly IFileSystem FileSystem; + + public FileProcessorDomainService(IFileProcessorManager fileProcessorManager, + IAggregateRepository fileImportLogAggregateRepository, + IAggregateRepository fileAggregateRepository, + ITransactionProcessorClient transactionProcessorClient, + IEstateClient estateClient, + ISecurityServiceClient securityServiceClient, + Func fileFormatHandlerResolver, + IFileSystem fileSystem) { + this.FileProcessorManager = fileProcessorManager; + this.FileImportLogAggregateRepository = fileImportLogAggregateRepository; + this.FileAggregateRepository = fileAggregateRepository; + this.TransactionProcessorClient = transactionProcessorClient; + this.EstateClient = estateClient; + this.SecurityServiceClient = securityServiceClient; + this.FileFormatHandlerResolver = fileFormatHandlerResolver; + this.FileSystem = fileSystem; + } + + public async Task UploadFile(UploadFileRequest request, + CancellationToken cancellationToken) { + DateTime importLogDateTime = request.FileUploadedDateTime; + + // This will now create the import log and add an event for the file being uploaded + Guid importLogId = Helpers.CalculateFileImportLogAggregateId(importLogDateTime.Date, request.EstateId); + + // Get the import log + FileImportLogAggregate fileImportLogAggregate = await this.FileImportLogAggregateRepository.GetLatestVersion(importLogId, cancellationToken); + + if (fileImportLogAggregate.IsCreated == false) + { + // First file of the day so create + fileImportLogAggregate.CreateImportLog(request.EstateId, importLogDateTime); + } + + // Move the file + FileProfile fileProfile = await this.FileProcessorManager.GetFileProfile(request.FileProfileId, cancellationToken); + + if (fileProfile == null) + { + throw new NotFoundException($"No file profile found with Id {request.FileProfileId}"); + } + + // Copy file from the temp location to file processing listening directory + IFileInfo file = this.FileSystem.FileInfo.FromFileName(request.FilePath); + if (file.Exists == false) + { + throw new FileNotFoundException($"File {file.FullName} not found"); + } + String originalName = file.Name; + + if (this.FileSystem.Directory.Exists(fileProfile.ListeningDirectory) == false) + { + throw new DirectoryNotFoundException($"Directory {fileProfile.ListeningDirectory} not found"); + } + + // Read the file data + String fileContent = null; + //Open file for Read\Write + using (Stream fs = file.Open(FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) + { + //Create object of StreamReader by passing FileStream object on which it needs to operates on + using (StreamReader sr = new StreamReader(fs)) + { + //Use ReadToEnd method to read all the content from file + fileContent = await sr.ReadToEndAsync(); + } + } + + Guid fileId = this.CreateGuidFromFileData(fileContent); + + String fileDestination = $"{fileProfile.ListeningDirectory}//{request.EstateId:N}-{fileId:N}"; + file.MoveTo(fileDestination, overwrite: true); + + // Update Import log aggregate + fileImportLogAggregate.AddImportedFile(fileId, request.MerchantId, request.UserId, request.FileProfileId, originalName, fileDestination, request.FileUploadedDateTime); + + // Save changes + await this.FileImportLogAggregateRepository.SaveChanges(fileImportLogAggregate, cancellationToken); + + return fileId; + } + + public async Task ProcessUploadedFile(ProcessUploadedFileRequest request, + CancellationToken cancellationToken) { + // TODO: Should the file id be generated from the file uploaded to protect against duplicate files??? + FileAggregate fileAggregate = await this.FileAggregateRepository.GetLatestVersion(request.FileId, cancellationToken); + + fileAggregate.CreateFile(request.FileImportLogId, request.EstateId, request.MerchantId, request.UserId, request.FileProfileId, request.FilePath, request.FileUploadedDateTime); + + await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + } + + public async Task ProcessSafaricomTopup(SafaricomTopupRequest request, + CancellationToken cancellationToken) { + await this.ProcessFile(request.FileId, request.FileProfileId, request.FileName, cancellationToken); + } + + public async Task ProcessVoucher(VoucherRequest request, + CancellationToken cancellationToken) { + await this.ProcessFile(request.FileId, request.FileProfileId, request.FileName, cancellationToken); + } + + public async Task ProcessTransactionForFileLine(ProcessTransactionForFileLineRequest request, + CancellationToken cancellationToken) { + // Get the file aggregate, this tells us the file profile information + FileAggregate fileAggregate = await this.FileAggregateRepository.GetLatestVersion(request.FileId, cancellationToken); + + FileDetails fileDetails = fileAggregate.GetFile(); + + if (fileDetails.FileLines.Any() == false) + { + throw new NotSupportedException($"File Id [{request.FileId}] has no lines added"); + } + + FileLine fileLine = fileDetails.FileLines.SingleOrDefault(f => f.LineNumber == request.LineNumber); + + if (fileLine == null) + { + throw new NotFoundException($"File Line Number {request.LineNumber} not found in File Id {request.FileId}"); + } + + if (fileLine.ProcessingResult != ProcessingResult.NotProcessed) + { + // Line already processed + return; + } + + FileProfile fileProfile = await this.FileProcessorManager.GetFileProfile(fileDetails.FileProfileId, cancellationToken); + + if (fileProfile == null) + { + throw new NotFoundException($"No file profile found with Id {fileDetails.FileProfileId}"); + } + + // Determine if we need to actually process this file line + if (this.FileLineCanBeIgnored(fileLine.LineData, fileProfile.FileFormatHandler)) + { + // Write something to aggregate to say line was explicity ignored + fileAggregate.RecordFileLineAsIgnored(fileLine.LineNumber); + await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + return; + } + + // need to now parse the line (based on the file format), this builds the metadata + Dictionary transactionMetadata = this.ParseFileLine(fileLine.LineData, fileProfile.FileFormatHandler); + + if (transactionMetadata == null) + { + // Line failed to parse so record this + fileAggregate.RecordFileLineAsRejected(fileLine.LineNumber, "Invalid Format"); + await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + return; + } + + // Add the file data to the request metadata + transactionMetadata.Add("FileId", request.FileId.ToString()); + transactionMetadata.Add("FileLineNumber", fileLine.LineNumber.ToString()); + + String operatorName = fileProfile.OperatorName; + if (transactionMetadata.ContainsKey("OperatorName")) + { + // extract the value + operatorName = transactionMetadata["OperatorName"]; + transactionMetadata = transactionMetadata.Where(x => x.Key != "OperatorName").ToDictionary(x => x.Key, x => x.Value); + } + + this.TokenResponse = await this.GetToken(cancellationToken); + + Interlocked.Increment(ref FileProcessorDomainService.TransactionNumber); + + // Get the merchant details + MerchantResponse merchant = await this.EstateClient.GetMerchant(this.TokenResponse.AccessToken, fileDetails.EstateId, fileDetails.MerchantId, cancellationToken); + if (merchant == null) + { + throw new NotFoundException($"Merchant not found with Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId}"); + } + List contracts = await this.EstateClient.GetMerchantContracts(this.TokenResponse.AccessToken, fileDetails.EstateId, fileDetails.MerchantId, cancellationToken); + + if (contracts.Any() == false) + { + throw new NotFoundException($"No contracts found for Merchant Id {fileDetails.MerchantId} on estate Id {fileDetails.EstateId}"); + } + + ContractResponse? contract = null; + if (fileProfile.OperatorName == "Voucher") + { + contract = contracts.SingleOrDefault(c => c.Description.Contains(operatorName)); + } + else + { + contract = contracts.SingleOrDefault(c => c.OperatorName == operatorName); + } + + + if (contract == null) + { + throw new NotFoundException($"No merchant contract for operator Id {operatorName} found for Merchant Id {merchant.MerchantId}"); + } + + ContractProduct? product = contract.Products.SingleOrDefault(p => p.Value == null); // TODO: Is this enough or should the name be used and stored in file profile?? + + if (product == null) + { + throw new NotFoundException($"No variable value product found on the merchant contract for operator Id {fileProfile.OperatorName} and Merchant Id {merchant.MerchantId}"); + } + + // Build a transaction request message + SaleTransactionRequest saleTransactionRequest = new SaleTransactionRequest + { + + EstateId = fileDetails.EstateId, + MerchantId = fileDetails.MerchantId, + TransactionDateTime = fileDetails.FileReceivedDateTime, + TransactionNumber = FileProcessorDomainService.TransactionNumber.ToString(), + TransactionType = "Sale", + ContractId = contract.ContractId, + DeviceIdentifier = merchant.Devices.First().Value, + OperatorIdentifier = contract.OperatorName, + ProductId = product.ProductId, + AdditionalTransactionMetadata = transactionMetadata, + TransactionSource = 2 // File based transaction + }; + + SerialisedMessage serialisedRequestMessage = new SerialisedMessage + { + Metadata = new Dictionary + { + {"estate_id", fileDetails.EstateId.ToString()}, + {"merchant_id", fileDetails.MerchantId.ToString()} + }, + SerialisedData = JsonConvert.SerializeObject(saleTransactionRequest, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }) + }; + + Logger.LogInformation(serialisedRequestMessage.SerialisedData); + + // Send request to transaction processor + SerialisedMessage serialisedResponseMessage = await this.TransactionProcessorClient.PerformTransaction(this.TokenResponse.AccessToken, serialisedRequestMessage, cancellationToken); + + // Get the sale transaction response + SaleTransactionResponse saleTransactionResponse = JsonConvert.DeserializeObject(serialisedResponseMessage.SerialisedData); + + if (saleTransactionResponse.ResponseCode == "0000") + { + // record response against file line in file aggregate + fileAggregate.RecordFileLineAsSuccessful(request.LineNumber, saleTransactionResponse.TransactionId); + } + else + { + fileAggregate.RecordFileLineAsFailed(request.LineNumber, saleTransactionResponse.TransactionId, saleTransactionResponse.ResponseCode, saleTransactionResponse.ResponseMessage); + } + + // Save changes to file aggregate + // TODO: Add retry round this save (maybe 3 retries) + await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + } + + private async Task ProcessFile(Guid fileId, + Guid fileProfileId, + String fileName, + CancellationToken cancellationToken) + { + IFileInfo inProgressFile = null; + FileProfile fileProfile = null; + try + { + fileProfile = await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); + + if (fileProfile == null) + { + throw new NotFoundException($"No file profile found with Id {fileProfileId}"); + } + + // Check the processed/failed directories exist + if (this.FileSystem.Directory.Exists(fileProfile.ProcessedDirectory) == false) + { + Logger.LogWarning($"Creating Directory {fileProfile.ProcessedDirectory} as not found"); + this.FileSystem.Directory.CreateDirectory(fileProfile.ProcessedDirectory); + } + + if (this.FileSystem.Directory.Exists(fileProfile.FailedDirectory) == false) + { + Logger.LogWarning($"Creating Directory {fileProfile.FailedDirectory} as not found"); + this.FileSystem.Directory.CreateDirectory(fileProfile.FailedDirectory); + } + + inProgressFile = this.FileSystem.FileInfo.FromFileName(fileName); + + if (inProgressFile.Exists == false) + { + throw new FileNotFoundException($"File {inProgressFile.FullName} not found"); + } + + FileAggregate fileAggregate = + await this.FileAggregateRepository.GetLatestVersion(fileId, cancellationToken); + + if (fileAggregate.IsCreated == false) + { + throw new InvalidOperationException($"File with Id {fileId} not created"); + } + + String fileContent = null; + //Open file for Read\Write + using (Stream fs = inProgressFile.Open(FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) + { + //Create object of StreamReader by passing FileStream object on which it needs to operates on + using (StreamReader sr = new StreamReader(fs)) + { + //Use ReadToEnd method to read all the content from file + fileContent = await sr.ReadToEndAsync(); + } + } + + if (String.IsNullOrEmpty(fileContent) == false) + { + String[] fileLines = fileContent.Split(fileProfile.LineTerminator); + + foreach (String fileLine in fileLines) + { + fileAggregate.AddFileLine(fileLine.Trim()); + } + + await this.FileAggregateRepository.SaveChanges(fileAggregate, cancellationToken); + } + + Logger.LogInformation( + $"About to move file {inProgressFile.Name} to [{fileProfile.ProcessedDirectory}]"); + + // TODO: Move file now + inProgressFile.MoveTo($"{fileProfile.ProcessedDirectory}/{inProgressFile.Name}"); + } + catch (Exception e) + { + if (inProgressFile != null && fileProfile != null) + inProgressFile.MoveTo($"{fileProfile.FailedDirectory}/{inProgressFile.Name}"); + + Logger.LogError(e); + throw; + } + } + + private static Int32 TransactionNumber = 0; + + private Guid CreateGuidFromFileData(String fileContents) + { + using (SHA256 sha256Hash = SHA256.Create()) + { + //Generate hash from the key + Byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(fileContents)); + + Byte[] j = bytes.Skip(Math.Max(0, bytes.Count() - 16)).ToArray(); //Take last 16 + + //Create our Guid. + return new Guid(j); + } + } + + private Boolean FileLineCanBeIgnored(String domainEventFileLine, + String fileProfileFileFormatHandler) + { + // Ignore empty files + if (String.IsNullOrEmpty(domainEventFileLine)) + return true; + + IFileFormatHandler fileFormatHandler = this.FileFormatHandlerResolver(fileProfileFileFormatHandler); + + return fileFormatHandler.FileLineCanBeIgnored(domainEventFileLine); + } + + private Dictionary ParseFileLine(String domainEventFileLine, + String fileProfileFileFormatHandler) + { + try + { + IFileFormatHandler fileFormatHandler = this.FileFormatHandlerResolver(fileProfileFileFormatHandler); + + return fileFormatHandler.ParseFileLine(domainEventFileLine); + } + catch (InvalidDataException iex) + { + Logger.LogWarning(iex.Message); + return null; + } + } + + private TokenResponse TokenResponse; + + private async Task GetToken(CancellationToken cancellationToken) + { + // Get a token to talk to the estate service + String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); + String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); + Logger.LogInformation($"Client Id is {clientId}"); + Logger.LogInformation($"Client Secret is {clientSecret}"); + + if (this.TokenResponse == null) + { + TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + Logger.LogInformation($"Token is {token.AccessToken}"); + return token; + } + + if (this.TokenResponse.Expires.UtcDateTime.Subtract(DateTime.UtcNow) < TimeSpan.FromMinutes(2)) + { + Logger.LogInformation($"Token is about to expire at {this.TokenResponse.Expires.DateTime:O}"); + TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + Logger.LogInformation($"Token is {token.AccessToken}"); + return token; + } + + return this.TokenResponse; + } +} \ No newline at end of file diff --git a/FileProcessor.BusinessLogic/Services/IFileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/IFileProcessorDomainService.cs new file mode 100644 index 0000000..31fc767 --- /dev/null +++ b/FileProcessor.BusinessLogic/Services/IFileProcessorDomainService.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; + +namespace FileProcessor.BusinessLogic.Services +{ + using System.Threading; + using FileProcessor.BusinessLogic.RequestHandlers; + using MediatR; + using Requests; + + public interface IFileProcessorDomainService + { + Task UploadFile(UploadFileRequest request, CancellationToken cancellationToken); + + Task ProcessUploadedFile(ProcessUploadedFileRequest request, + CancellationToken cancellationToken); + + Task ProcessSafaricomTopup(SafaricomTopupRequest request, + CancellationToken cancellationToken); + + Task ProcessVoucher(VoucherRequest request, CancellationToken cancellationToken); + + Task ProcessTransactionForFileLine(ProcessTransactionForFileLineRequest request, + CancellationToken cancellationToken); + } +} diff --git a/FileProcessor.Testing/FileProcessor.Testing.csproj b/FileProcessor.Testing/FileProcessor.Testing.csproj index b6f437b..1646285 100644 --- a/FileProcessor.Testing/FileProcessor.Testing.csproj +++ b/FileProcessor.Testing/FileProcessor.Testing.csproj @@ -2,7 +2,7 @@ net7.0 - Full + None diff --git a/FileProcessor.Testing/TestData.cs b/FileProcessor.Testing/TestData.cs index c184f3b..648d6b7 100644 --- a/FileProcessor.Testing/TestData.cs +++ b/FileProcessor.Testing/TestData.cs @@ -4,6 +4,7 @@ namespace FileProcessor.Testing { using System.Collections.Generic; using BusinessLogic.Managers; + using BusinessLogic.Requests; using EstateManagement.DataTransferObjects.Responses; using EstateReporting.Database.Entities; using File.DomainEvents; @@ -787,5 +788,20 @@ public static List GetMerchantContractsResponseNoNullValueProd }, }; + + public static UploadFileRequest UploadFileRequest => + new UploadFileRequest(TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FilePath, TestData.FileProfileId, TestData.FileUploadedDateTime); + + public static ProcessUploadedFileRequest ProcessUploadedFileRequest => + new ProcessUploadedFileRequest(TestData.EstateId, TestData.MerchantId, TestData.FileImportLogId, TestData.FileId, TestData.UserId, TestData.FilePath, TestData.FileProfileId, + TestData.FileUploadedDateTime); + + public static SafaricomTopupRequest SafaricomTopupRequest => new SafaricomTopupRequest(TestData.FileId, TestData.FileName, TestData.FileProfileId); + + public static VoucherRequest VoucherRequest => new VoucherRequest(TestData.FileId, TestData.FileName, TestData.FileProfileId); + + public static ProcessTransactionForFileLineRequest ProcessTransactionForFileLineRequest => + new ProcessTransactionForFileLineRequest(TestData.FileId, TestData.LineNumber, TestData.FileLine); + } } diff --git a/FileProcessor.Tests/BootstrapperTests.cs b/FileProcessor.Tests/BootstrapperTests.cs index 4584dba..6262670 100644 --- a/FileProcessor.Tests/BootstrapperTests.cs +++ b/FileProcessor.Tests/BootstrapperTests.cs @@ -16,7 +16,7 @@ namespace FileProcessor.Tests public class BootstrapperTests { - [Fact(Skip = "Needs investigation")] + [Fact] public void VerifyBootstrapperIsValid() { Mock hostingEnvironment = new Mock(); @@ -52,6 +52,7 @@ private void AddTestRegistrations(ServiceRegistry services, services.AddSingleton(diagnosticSource); services.AddSingleton(hostingEnvironment); services.AddSingleton(hostingEnvironment); + services.AddSingleton(Startup.Configuration); } } } \ No newline at end of file diff --git a/FileProcessor.Tests/FileProcessor.Tests.csproj b/FileProcessor.Tests/FileProcessor.Tests.csproj index e534f90..fd20220 100644 --- a/FileProcessor.Tests/FileProcessor.Tests.csproj +++ b/FileProcessor.Tests/FileProcessor.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - Full + None false diff --git a/FileProcessor.sln b/FileProcessor.sln index b087cae..0893835 100644 --- a/FileProcessor.sln +++ b/FileProcessor.sln @@ -39,15 +39,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.Tests", "File EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.Client", "FileProcessor.Client\FileProcessor.Client.csproj", "{958AD704-4785-49B6-A90E-FEA2F63D92AC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9E93C30A-B086-41C9-ACDA-6B94BA769CD1}" - ProjectSection(SolutionItems) = preProject - .github\workflows\buildwindowsimage.yml = .github\workflows\buildwindowsimage.yml - .github\workflows\createrelease.yml = .github\workflows\createrelease.yml - .github\workflows\nightlybuild.yml = .github\workflows\nightlybuild.yml - .github\workflows\pullrequest.yml = .github\workflows\pullrequest.yml - .github\workflows\pushtomain.yml = .github\workflows\pushtomain.yml - EndProjectSection -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/FileProcessor/Bootstrapper/ClientRegistry.cs b/FileProcessor/Bootstrapper/ClientRegistry.cs index fef8afd..3c7492e 100644 --- a/FileProcessor/Bootstrapper/ClientRegistry.cs +++ b/FileProcessor/Bootstrapper/ClientRegistry.cs @@ -1,6 +1,7 @@ namespace FileProcessor.Bootstrapper; using System; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using EstateManagement.Client; using Lamar; @@ -9,10 +10,7 @@ using Shared.General; using TransactionProcessor.Client; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class ClientRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Bootstrapper/DomainEventHandlerRegistry.cs b/FileProcessor/Bootstrapper/DomainEventHandlerRegistry.cs index 3210f10..a048873 100644 --- a/FileProcessor/Bootstrapper/DomainEventHandlerRegistry.cs +++ b/FileProcessor/Bootstrapper/DomainEventHandlerRegistry.cs @@ -2,16 +2,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using BusinessLogic.EventHandling; using Lamar; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Shared.EventStore.EventHandling; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class DomainEventHandlerRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Bootstrapper/FileRegistry.cs b/FileProcessor/Bootstrapper/FileRegistry.cs index a2afd60..a9855ef 100644 --- a/FileProcessor/Bootstrapper/FileRegistry.cs +++ b/FileProcessor/Bootstrapper/FileRegistry.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using System.Linq; using BusinessLogic.FileFormatHandlers; @@ -10,10 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class FileRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Bootstrapper/MediatorRegistry.cs b/FileProcessor/Bootstrapper/MediatorRegistry.cs index 3ec354c..bb91c48 100644 --- a/FileProcessor/Bootstrapper/MediatorRegistry.cs +++ b/FileProcessor/Bootstrapper/MediatorRegistry.cs @@ -1,16 +1,14 @@ namespace FileProcessor.Bootstrapper; using System; +using System.Diagnostics.CodeAnalysis; using BusinessLogic.RequestHandlers; using BusinessLogic.Requests; using Lamar; using MediatR; using Microsoft.Extensions.DependencyInjection; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class MediatorRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Bootstrapper/MiddlewareRegistry.cs b/FileProcessor/Bootstrapper/MiddlewareRegistry.cs index 5f47c00..806c647 100644 --- a/FileProcessor/Bootstrapper/MiddlewareRegistry.cs +++ b/FileProcessor/Bootstrapper/MiddlewareRegistry.cs @@ -1,6 +1,7 @@ namespace FileProcessor.Bootstrapper { using System; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Net.Http; @@ -17,10 +18,7 @@ using Shared.Extensions; using Shared.General; - /// - /// - /// - /// + [ExcludeFromCodeCoverage] public class MiddlewareRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Bootstrapper/MiscRegistry.cs b/FileProcessor/Bootstrapper/MiscRegistry.cs index 4804c76..1496005 100644 --- a/FileProcessor/Bootstrapper/MiscRegistry.cs +++ b/FileProcessor/Bootstrapper/MiscRegistry.cs @@ -1,13 +1,12 @@ namespace FileProcessor.Bootstrapper; using BusinessLogic.Common; +using BusinessLogic.Services; using Lamar; using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class MiscRegistry : ServiceRegistry { #region Constructors @@ -19,6 +18,7 @@ public MiscRegistry() { this.AddSingleton(); this.AddSingleton(); + this.AddSingleton(); } #endregion diff --git a/FileProcessor/Bootstrapper/RepositoryRegistry.cs b/FileProcessor/Bootstrapper/RepositoryRegistry.cs index 0104429..b26aea4 100644 --- a/FileProcessor/Bootstrapper/RepositoryRegistry.cs +++ b/FileProcessor/Bootstrapper/RepositoryRegistry.cs @@ -1,10 +1,12 @@ namespace FileProcessor.Bootstrapper; using System; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Net.Security; using BusinessLogic.Common; using BusinessLogic.Managers; +using Common; using EstateReporting.Database; using FileAggregate; using FileImportLogAggregate; @@ -20,10 +22,7 @@ using Shared.General; using Shared.Repositories; -/// -/// -/// -/// +[ExcludeFromCodeCoverage] public class RepositoryRegistry : ServiceRegistry { #region Constructors diff --git a/FileProcessor/Common/Extensions.cs b/FileProcessor/Common/Extensions.cs new file mode 100644 index 0000000..84a5b50 --- /dev/null +++ b/FileProcessor/Common/Extensions.cs @@ -0,0 +1,144 @@ +namespace FileProcessor.Common; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Threading; +using EventStore.Client; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Shared.EventStore.EventHandling; +using Shared.EventStore.SubscriptionWorker; +using Shared.General; +using Shared.Logger; + +[ExcludeFromCodeCoverage] +public static class Extensions +{ + public static IServiceCollection AddInSecureEventStoreClient( + this IServiceCollection services, + Uri address, + Func? createHttpMessageHandler = null) + { + return services.AddEventStoreClient((Action)(options => + { + options.ConnectivitySettings.Address = address; + options.ConnectivitySettings.Insecure = true; + options.CreateHttpMessageHandler = createHttpMessageHandler; + })); + } + + static Action log = (tt, subType, message) => { + String logMessage = $"{subType} - {message}"; + switch (tt) + { + case TraceEventType.Critical: + Logger.LogCritical(new Exception(logMessage)); + break; + case TraceEventType.Error: + Logger.LogError(new Exception(logMessage)); + break; + case TraceEventType.Warning: + Logger.LogWarning(logMessage); + break; + case TraceEventType.Information: + Logger.LogInformation(logMessage); + break; + case TraceEventType.Verbose: + Logger.LogDebug(logMessage); + break; + } + }; + + static Action mainLog = (tt, message) => Extensions.log(tt, "MAIN", message); + static Action orderedLog = (tt, message) => Extensions.log(tt, "ORDERED", message); + + public static void PreWarm(this IApplicationBuilder applicationBuilder) + { + Startup.LoadTypes(); + + IConfigurationSection subscriptionConfigSection = Startup.Configuration.GetSection("AppSettings:SubscriptionConfiguration"); + SubscriptionWorkersRoot subscriptionWorkersRoot = new SubscriptionWorkersRoot(); + subscriptionConfigSection.Bind(subscriptionWorkersRoot); + + if (subscriptionWorkersRoot.InternalSubscriptionService) + { + String eventStoreConnectionString = ConfigurationReader.GetValue("EventStoreSettings", "ConnectionString"); + + ISubscriptionRepository subscriptionRepository = SubscriptionRepository.Create(eventStoreConnectionString, subscriptionWorkersRoot.InternalSubscriptionServiceCacheDuration); + ((SubscriptionRepository)subscriptionRepository).Trace += (sender, + s) => Extensions.log(TraceEventType.Information, "REPOSITORY", s); + + // init our SubscriptionRepository + subscriptionRepository.PreWarm(CancellationToken.None).Wait(); + + List workers = Extensions.ConfigureSubscriptions(subscriptionRepository, subscriptionWorkersRoot); + foreach (SubscriptionWorker subscriptionWorker in workers) + { + subscriptionWorker.StartAsync(CancellationToken.None).Wait(); + } + } + } + + private static List ConfigureSubscriptions(ISubscriptionRepository subscriptionRepository, SubscriptionWorkersRoot configuration) + { + List workers = new List(); + + foreach (SubscriptionWorkerConfig configurationSubscriptionWorker in configuration.SubscriptionWorkers) + { + if (configurationSubscriptionWorker.Enabled == false) + continue; + + if (configurationSubscriptionWorker.IsOrdered) + { + IDomainEventHandlerResolver eventHandlerResolver = Startup.Container.GetInstance("Ordered"); + SubscriptionWorker worker = SubscriptionWorker.CreateOrderedSubscriptionWorker(Startup.EventStoreClientSettings, + eventHandlerResolver, + subscriptionRepository, + configuration.PersistentSubscriptionPollingInSeconds); + worker.Trace += (_, + args) => Extensions.orderedLog(TraceEventType.Information, args.Message); + worker.Warning += (_, + args) => Extensions.orderedLog(TraceEventType.Warning, args.Message); + worker.Error += (_, + args) => Extensions.orderedLog(TraceEventType.Error, args.Message); + worker.SetIgnoreGroups(configurationSubscriptionWorker.IgnoreGroups); + worker.SetIgnoreStreams(configurationSubscriptionWorker.IgnoreStreams); + worker.SetIncludeGroups(configurationSubscriptionWorker.IncludeGroups); + worker.SetIncludeStreams(configurationSubscriptionWorker.IncludeStreams); + workers.Add(worker); + } + else + { + for (Int32 i = 0; i < configurationSubscriptionWorker.InstanceCount; i++) + { + IDomainEventHandlerResolver eventHandlerResolver = Startup.Container.GetInstance("Main"); + SubscriptionWorker worker = SubscriptionWorker.CreateSubscriptionWorker(Startup.EventStoreClientSettings, + eventHandlerResolver, + subscriptionRepository, + configurationSubscriptionWorker.InflightMessages, + configuration.PersistentSubscriptionPollingInSeconds); + + worker.Trace += (_, + args) => Extensions.mainLog(TraceEventType.Information, args.Message); + worker.Warning += (_, + args) => Extensions.mainLog(TraceEventType.Warning, args.Message); + worker.Error += (_, + args) => Extensions.mainLog(TraceEventType.Error, args.Message); + + worker.SetIgnoreGroups(configurationSubscriptionWorker.IgnoreGroups); + worker.SetIgnoreStreams(configurationSubscriptionWorker.IgnoreStreams); + worker.SetIncludeGroups(configurationSubscriptionWorker.IncludeGroups); + worker.SetIncludeStreams(configurationSubscriptionWorker.IncludeStreams); + + workers.Add(worker); + } + } + } + + return workers; + } +} \ No newline at end of file diff --git a/FileProcessor/Common/SubscriptionWorkerConfig.cs b/FileProcessor/Common/SubscriptionWorkerConfig.cs new file mode 100644 index 0000000..0f30f46 --- /dev/null +++ b/FileProcessor/Common/SubscriptionWorkerConfig.cs @@ -0,0 +1,18 @@ +namespace FileProcessor.Common; + +using System; +using System.Diagnostics.CodeAnalysis; + +[ExcludeFromCodeCoverage] +public class SubscriptionWorkerConfig +{ + public String WorkerName { get; set; } + public String IncludeGroups { get; set; } + public String IgnoreGroups { get; set; } + public String IncludeStreams { get; set; } + public String IgnoreStreams { get; set; } + public Boolean Enabled { get; set; } + public Int32 InflightMessages { get; set; } + public Int32 InstanceCount { get; set; } + public Boolean IsOrdered { get; set; } +} \ No newline at end of file diff --git a/FileProcessor/Common/SubscriptionWorkersRoot.cs b/FileProcessor/Common/SubscriptionWorkersRoot.cs new file mode 100644 index 0000000..23d8e1f --- /dev/null +++ b/FileProcessor/Common/SubscriptionWorkersRoot.cs @@ -0,0 +1,14 @@ +namespace FileProcessor.Common; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +[ExcludeFromCodeCoverage] +public class SubscriptionWorkersRoot +{ + public Boolean InternalSubscriptionService { get; set; } + public Int32 PersistentSubscriptionPollingInSeconds { get; set; } + public Int32 InternalSubscriptionServiceCacheDuration { get; set; } + public List SubscriptionWorkers { get; set; } +} \ No newline at end of file diff --git a/FileProcessor/Startup.cs b/FileProcessor/Startup.cs index eef3cc0..6718810 100644 --- a/FileProcessor/Startup.cs +++ b/FileProcessor/Startup.cs @@ -10,14 +10,11 @@ namespace FileProcessor { - using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Abstractions; - using System.Net.Http; using System.Reflection; - using System.Threading; using Bootstrapper; using BusinessLogic.Common; using BusinessLogic.EventHandling; @@ -34,6 +31,7 @@ namespace FileProcessor using FIleProcessor.Models; using HealthChecks.UI.Client; using Lamar; + using LamarCodeGeneration.Util; using MediatR; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -49,10 +47,8 @@ namespace FileProcessor using Shared.EntityFramework; using Shared.EntityFramework.ConnectionStringConfiguration; using Shared.EventStore.Aggregate; - using Shared.EventStore.EventHandling; using Shared.EventStore.EventStore; using Shared.EventStore.Extensions; - using Shared.EventStore.SubscriptionWorker; using Shared.Extensions; using Shared.General; using Shared.Logger; @@ -147,7 +143,33 @@ public static void LoadTypes() FileLineAddedEvent fileLineAddedEvent = new FileLineAddedEvent(Guid.Empty, Guid.Empty, 0, String.Empty); - TypeProvider.LoadDomainEventsTypeDynamically(); + //TypeProvider.LoadDomainEventsTypeDynamically(new List() { + // "Pomelo", + // "Microsoft" + // }); + + LoadDomainEventsTypeDynamically(new List() { + "Pomelo", + "Microsoft" + }); + } + + public static void LoadDomainEventsTypeDynamically(List assemblyFilters = null) + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + List source = new List(); + foreach (string assemblyFilter in assemblyFilters) + { + List filteredAssemblies = assemblies.Where(a => a.FullName.Contains(assemblyFilter) == true).ToList(); + foreach (Assembly a in filteredAssemblies) { + assemblies = assemblies.Remove(a); + } + } + source.AddRange(assemblies.SelectMany((Func>)(a => (IEnumerable)a.GetTypes()))); + + foreach (Type type in source.Where((Func)(t => t.IsSubclassOf(typeof(DomainEvent)))).OrderBy((Func)(e => e.Name)).ToList()) + TypeMap.AddType(type, type.Name); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -206,152 +228,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF app.PreWarm(); } } - - public static class Extensions - { - public static IServiceCollection AddInSecureEventStoreClient( - this IServiceCollection services, - Uri address, - Func? createHttpMessageHandler = null) - { - return services.AddEventStoreClient((Action)(options => - { - options.ConnectivitySettings.Address = address; - options.ConnectivitySettings.Insecure = true; - options.CreateHttpMessageHandler = createHttpMessageHandler; - })); - } - - static Action log = (tt, subType, message) => { - String logMessage = $"{subType} - {message}"; - switch (tt) - { - case TraceEventType.Critical: - Logger.LogCritical(new Exception(logMessage)); - break; - case TraceEventType.Error: - Logger.LogError(new Exception(logMessage)); - break; - case TraceEventType.Warning: - Logger.LogWarning(logMessage); - break; - case TraceEventType.Information: - Logger.LogInformation(logMessage); - break; - case TraceEventType.Verbose: - Logger.LogDebug(logMessage); - break; - } - }; - - static Action mainLog = (tt, message) => Extensions.log(tt, "MAIN", message); - static Action orderedLog = (tt, message) => Extensions.log(tt, "ORDERED", message); - - public static void PreWarm(this IApplicationBuilder applicationBuilder) - { - Startup.LoadTypes(); - - IConfigurationSection subscriptionConfigSection = Startup.Configuration.GetSection("AppSettings:SubscriptionConfiguration"); - SubscriptionWorkersRoot subscriptionWorkersRoot = new SubscriptionWorkersRoot(); - subscriptionConfigSection.Bind(subscriptionWorkersRoot); - - if (subscriptionWorkersRoot.InternalSubscriptionService) - { - String eventStoreConnectionString = ConfigurationReader.GetValue("EventStoreSettings", "ConnectionString"); - - ISubscriptionRepository subscriptionRepository = SubscriptionRepository.Create(eventStoreConnectionString, subscriptionWorkersRoot.InternalSubscriptionServiceCacheDuration); - ((SubscriptionRepository)subscriptionRepository).Trace += (sender, - s) => Extensions.log(TraceEventType.Information, "REPOSITORY", s); - - // init our SubscriptionRepository - subscriptionRepository.PreWarm(CancellationToken.None).Wait(); - - List workers = ConfigureSubscriptions(subscriptionRepository, subscriptionWorkersRoot); - foreach (SubscriptionWorker subscriptionWorker in workers) - { - subscriptionWorker.StartAsync(CancellationToken.None).Wait(); - } - } - } - - private static List ConfigureSubscriptions(ISubscriptionRepository subscriptionRepository, SubscriptionWorkersRoot configuration) - { - List workers = new List(); - - foreach (SubscriptionWorkerConfig configurationSubscriptionWorker in configuration.SubscriptionWorkers) - { - if (configurationSubscriptionWorker.Enabled == false) - continue; - - if (configurationSubscriptionWorker.IsOrdered) - { - IDomainEventHandlerResolver eventHandlerResolver = Startup.Container.GetInstance("Ordered"); - SubscriptionWorker worker = SubscriptionWorker.CreateOrderedSubscriptionWorker(Startup.EventStoreClientSettings, - eventHandlerResolver, - subscriptionRepository, - configuration.PersistentSubscriptionPollingInSeconds); - worker.Trace += (_, - args) => Extensions.orderedLog(TraceEventType.Information, args.Message); - worker.Warning += (_, - args) => Extensions.orderedLog(TraceEventType.Warning, args.Message); - worker.Error += (_, - args) => Extensions.orderedLog(TraceEventType.Error, args.Message); - worker.SetIgnoreGroups(configurationSubscriptionWorker.IgnoreGroups); - worker.SetIgnoreStreams(configurationSubscriptionWorker.IgnoreStreams); - worker.SetIncludeGroups(configurationSubscriptionWorker.IncludeGroups); - worker.SetIncludeStreams(configurationSubscriptionWorker.IncludeStreams); - workers.Add(worker); - } - else - { - for (Int32 i = 0; i < configurationSubscriptionWorker.InstanceCount; i++) - { - IDomainEventHandlerResolver eventHandlerResolver = Startup.Container.GetInstance("Main"); - SubscriptionWorker worker = SubscriptionWorker.CreateSubscriptionWorker(Startup.EventStoreClientSettings, - eventHandlerResolver, - subscriptionRepository, - configurationSubscriptionWorker.InflightMessages, - configuration.PersistentSubscriptionPollingInSeconds); - - worker.Trace += (_, - args) => Extensions.mainLog(TraceEventType.Information, args.Message); - worker.Warning += (_, - args) => Extensions.mainLog(TraceEventType.Warning, args.Message); - worker.Error += (_, - args) => Extensions.mainLog(TraceEventType.Error, args.Message); - - worker.SetIgnoreGroups(configurationSubscriptionWorker.IgnoreGroups); - worker.SetIgnoreStreams(configurationSubscriptionWorker.IgnoreStreams); - worker.SetIncludeGroups(configurationSubscriptionWorker.IncludeGroups); - worker.SetIncludeStreams(configurationSubscriptionWorker.IncludeStreams); - - workers.Add(worker); - } - } - } - - return workers; - } - } - - public class SubscriptionWorkersRoot - { - public Boolean InternalSubscriptionService { get; set; } - public Int32 PersistentSubscriptionPollingInSeconds { get; set; } - public Int32 InternalSubscriptionServiceCacheDuration { get; set; } - public List SubscriptionWorkers { get; set; } - } - - public class SubscriptionWorkerConfig - { - public String WorkerName { get; set; } - public String IncludeGroups { get; set; } - public String IgnoreGroups { get; set; } - public String IncludeStreams { get; set; } - public String IgnoreStreams { get; set; } - public Boolean Enabled { get; set; } - public Int32 InflightMessages { get; set; } - public Int32 InstanceCount { get; set; } - public Boolean IsOrdered { get; set; } - } } From 9f3f2be251bf7f5b2e4550c3266e85aa8006643f Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 2 Jan 2023 08:07:37 +0000 Subject: [PATCH 2/4] some code review fixes --- .../FileProcessorDomainServiceTests.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs index 1db48ee..2728974 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs @@ -78,14 +78,10 @@ public FileProcessorDomainServiceTests() [Fact] public async Task FileRequestHandler_UploadFileRequest_RequestIsHandled() { - this.FileProcessorManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.FileProfileSafaricom); this.FileImportLogAggregateRepository.Setup(f => f.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEmptyFileImportLogAggregate); - - - this.FileSystem.AddFile(TestData.FilePathWithName, new MockFileData("D,1,1,1")); this.FileSystem.AddDirectory("home/txnproc/bulkfiles/safaricom"); @@ -207,8 +203,6 @@ public void FileRequestHandler_SafaricomTopupRequest_RequestIsHandled() this.VerifyFileProcessing("home/txnproc/bulkfiles/safaricom/processed"); } - - [Fact] public void FileRequestHandler_SafaricomTopupRequest_FileAggregateNotCreated_RequestIsHandled() { @@ -807,9 +801,7 @@ public void FileRequestHandler_VoucherRequest_RequestIsHandled() }); this.VerifyFileProcessing("home/txnproc/bulkfiles/voucher/processed"); } - - - + [Fact] public void FileRequestHandler_VoucherRequest_FileAggregateNotCreated_RequestIsHandled() { From db98effb31352843566cd7ecfa7675b4b1f2cb1a Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 2 Jan 2023 08:10:01 +0000 Subject: [PATCH 3/4] More fixes --- FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs | 5 ----- .../Services/FileProcessorDomainService.cs | 1 - 2 files changed, 6 deletions(-) diff --git a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs index fb304e4..c95fb58 100644 --- a/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileRequestHandlerTests.cs @@ -24,7 +24,6 @@ public FileRequestHandlerTests() { } public async Task FileRequestHandler_HandleUploadFileRequest_RequestHandled() { - Should.NotThrow(async () => { await this.FileRequestHandler.Handle(TestData.UploadFileRequest, CancellationToken.None); }); @@ -32,7 +31,6 @@ public async Task FileRequestHandler_HandleUploadFileRequest_RequestHandled() { public async Task FileRequestHandler_ProcessUploadedFileRequest_RequestHandled() { - Should.NotThrow(async () => { await this.FileRequestHandler.Handle(TestData.ProcessUploadedFileRequest, CancellationToken.None); }); @@ -40,7 +38,6 @@ public async Task FileRequestHandler_ProcessUploadedFileRequest_RequestHandled() public async Task FileRequestHandler_SafaricomTopupRequest_RequestHandled() { - Should.NotThrow(async () => { await this.FileRequestHandler.Handle(TestData.SafaricomTopupRequest, CancellationToken.None); }); @@ -48,7 +45,6 @@ public async Task FileRequestHandler_SafaricomTopupRequest_RequestHandled() public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_RequestHandled() { - Should.NotThrow(async () => { await this.FileRequestHandler.Handle(TestData.ProcessTransactionForFileLineRequest, CancellationToken.None); }); @@ -56,7 +52,6 @@ public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_Reques public async Task FileRequestHandler_VoucherRequest_RequestHandled() { - Should.NotThrow(async () => { await this.FileRequestHandler.Handle(TestData.VoucherRequest, CancellationToken.None); }); diff --git a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs index 2b33e24..75ec9d6 100644 --- a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs @@ -256,7 +256,6 @@ public async Task ProcessTransactionForFileLine(ProcessTransactionForFileLineReq // Build a transaction request message SaleTransactionRequest saleTransactionRequest = new SaleTransactionRequest { - EstateId = fileDetails.EstateId, MerchantId = fileDetails.MerchantId, TransactionDateTime = fileDetails.FileReceivedDateTime, From 04eab8cb0bad60312f30366abbb8330b330438d6 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 2 Jan 2023 08:11:29 +0000 Subject: [PATCH 4/4] .. --- .../Services/FileProcessorDomainService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs index 75ec9d6..214652e 100644 --- a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs @@ -240,7 +240,6 @@ public async Task ProcessTransactionForFileLine(ProcessTransactionForFileLineReq contract = contracts.SingleOrDefault(c => c.OperatorName == operatorName); } - if (contract == null) { throw new NotFoundException($"No merchant contract for operator Id {operatorName} found for Merchant Id {merchant.MerchantId}");