From fc51ac020dd2b75b96e928f8884ccec4bf71434f Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Thu, 19 Mar 2020 19:09:57 +0000 Subject: [PATCH 1/2] Business Rules on operators added --- .../Requests/RequestTests.cs | 4 +- .../Services/TransactionDomainServiceTests.cs | 320 +++++++++++- .../Services/TransactionDomainService.cs | 180 +++++-- .../Services/TransactionResponseCode.cs | 4 + .../TransactionProcessor.BusinessLogic.csproj | 2 +- .../SaleTransactionFeature.feature | 3 + .../SaleTransactionFeature.feature.cs | 47 +- ...ansactionProcessor.IntegrationTests.csproj | 4 +- TransactionProcessor.Testing/TestData.cs | 485 ++++++++++++------ 9 files changed, 819 insertions(+), 230 deletions(-) diff --git a/TransactionProcessor.BusinessLogic.Tests/Requests/RequestTests.cs b/TransactionProcessor.BusinessLogic.Tests/Requests/RequestTests.cs index 3de4e355..d91b6e49 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Requests/RequestTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Requests/RequestTests.cs @@ -32,7 +32,7 @@ public void ProcessSaleTransactionRequest_CanBeCreated_IsCreated() { ProcessSaleTransactionRequest processSaleTransactionRequest = ProcessSaleTransactionRequest.Create(TestData.TransactionId, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData); processSaleTransactionRequest.ShouldNotBeNull(); @@ -43,7 +43,7 @@ public void ProcessSaleTransactionRequest_CanBeCreated_IsCreated() processSaleTransactionRequest.TransactionDateTime.ShouldBe(TestData.TransactionDateTime); processSaleTransactionRequest.TransactionNumber.ShouldBe(TestData.TransactionNumber); processSaleTransactionRequest.TransactionId.ShouldBe(TestData.TransactionId); - processSaleTransactionRequest.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier); + processSaleTransactionRequest.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); processSaleTransactionRequest.AdditionalTransactionMetadata.ShouldNotBeNull(); processSaleTransactionRequest.AdditionalTransactionMetadata.Count.ShouldBe(TestData.AdditionalTransactionMetaData.Count); } diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index 536e94c9..f4fbb92d 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -49,9 +49,9 @@ public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIs Mock securityServiceClient = new Mock(); securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); Mock operatorProxy = new Mock(); Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; @@ -96,7 +96,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_MerchantWithN Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetMerchantResponseWithNullDevices); @@ -141,7 +141,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_MerchantWithN Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetMerchantResponseWithNoDevices); @@ -185,9 +185,9 @@ public async Task TransactionDomainService_ProcessLogonTransaction_IncorrectDevi Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, @@ -231,7 +231,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_InvalidEstate securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ThrowsAsync(new Exception("Exception", new KeyNotFoundException("Invalid Estate"))); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, @@ -273,7 +273,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_InvalidMercha Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetEmptyMerchantResponse); @@ -315,9 +315,9 @@ public async Task TransactionDomainService_ProcessSaleTransaction_TransactionIsP Mock securityServiceClient = new Mock(); securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); Mock operatorProxy = new Mock(); Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; @@ -332,7 +332,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_TransactionIsP TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); @@ -364,7 +364,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithNu Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetMerchantResponseWithNullDevices); @@ -378,7 +378,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithNu TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); @@ -411,7 +411,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithNo Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetMerchantResponseWithNoDevices); @@ -425,7 +425,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithNo TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); @@ -457,9 +457,9 @@ public async Task TransactionDomainService_ProcessSaleTransaction_IncorrectDevic Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, @@ -471,7 +471,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_IncorrectDevic TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier1, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); @@ -505,7 +505,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_InvalidEstate_ securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ThrowsAsync(new Exception("Exception", new KeyNotFoundException("Invalid Estate"))); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(TestData.GetMerchantResponse); + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); TransactionDomainService transactionDomainService = new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, @@ -517,7 +517,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_InvalidEstate_ TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier1, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); @@ -549,7 +549,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_InvalidMerchan Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); - estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetEmptyMerchantResponse); @@ -563,13 +563,289 @@ public async Task TransactionDomainService_ProcessSaleTransaction_InvalidMerchan TestData.TransactionDateTime, TestData.TransactionNumber, TestData.DeviceIdentifier1, - TestData.OperatorIdentifier, + TestData.OperatorIdentifier1, TestData.AdditionalTransactionMetaData, CancellationToken.None); this.ValidateResponse(response, TransactionResponseCode.InvalidMerchantId); } + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_EstateWithEmptyOperators_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.NoEstateOperators)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithEmptyOperators); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.NoEstateOperators); + } + + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_EstateWithNullOperators_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.NoEstateOperators)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithNullOperators); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.NoEstateOperators); + } + + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_OperatorNotSupportedByEstate_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.OperatorNotValidForEstate)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier2, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.OperatorNotValidForEstate); + } + + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithEmptyOperators_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.NoMerchantOperators)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithEmptyOperators); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.NoMerchantOperators); + } + + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_MerchantWithNullOperators_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.NoMerchantOperators)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithNullOperators); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.NoMerchantOperators); + } + + [Fact] + public async Task TransactionDomainService_ProcessSaleTransaction_OperatorNotSupportedByMerchant_TransactionIsProcessed() + { + IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); + ConfigurationReader.Initialise(configurationRoot); + + Logger.Initialise(NullLogger.Instance); + + Mock aggregateRepositoryManager = new Mock(); + Mock> transactionAggregateRepository = new Mock>(); + + aggregateRepositoryManager.Setup(x => x.GetAggregateRepository(It.IsAny())).Returns(transactionAggregateRepository.Object); + transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyTransactionAggregate) + .ReturnsAsync(TestData.GetStartedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.OperatorNotValidForMerchant)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + Mock securityServiceClient = new Mock(); + Mock operatorProxy = new Mock(); + Func operatorProxyResolver = (operatorName) => { return operatorProxy.Object; }; + + securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + estateClient.Setup(e => e.GetEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.GetEstateResponseWithOperator1); + estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator2); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object, + operatorProxyResolver); + + ProcessSaleTransactionResponse response = await transactionDomainService.ProcessSaleTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData, + CancellationToken.None); + + this.ValidateResponse(response, TransactionResponseCode.OperatorNotValidForMerchant); + } + private void ValidateResponse(ProcessLogonTransactionResponse response, TransactionResponseCode transactionResponseCode) { diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index da281e91..9de67a93 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -98,7 +98,7 @@ public async Task ProcessLogonTransaction(Guid transactionAggregate.StartTransaction(transactionDateTime, transactionNumber, transactionType, estateId, merchantId, deviceIdentifier); await transactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken); - (String responseMessage, TransactionResponseCode responseCode) validationResult = await this.ValidateTransaction(estateId, merchantId, deviceIdentifier, transactionType, cancellationToken); + (String responseMessage, TransactionResponseCode responseCode) validationResult = await this.ValidateLogonTransaction(estateId, merchantId, deviceIdentifier, cancellationToken); if (validationResult.responseCode == TransactionResponseCode.Success) { @@ -138,7 +138,7 @@ public async Task ProcessLogonTransaction(Guid /// The transaction date time. /// The transaction number. /// The device identifier. - /// The operator identifier. + /// The operator identifier. /// The additional transaction metadata. /// The cancellation token. /// @@ -148,7 +148,7 @@ public async Task ProcessSaleTransaction(Guid tr DateTime transactionDateTime, String transactionNumber, String deviceIdentifier, - String operatorId, + String operatorIdentifier, Dictionary additionalTransactionMetadata, CancellationToken cancellationToken) { @@ -161,13 +161,13 @@ public async Task ProcessSaleTransaction(Guid tr transactionAggregate.StartTransaction(transactionDateTime, transactionNumber, transactionType, estateId, merchantId, deviceIdentifier); await transactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken); - (String responseMessage, TransactionResponseCode responseCode) validationResult = await this.ValidateTransaction(estateId, merchantId, deviceIdentifier, transactionType, cancellationToken); + (String responseMessage, TransactionResponseCode responseCode) validationResult = await this.ValidateSaleTransaction(estateId, merchantId, deviceIdentifier, operatorIdentifier, cancellationToken); if (validationResult.responseCode == TransactionResponseCode.Success) { // TODO: Do the online processing with the operator here MerchantResponse merchant = await this.GetMerchant(estateId, merchantId, cancellationToken); - IOperatorProxy operatorProxy = OperatorProxyResolver(operatorId); + IOperatorProxy operatorProxy = OperatorProxyResolver(operatorIdentifier); await operatorProxy.ProcessSaleMessage(transactionId, merchant, transactionDateTime, additionalTransactionMetadata, cancellationToken); // Record the successful validation @@ -198,57 +198,132 @@ public async Task ProcessSaleTransaction(Guid tr }; } + /// + /// Validates the transaction. + /// + /// The estate identifier. + /// The merchant identifier. + /// The cancellation token. + /// + /// + /// Estate Id [{estateId}] is not a valid estate + /// or + /// Merchant Id [{merchantId}] is not a valid merchant for estate [{estate.EstateName}] + /// + private async Task<(EstateResponse estate, MerchantResponse merchant)> ValidateTransaction(Guid estateId, + Guid merchantId, CancellationToken cancellationToken) + { + EstateResponse estate = null; + // Validate the Estate Record is a valid estate + try + { + estate = await this.GetEstate(estateId, cancellationToken); + } + catch (Exception ex) when (ex.InnerException != null && ex.InnerException.GetType() == typeof(KeyNotFoundException)) + { + throw new TransactionValidationException($"Estate Id [{estateId}] is not a valid estate", TransactionResponseCode.InvalidEstateId); + } + + // get the merchant record and validate the device + // TODO: Token + MerchantResponse merchant = await this.GetMerchant(estateId, merchantId, cancellationToken); + + // TODO: Remove this once GetMerchant returns correct response when merchant not found + if (merchant.MerchantName == null) + { + throw new TransactionValidationException($"Merchant Id [{merchantId}] is not a valid merchant for estate [{estate.EstateName}]", + TransactionResponseCode.InvalidMerchantId); + } + + return (estate, merchant); + } + /// /// Validates the transaction. /// /// The estate identifier. /// The merchant identifier. /// The device identifier. - /// Type of the transaction. /// The cancellation token. /// /// Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName} - private async Task<(String responseMessage, TransactionResponseCode responseCode)> ValidateTransaction(Guid estateId, + private async Task<(String responseMessage, TransactionResponseCode responseCode)> ValidateLogonTransaction(Guid estateId, Guid merchantId, String deviceIdentifier, - TransactionType transactionType, CancellationToken cancellationToken) { try { - EstateResponse estate = null; - // Validate the Estate Record is a valid estate - try + (EstateResponse estate, MerchantResponse merchant) validateTransactionResponse = await this.ValidateTransaction(estateId, merchantId, cancellationToken); + EstateResponse estate = validateTransactionResponse.estate; + MerchantResponse merchant = validateTransactionResponse.merchant; + + // Device Validation + if (merchant.Devices == null || merchant.Devices.Any() == false) { - estate = await this.GetEstate(estateId, cancellationToken); + await this.AddDeviceToMerchant(estateId, merchantId, deviceIdentifier, cancellationToken); } - catch (Exception ex) when (ex.InnerException != null && ex.InnerException.GetType() == typeof(KeyNotFoundException)) + else { - throw new TransactionValidationException($"Estate Id [{estateId}] is not a valid estate", TransactionResponseCode.InvalidEstateId); - } - - // get the merchant record and validate the device - // TODO: Token - MerchantResponse merchant = await this.GetMerchant(estateId, merchantId, cancellationToken); + // Validate the device + KeyValuePair device = merchant.Devices.SingleOrDefault(d => d.Value == deviceIdentifier); - // TODO: Remove this once GetMerchant returns correct response when merchant not found - if (merchant.MerchantName == null) - { - throw new TransactionValidationException($"Merchant Id [{merchantId}] is not a valid merchant for estate [{estate.EstateName}]", - TransactionResponseCode.InvalidMerchantId); + if (device.Key == Guid.Empty) + { + // Device not found,throw error + throw new TransactionValidationException($"Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName}", + TransactionResponseCode.InvalidDeviceIdentifier); + } } + + // If we get here everything is good + return ("SUCCESS", TransactionResponseCode.Success); + } + catch (TransactionValidationException tvex) + { + return (tvex.Message, tvex.ResponseCode); + } + } + /// + /// Validates the sale transaction. + /// + /// The estate identifier. + /// The merchant identifier. + /// The device identifier. + /// The operator identifier. + /// The cancellation token. + /// + /// + /// Merchant {merchant.MerchantName} has no valid Devices for this transaction. + /// or + /// Device Identifier {deviceIdentifier} not valid for Merchant {merchant.MerchantName} + /// or + /// Estate {estate.EstateName} has no operators defined + /// or + /// Operator {operatorIdentifier} not configured for Estate [{estate.EstateName}] + /// or + /// Merchant {merchant.MerchantName} has no operators defined + /// or + /// Operator {operatorIdentifier} not configured for Merchant [{merchant.MerchantName}] + /// + private async Task<(String responseMessage, TransactionResponseCode responseCode)> ValidateSaleTransaction(Guid estateId, + Guid merchantId, + String deviceIdentifier, + String operatorIdentifier, + CancellationToken cancellationToken) + { + try + { + (EstateResponse estate, MerchantResponse merchant) validateTransactionResponse = await this.ValidateTransaction(estateId, merchantId, cancellationToken); + EstateResponse estate = validateTransactionResponse.estate; + MerchantResponse merchant = validateTransactionResponse.merchant; + + // Device Validation if (merchant.Devices == null || merchant.Devices.Any() == false) { - if (transactionType == TransactionType.Logon) - { - await this.AddDeviceToMerchant(estateId, merchantId, deviceIdentifier, cancellationToken); - } - else - { - throw new TransactionValidationException($"Merchant {merchant.MerchantName} has no valid Devices for this transaction.", - TransactionResponseCode.NoValidDevices); - } + throw new TransactionValidationException($"Merchant {merchant.MerchantName} has no valid Devices for this transaction.", + TransactionResponseCode.NoValidDevices); } else { @@ -263,6 +338,39 @@ public async Task ProcessSaleTransaction(Guid tr } } + // Operator Validation (Estate) + if (estate.Operators == null || estate.Operators.Any() == false) + { + throw new TransactionValidationException($"Estate {estate.EstateName} has no operators defined", + TransactionResponseCode.NoEstateOperators); + } + else + { + // Operators have been configured for the estate + EstateOperatorResponse operatorRecord = estate.Operators.SingleOrDefault(o => o.Name == operatorIdentifier); + if (operatorRecord == null) + { + throw new TransactionValidationException($"Operator {operatorIdentifier} not configured for Estate [{estate.EstateName}]", TransactionResponseCode.OperatorNotValidForEstate); + } + } + + // Operator Validation (Merchant) + if (merchant.Operators == null || merchant.Operators.Any() == false) + { + throw new TransactionValidationException($"Merchant {merchant.MerchantName} has no operators defined", + TransactionResponseCode.NoEstateOperators); + } + else + { + // Operators have been configured for the estate + MerchantOperatorResponse operatorRecord = merchant.Operators.SingleOrDefault(o => o.Name == operatorIdentifier); + if (operatorRecord == null) + { + throw new TransactionValidationException($"Operator {operatorIdentifier} not configured for Merchant [{merchant.MerchantName}]", TransactionResponseCode.OperatorNotValidForMerchant); + } + } + + // If we get here everything is good return ("SUCCESS", TransactionResponseCode.Success); } @@ -270,12 +378,6 @@ public async Task ProcessSaleTransaction(Guid tr { return (tvex.Message, tvex.ResponseCode); } - catch (Exception ex) - { - Logger.LogError(ex); - return ("Unspecified Processing Error", TransactionResponseCode.UnknownFailure); - } - } private TokenResponse TokenResponse; diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs b/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs index 498ababb..1c6def96 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs @@ -8,6 +8,10 @@ public enum TransactionResponseCode InvalidEstateId = 1001, InvalidMerchantId = 1002, NoValidDevices = 1003, + NoEstateOperators = 1004, + OperatorNotValidForEstate = 1005, + NoMerchantOperators = 1006, + OperatorNotValidForMerchant = 1007, // A Catch All generic Error where reason has not been identified UnknownFailure = 9999 diff --git a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj index 8f63465e..4334670d 100644 --- a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj +++ b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj @@ -5,7 +5,7 @@ - + diff --git a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature index 0857a168..0830cf70 100644 --- a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature +++ b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature @@ -24,6 +24,7 @@ Background: Given I have created the following operators | EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber | | Test Estate 1 | Safaricom | True | True | + | Test Estate 2 | Safaricom | True | True | Given I create the following merchants | MerchantName | AddressLine1 | Town | Region | Country | ContactName | EmailAddress | EstateName | @@ -34,6 +35,8 @@ Background: Given I have assigned the following operator to the merchants | OperatorName | MerchantName | MerchantNumber | TerminalNumber | EstateName | | Safaricom | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | + | Safaricom | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | + | Safaricom | Test Merchant 3 | 00000003 | 10000003 | Test Estate 2 | Given I have assigned the following devices to the merchants | DeviceIdentifier | MerchantName | EstateName | diff --git a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs index 9f071a31..10814203 100644 --- a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs +++ b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs @@ -148,6 +148,11 @@ public virtual void FeatureBackground() "Safaricom", "True", "True"}); + table26.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "True", + "True"}); #line 24 testRunner.Given("I have created the following operators", ((string)(null)), table26, "Given "); #line hidden @@ -187,7 +192,7 @@ public virtual void FeatureBackground() "Test Contact 3", "testcontact3@merchant2.co.uk", "Test Estate 2"}); -#line 28 +#line 29 testRunner.Given("I create the following merchants", ((string)(null)), table27, "Given "); #line hidden TechTalk.SpecFlow.Table table28 = new TechTalk.SpecFlow.Table(new string[] { @@ -202,7 +207,19 @@ public virtual void FeatureBackground() "00000001", "10000001", "Test Estate 1"}); -#line 34 + table28.AddRow(new string[] { + "Safaricom", + "Test Merchant 2", + "00000002", + "10000002", + "Test Estate 1"}); + table28.AddRow(new string[] { + "Safaricom", + "Test Merchant 3", + "00000003", + "10000003", + "Test Estate 2"}); +#line 35 testRunner.Given("I have assigned the following operator to the merchants", ((string)(null)), table28, "Given "); #line hidden TechTalk.SpecFlow.Table table29 = new TechTalk.SpecFlow.Table(new string[] { @@ -221,7 +238,7 @@ public virtual void FeatureBackground() "123456782", "Test Merchant 3", "Test Estate 2"}); -#line 38 +#line 41 testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table29, "Given "); #line hidden } @@ -241,7 +258,7 @@ public virtual void SaleTransactions() "PRTest"}; TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transactions", null, new string[] { "PRTest"}); -#line 45 +#line 48 this.ScenarioInitialize(scenarioInfo); #line hidden bool isScenarioIgnored = default(bool); @@ -300,7 +317,7 @@ public virtual void SaleTransactions() "Test Estate 2", "Safaricom", "100.00"}); -#line 47 +#line 50 testRunner.When("I perform the following transactions", ((string)(null)), table30, "When "); #line hidden TechTalk.SpecFlow.Table table31 = new TechTalk.SpecFlow.Table(new string[] { @@ -327,7 +344,7 @@ public virtual void SaleTransactions() "3", "0000", "SUCCESS"}); -#line 53 +#line 56 testRunner.Then("transaction response should contain the following information", ((string)(null)), table31, "Then "); #line hidden } @@ -344,7 +361,7 @@ public virtual void SaleTransactionWithInvalidDevice() "PRTest"}; TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Device", null, new string[] { "PRTest"}); -#line 60 +#line 63 this.ScenarioInitialize(scenarioInfo); #line hidden bool isScenarioIgnored = default(bool); @@ -385,7 +402,7 @@ public virtual void SaleTransactionWithInvalidDevice() "Test Estate 1", "Safaricom", "100.00"}); -#line 62 +#line 65 testRunner.When("I perform the following transactions", ((string)(null)), table32, "When "); #line hidden TechTalk.SpecFlow.Table table33 = new TechTalk.SpecFlow.Table(new string[] { @@ -400,7 +417,7 @@ public virtual void SaleTransactionWithInvalidDevice() "1", "1000", "Device Identifier 123456781 not valid for Merchant Test Merchant 1"}); -#line 66 +#line 69 testRunner.Then("transaction response should contain the following information", ((string)(null)), table33, "Then "); #line hidden } @@ -414,7 +431,7 @@ public virtual void SaleTransactionWithInvalidEstate() { string[] tagsOfScenario = ((string[])(null)); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Estate", null, ((string[])(null))); -#line 70 +#line 73 this.ScenarioInitialize(scenarioInfo); #line hidden bool isScenarioIgnored = default(bool); @@ -455,7 +472,7 @@ public virtual void SaleTransactionWithInvalidEstate() "InvalidEstate", "Safaricom", "100.00"}); -#line 72 +#line 75 testRunner.When("I perform the following transactions", ((string)(null)), table34, "When "); #line hidden TechTalk.SpecFlow.Table table35 = new TechTalk.SpecFlow.Table(new string[] { @@ -470,7 +487,7 @@ public virtual void SaleTransactionWithInvalidEstate() "1", "1001", "Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate"}); -#line 76 +#line 79 testRunner.Then("transaction response should contain the following information", ((string)(null)), table35, "Then "); #line hidden } @@ -484,7 +501,7 @@ public virtual void SaleTransactionWithInvalidMerchant() { string[] tagsOfScenario = ((string[])(null)); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Merchant", null, ((string[])(null))); -#line 80 +#line 83 this.ScenarioInitialize(scenarioInfo); #line hidden bool isScenarioIgnored = default(bool); @@ -525,7 +542,7 @@ public virtual void SaleTransactionWithInvalidMerchant() "Test Estate 1", "Safaricom", "100.00"}); -#line 82 +#line 85 testRunner.When("I perform the following transactions", ((string)(null)), table36, "When "); #line hidden TechTalk.SpecFlow.Table table37 = new TechTalk.SpecFlow.Table(new string[] { @@ -541,7 +558,7 @@ public virtual void SaleTransactionWithInvalidMerchant() "1002", "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + "tate [Test Estate 1]"}); -#line 86 +#line 89 testRunner.Then("transaction response should contain the following information", ((string)(null)), table37, "Then "); #line hidden } diff --git a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj index ebcc0f2f..5057ea70 100644 --- a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj +++ b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index de59382c..30137692 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TransactionProcessor.Testing +namespace TransactionProcessor.Testing { + using System; + using System.Collections.Generic; using BusinessLogic.OperatorInterfaces.SafaricomPinless; using BusinessLogic.Requests; using BusinessLogic.Services; @@ -14,64 +12,346 @@ namespace TransactionProcessor.Testing public class TestData { - public static ProcessLogonTransactionResponse ProcessLogonTransactionResponseModel = new ProcessLogonTransactionResponse - { - ResponseMessage = TestData.ResponseMessage, - ResponseCode = TestData.ResponseCode, - }; - - public static String ResponseMessage = "SUCCESS"; + #region Fields - public static String ResponseCode= "0000"; + public static String AuthorisationCode = "ABCD1234"; public static String DeclinedResponseCode = "0001"; public static String DeclinedResponseMessage = "DeclinedResponseMessage"; + public static Guid DeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); + + public static String DeviceIdentifier = "1234567890"; + + public static String DeviceIdentifier1 = "1234567891"; + public static Guid EstateId = Guid.Parse("A522FA27-F9D0-470A-A88D-325DED3B62EE"); + + public static String EstateName = "Test Estate 1"; + + public static String FailedSafaricomTopup = + "EXRCTRFRESP50002-JUL-201810002281420200314231322847Topup failed"; + + public static Boolean IsAuthorised = true; + public static Guid MerchantId = Guid.Parse("833B5AAC-A5C5-46C2-A499-F2B4252B2942"); - public static Guid TransactionId = Guid.Parse("AE89B2F6-307B-46F4-A8E7-CEF27097D766"); - public static ProcessLogonTransactionRequest ProcessLogonTransactionRequest = ProcessLogonTransactionRequest.Create( TestData.TransactionId, TestData.EstateId, TestData.MerchantId, - TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), - TestData.TransactionDateTime, - TestData.TransactionNumber); + public static Guid OperatorId = Guid.Parse("804E9D8D-C6FE-4A46-9E55-6A04EA3E1AE5"); - public static String DeviceIdentifier = "1234567890"; + public static String OperatorIdentifier1 = "Safaricom"; - public static String DeviceIdentifier1 = "1234567891"; + public static String OperatorIdentifier2 = "NotSupported"; - public static TransactionType TransactionTypeLogon = TransactionType.Logon; - public static TransactionType TransactionTypeSale = TransactionType.Sale; + public static Boolean RequireCustomMerchantNumber = true; + + public static Boolean RequireCustomTerminalNumber = true; + + public static String ResponseCode = "0000"; + + public static String ResponseMessage = "SUCCESS"; + + public static String SuccessfulSafaricomTopup = + "EXRCTRFRESP20002-JUL-201810002281420200314231322847Topup Successful"; public static DateTime TransactionDateTime = DateTime.Now; + public static Guid TransactionId = Guid.Parse("AE89B2F6-307B-46F4-A8E7-CEF27097D766"); + public static String TransactionNumber = "0001"; - public static String AuthorisationCode = "ABCD1234"; + public static TransactionType TransactionTypeLogon = TransactionType.Logon; - public static Boolean IsAuthorised = true; + public static TransactionType TransactionTypeSale = TransactionType.Sale; - public static TransactionAggregate GetEmptyTransactionAggregate() - { - return TransactionAggregate.Create(TestData.TransactionId); - } + private static readonly String MerchantName = "Test Merchant Name"; + + private static String MerchantNumber = "12345678"; + + private static String TerminalNumber = "00000001"; + + #endregion + + #region Properties + + public static Dictionary AdditionalTransactionMetaData => + new Dictionary + { + {"Amount", "100.00"} + }; + + public static IReadOnlyDictionary DefaultAppSettings => + new Dictionary + { + ["AppSettings:ClientId"] = "clientId", + ["AppSettings:ClientSecret"] = "clientSecret" + }; + + public static EstateResponse GetEmptyEstateResponse => + new EstateResponse + { + EstateName = null, + EstateId = TestData.EstateId + }; + + public static MerchantResponse GetEmptyMerchantResponse => + new MerchantResponse + { + MerchantId = TestData.MerchantId, + MerchantName = null + }; + + public static EstateResponse GetEstateResponseWithOperator1 => + new EstateResponse + { + EstateName = TestData.EstateName, + EstateId = TestData.EstateId, + Operators = new List + { + new EstateOperatorResponse + { + Name = TestData.OperatorIdentifier1, + OperatorId = TestData.OperatorId, + RequireCustomMerchantNumber = TestData.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = TestData.RequireCustomTerminalNumber + } + } + }; + + public static EstateResponse GetEstateResponseWithOperator2 => + new EstateResponse + { + EstateName = TestData.EstateName, + EstateId = TestData.EstateId, + Operators = new List + { + new EstateOperatorResponse + { + Name = TestData.OperatorIdentifier1, + OperatorId = TestData.OperatorId, + RequireCustomMerchantNumber = TestData.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = TestData.RequireCustomTerminalNumber + } + } + }; + + public static EstateResponse GetEstateResponseWithNullOperators => + new EstateResponse + { + EstateName = TestData.EstateName, + EstateId = TestData.EstateId, + Operators = null + }; + + public static EstateResponse GetEstateResponseWithEmptyOperators => + new EstateResponse + { + EstateName = TestData.EstateName, + EstateId = TestData.EstateId, + Operators = new List() + }; + + public static MerchantResponse GetMerchantResponseWithOperator1 => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = new Dictionary + { + {TestData.DeviceId, TestData.DeviceIdentifier} + }, + Operators = new List + { + new MerchantOperatorResponse + { + Name = TestData.OperatorIdentifier1, + OperatorId = TestData.OperatorId, + MerchantNumber = TestData.MerchantNumber, + TerminalNumber = TestData.TerminalNumber + } + } + }; + + public static MerchantResponse GetMerchantResponseWithOperator2 => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = new Dictionary + { + {TestData.DeviceId, TestData.DeviceIdentifier} + }, + Operators = new List + { + new MerchantOperatorResponse + { + Name = TestData.OperatorIdentifier2, + OperatorId = TestData.OperatorId, + MerchantNumber = TestData.MerchantNumber, + TerminalNumber = TestData.TerminalNumber + } + } + }; + + public static MerchantResponse GetMerchantResponseWithNoDevices => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = new Dictionary(), + Operators = new List + { + new MerchantOperatorResponse + { + Name = TestData.OperatorIdentifier1, + OperatorId = TestData.OperatorId, + MerchantNumber = TestData.MerchantNumber, + TerminalNumber = TestData.TerminalNumber + } + } + }; + + public static MerchantResponse GetMerchantResponseWithNullDevices => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = null, + Operators = new List + { + new MerchantOperatorResponse + { + Name = TestData.OperatorIdentifier1, + OperatorId = TestData.OperatorId, + MerchantNumber = TestData.MerchantNumber, + TerminalNumber = TestData.TerminalNumber + } + } + }; + + public static MerchantResponse GetMerchantResponseWithEmptyOperators => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = new Dictionary + { + {TestData.DeviceId, TestData.DeviceIdentifier} + }, + Operators = new List() + }; + + public static MerchantResponse GetMerchantResponseWithNullOperators => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName, + Devices = new Dictionary + { + {TestData.DeviceId, TestData.DeviceIdentifier} + }, + Operators = null + }; + + public static MerchantResponse Merchant => + new MerchantResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + MerchantName = TestData.MerchantName + }; + + public static ProcessLogonTransactionRequest ProcessLogonTransactionRequest => + ProcessLogonTransactionRequest.Create(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.DeviceIdentifier, + TestData.TransactionTypeLogon.ToString(), + TestData.TransactionDateTime, + TestData.TransactionNumber); + + public static ProcessLogonTransactionResponse ProcessLogonTransactionResponseModel => + new ProcessLogonTransactionResponse + { + ResponseMessage = TestData.ResponseMessage, + ResponseCode = TestData.ResponseCode + }; + + public static ProcessSaleTransactionRequest ProcessSaleTransactionRequest => + ProcessSaleTransactionRequest.Create(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.DeviceIdentifier, + TestData.TransactionTypeLogon.ToString(), + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.OperatorIdentifier1, + TestData.AdditionalTransactionMetaData); + + public static ProcessSaleTransactionResponse ProcessSaleTransactionResponseModel => + new ProcessSaleTransactionResponse + { + ResponseMessage = TestData.ResponseMessage, + ResponseCode = TestData.ResponseCode, + AdditionalTransactionMetadata = new Dictionary + { + {"OperatorResponseCode", "1000"} + } + }; + + public static SafaricomConfiguration SafaricomConfiguration => + new SafaricomConfiguration + { + ExtCode = "ExtCode1", + LoginId = "LoginId", + MSISDN = "123456789", + Password = "Password", + Url = "http://localhost", + Pin = "1234" + }; + + #endregion + + #region Methods - public static TransactionAggregate GetStartedTransactionAggregate() + public static TransactionAggregate GetCompletedTransactionAggregate() { TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); - transactionAggregate.StartTransaction(TestData.TransactionDateTime,TestData.TransactionNumber, TestData.TransactionTypeLogon, TestData.EstateId, TestData.MerchantId, + transactionAggregate.StartTransaction(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.TransactionTypeLogon, + TestData.EstateId, + TestData.MerchantId, TestData.DeviceIdentifier); + transactionAggregate.AuthoriseTransactionLocally(TestData.AuthorisationCode, TestData.ResponseCode, TestData.ResponseMessage); + + transactionAggregate.CompleteTransaction(); + return transactionAggregate; } + public static TransactionAggregate GetEmptyTransactionAggregate() + { + return TransactionAggregate.Create(TestData.TransactionId); + } + public static TransactionAggregate GetLocallyAuthorisedTransactionAggregate() { TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); - transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, TestData.TransactionTypeLogon, TestData.EstateId, TestData.MerchantId, + transactionAggregate.StartTransaction(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.TransactionTypeLogon, + TestData.EstateId, + TestData.MerchantId, TestData.DeviceIdentifier); transactionAggregate.AuthoriseTransactionLocally(TestData.AuthorisationCode, TestData.ResponseCode, TestData.ResponseMessage); @@ -83,141 +363,48 @@ public static TransactionAggregate GetLocallyDeclinedTransactionAggregate(Transa { TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); - transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, TestData.TransactionTypeLogon, TestData.EstateId, TestData.MerchantId, + transactionAggregate.StartTransaction(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.TransactionTypeLogon, + TestData.EstateId, + TestData.MerchantId, TestData.DeviceIdentifier); - transactionAggregate.DeclineTransactionLocally(TestData.GetResponseCodeAsString(transactionResponseCode), TestData.GetResponseCodeMessage(transactionResponseCode)); + transactionAggregate.DeclineTransactionLocally(TestData.GetResponseCodeAsString(transactionResponseCode), + TestData.GetResponseCodeMessage(transactionResponseCode)); return transactionAggregate; } - public static TransactionAggregate GetCompletedTransactionAggregate() + public static String GetResponseCodeAsString(TransactionResponseCode transactionResponseCode) { - TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); - - transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, TestData.TransactionTypeLogon, TestData.EstateId, TestData.MerchantId, - TestData.DeviceIdentifier); - - transactionAggregate.AuthoriseTransactionLocally(TestData.AuthorisationCode, TestData.ResponseCode, TestData.ResponseMessage); - - transactionAggregate.CompleteTransaction(); - - return transactionAggregate; + return ((Int32)transactionResponseCode).ToString().PadLeft(4, '0'); } - public static IReadOnlyDictionary DefaultAppSettings { get; } = new Dictionary - { - ["AppSettings:ClientId"] = "clientId", - ["AppSettings:ClientSecret"] = "clientSecret" - }; - - public static TokenResponse TokenResponse() + public static String GetResponseCodeMessage(TransactionResponseCode transactionResponseCode) { - return SecurityService.DataTransferObjects.Responses.TokenResponse.Create("AccessToken", String.Empty, 100); + return transactionResponseCode.ToString(); } - public static Guid DeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); + public static TransactionAggregate GetStartedTransactionAggregate() + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); - private static String MerchantName = "Test Merchant Name"; - - public static MerchantResponse GetMerchantResponse = new MerchantResponse - { - EstateId = TestData.EstateId, - MerchantId = TestData.MerchantId, - MerchantName = TestData.MerchantName, - Devices = new Dictionary - { - {TestData.DeviceId, TestData.DeviceIdentifier} - } - }; - - public static MerchantResponse GetMerchantResponseWithNullDevices = new MerchantResponse - { - EstateId = TestData.EstateId, - MerchantId = TestData.MerchantId, - MerchantName = TestData.MerchantName, - Devices = null - }; - - public static MerchantResponse GetMerchantResponseWithNoDevices = new MerchantResponse - { - EstateId = TestData.EstateId, - MerchantId = TestData.MerchantId, - MerchantName = TestData.MerchantName, - Devices = new Dictionary() - }; - - public static EstateResponse GetEmptyEstateResponse = new EstateResponse - { - EstateName = null, - EstateId = TestData.EstateId - }; - public static String EstateName = "Test Estate 1"; - public static EstateResponse GetEstateResponse = new EstateResponse - { - EstateName = TestData.EstateName, - EstateId = TestData.EstateId - }; - - public static MerchantResponse GetEmptyMerchantResponse = new MerchantResponse - { - MerchantId = TestData.MerchantId, - MerchantName = null - }; - - public static ProcessSaleTransactionResponse ProcessSaleTransactionResponseModel = new ProcessSaleTransactionResponse - { - ResponseMessage = TestData.ResponseMessage, - ResponseCode = TestData.ResponseCode, - AdditionalTransactionMetadata = new Dictionary - { - {"OperatorResponseCode", "1000"} - } - }; - - public static String OperatorIdentifier = "Safaricom"; - - public static Dictionary AdditionalTransactionMetaData = new Dictionary - { - {"Amount", "100.00"} - }; - - public static ProcessSaleTransactionRequest ProcessSaleTransactionRequest = ProcessSaleTransactionRequest.Create(TestData.TransactionId, TestData.EstateId, TestData.MerchantId, - TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), - TestData.TransactionDateTime, - TestData.TransactionNumber, - TestData.OperatorIdentifier, - TestData.AdditionalTransactionMetaData); + transactionAggregate.StartTransaction(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.TransactionTypeLogon, + TestData.EstateId, + TestData.MerchantId, + TestData.DeviceIdentifier); - public static String GetResponseCodeAsString(TransactionResponseCode transactionResponseCode) - { - return ((Int32)transactionResponseCode).ToString().PadLeft(4, '0'); + return transactionAggregate; } - public static String GetResponseCodeMessage(TransactionResponseCode transactionResponseCode) + public static TokenResponse TokenResponse() { - return transactionResponseCode.ToString(); + return SecurityService.DataTransferObjects.Responses.TokenResponse.Create("AccessToken", string.Empty, 100); } - public static SafaricomConfiguration SafaricomConfiguration = new SafaricomConfiguration - { - ExtCode = "ExtCode1", - LoginId = "LoginId", - MSISDN = "123456789", - Password = "Password", - Url = "http://localhost", - Pin = "1234" - }; - - public static MerchantResponse Merchant = new MerchantResponse - { - EstateId = TestData.EstateId, - MerchantId = TestData.MerchantId, - MerchantName = TestData.MerchantName - }; - - public static String SuccessfulSafaricomTopup = "EXRCTRFRESP20002-JUL-201810002281420200314231322847Topup Successful"; - - public static String FailedSafaricomTopup = "EXRCTRFRESP50002-JUL-201810002281420200314231322847Topup failed"; + #endregion } -} +} \ No newline at end of file From 664e77af318a2e3ce3e2597895645c5d5be5e656 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Thu, 19 Mar 2020 19:14:08 +0000 Subject: [PATCH 2/2] small update --- .../Services/TransactionDomainService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index 9de67a93..e0b16501 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -255,7 +255,6 @@ public async Task ProcessSaleTransaction(Guid tr try { (EstateResponse estate, MerchantResponse merchant) validateTransactionResponse = await this.ValidateTransaction(estateId, merchantId, cancellationToken); - EstateResponse estate = validateTransactionResponse.estate; MerchantResponse merchant = validateTransactionResponse.merchant; // Device Validation