From d7a273ba503d0562ca2ddb15c2df139748b13b16 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Tue, 28 Jan 2020 19:41:47 +0000 Subject: [PATCH] Merchant Id validated --- .../Services/TransactionDomainServiceTests.cs | 81 ++++++++++++++----- ...actionProcessor.BusinessLogic.Tests.csproj | 2 +- .../Services/TransactionDomainService.cs | 12 +-- .../Services/TransactionResponseCode.cs | 3 +- .../Common/TestingContext.cs | 5 ++ .../LogonTransaction/LogonTransaction.feature | 14 ++++ .../LogonTransaction.feature.cs | 80 ++++++++++++++++++ TransactionProcessor.Testing/TestData.cs | 50 ++++++++++-- 8 files changed, 213 insertions(+), 34 deletions(-) diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index 903f66fa..4834fd84 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -62,9 +62,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIs TestData.DeviceIdentifier, CancellationToken.None); - response.ShouldNotBeNull(); - response.ResponseCode.ShouldBe(TestData.ResponseCode); - response.ResponseMessage.ShouldBe(TestData.ResponseMessage); + this.ValidateLogonResponse(response, TransactionResponseCode.Success); } [Fact] @@ -105,9 +103,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_MerchantWithN TestData.DeviceIdentifier, CancellationToken.None); - response.ShouldNotBeNull(); - response.ResponseCode.ShouldBe(TestData.ResponseCode); - response.ResponseMessage.ShouldBe(TestData.ResponseMessage); + this.ValidateLogonResponse(response, TransactionResponseCode.Success); } [Fact] @@ -148,9 +144,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_MerchantWithN TestData.DeviceIdentifier, CancellationToken.None); - response.ShouldNotBeNull(); - response.ResponseCode.ShouldBe(TestData.ResponseCode); - response.ResponseMessage.ShouldBe(TestData.ResponseMessage); + this.ValidateLogonResponse(response, TransactionResponseCode.Success); } [Fact] @@ -168,7 +162,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_IncorrectDevi transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetEmptyTransactionAggregate) .ReturnsAsync(TestData.GetStartedTransactionAggregate) - .ReturnsAsync(TestData.GetLocallyAuthorisedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.InvalidDeviceIdentifier)) .ReturnsAsync(TestData.GetCompletedTransactionAggregate); transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); @@ -191,13 +185,11 @@ public async Task TransactionDomainService_ProcessLogonTransaction_IncorrectDevi TestData.DeviceIdentifier1, CancellationToken.None); - response.ShouldNotBeNull(); - response.ResponseCode.ShouldBe(TestData.ResponseCode); - response.ResponseMessage.ShouldBe(TestData.ResponseMessage); + this.ValidateLogonResponse(response, TransactionResponseCode.InvalidDeviceIdentifier); } [Fact] - public async Task TransactionDomainService_ProcessLogonTransaction_InvlaidEstate_TransactionIsProcessed() + public async Task TransactionDomainService_ProcessLogonTransaction_InvalidEstate_TransactionIsProcessed() { IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); ConfigurationReader.Initialise(configurationRoot); @@ -211,7 +203,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_InvlaidEstate transactionAggregateRepository.SetupSequence(t => t.GetLatestVersion(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetEmptyTransactionAggregate) .ReturnsAsync(TestData.GetStartedTransactionAggregate) - .ReturnsAsync(TestData.GetLocallyAuthorisedTransactionAggregate) + .ReturnsAsync(TestData.GetLocallyDeclinedTransactionAggregate(TransactionResponseCode.InvalidEstateId)) .ReturnsAsync(TestData.GetCompletedTransactionAggregate); transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); @@ -234,10 +226,63 @@ public async Task TransactionDomainService_ProcessLogonTransaction_InvlaidEstate TestData.DeviceIdentifier1, CancellationToken.None); - response.ShouldNotBeNull(); - response.ResponseCode.ShouldBe(TestData.ResponseCode); - response.ResponseMessage.ShouldBe(TestData.ResponseMessage); + this.ValidateLogonResponse(response, TransactionResponseCode.InvalidEstateId); } + [Fact] + public async Task TransactionDomainService_ProcessLogonTransaction_InvalidMerchant_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.InvalidMerchantId)) + .ReturnsAsync(TestData.GetCompletedTransactionAggregate); + transactionAggregateRepository.Setup(t => t.SaveChanges(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + + Mock estateClient = new Mock(); + 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.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetEmptyMerchantResponse); + + TransactionDomainService transactionDomainService = + new TransactionDomainService(aggregateRepositoryManager.Object, estateClient.Object, securityServiceClient.Object); + + ProcessLogonTransactionResponse response = await transactionDomainService.ProcessLogonTransaction(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier1, + CancellationToken.None); + + this.ValidateLogonResponse(response, TransactionResponseCode.InvalidMerchantId); + } + + private void ValidateLogonResponse(ProcessLogonTransactionResponse response, + TransactionResponseCode transactionResponseCode) + { + response.ShouldNotBeNull(); + response.ResponseCode.ShouldBe(TestData.GetResponseCodeAsString(transactionResponseCode)); + + String messageToValidate = TestData.GetResponseCodeMessage(transactionResponseCode); + if (transactionResponseCode == TransactionResponseCode.Success) + { + messageToValidate = messageToValidate.ToUpper(); + } + + response.ResponseMessage.ShouldBe(messageToValidate); + } } } diff --git a/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj b/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj index d88ee50a..57578ef1 100644 --- a/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj +++ b/TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - None + Full false diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index d2aedf48..ae3b76c8 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -153,12 +153,6 @@ public async Task ProcessLogonTransaction(Guid // TODO: Remove this once GetEstate returns correct response when estate not found if (estate.EstateName == null) { - - - - - - throw new TransactionValidationException($"Estate Id [{estateId}] is not a valid estate", TransactionResponseCode.InvalidEstateId); } @@ -166,6 +160,12 @@ public async Task ProcessLogonTransaction(Guid // TODO: Token MerchantResponse merchant = await this.EstateClient.GetMerchant(token.AccessToken, 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); + } + if (merchant.Devices == null || merchant.Devices.Any() == false) { // Add the device to the merchant diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs b/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs index 73bc2029..ed8c098c 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionResponseCode.cs @@ -5,6 +5,7 @@ public enum TransactionResponseCode Success = 0, InvalidDeviceIdentifier = 1000, - InvalidEstateId = 1001 + InvalidEstateId = 1001, + InvalidMerchantId = 1002 } } \ No newline at end of file diff --git a/TransactionProcessor.IntegrationTests/Common/TestingContext.cs b/TransactionProcessor.IntegrationTests/Common/TestingContext.cs index f0caa899..9686e420 100644 --- a/TransactionProcessor.IntegrationTests/Common/TestingContext.cs +++ b/TransactionProcessor.IntegrationTests/Common/TestingContext.cs @@ -167,6 +167,11 @@ public void AddMerchant(Guid merchantId, public Guid GetMerchantId(String merchantName) { + if (merchantName == "InvalidMerchant") + { + return Guid.Parse("D59320FA-4C3E-4900-A999-483F6A10C69A"); + } + return this.Merchants.Single(m => m.Key == merchantName).Value; } diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature index af9d7dd7..7f172d4f 100644 --- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature +++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature @@ -93,3 +93,17 @@ Scenario: Logon Transaction with Invalid Estate | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | | InvalidEstate | Test Merchant 1 | 1 | 1001 | Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate | +Scenario: Logon Transaction with Invalid Merchant + + Given I have assigned the following devices to the merchants + | DeviceIdentifier | MerchantName | MerchantNumber | EstateName | + | 123456780 | Test Merchant 1 | 00000001 | Test Estate 1 | + + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | + | Today | 1 | Logon | InvalidMerchant | 123456781 | Test Estate 1 | + + Then transaction response should contain the following information + | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | + | Test Estate 1 | InvalidMerchant | 1 | 1002 | Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for estate [Test Estate 1] | + diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs index 78d77aa9..14adeafa 100644 --- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs +++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs @@ -547,6 +547,86 @@ public virtual void LogonTransactionWithInvalidEstate() this.ScenarioCleanup(); } + [Xunit.SkippableFactAttribute(DisplayName="Logon Transaction with Invalid Merchant")] + [Xunit.TraitAttribute("FeatureTitle", "LogonTransaction")] + [Xunit.TraitAttribute("Description", "Logon Transaction with Invalid Merchant")] + public virtual void LogonTransactionWithInvalidMerchant() + { + string[] tagsOfScenario = ((string[])(null)); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Logon Transaction with Invalid Merchant", null, ((string[])(null))); +#line 96 +this.ScenarioInitialize(scenarioInfo); +#line hidden + bool isScenarioIgnored = default(bool); + bool isFeatureIgnored = default(bool); + if ((tagsOfScenario != null)) + { + isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); + } + if ((this._featureTags != null)) + { + isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); + } + if ((isScenarioIgnored || isFeatureIgnored)) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 4 +this.FeatureBackground(); +#line hidden + TechTalk.SpecFlow.Table table19 = new TechTalk.SpecFlow.Table(new string[] { + "DeviceIdentifier", + "MerchantName", + "MerchantNumber", + "EstateName"}); + table19.AddRow(new string[] { + "123456780", + "Test Merchant 1", + "00000001", + "Test Estate 1"}); +#line 98 + testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table19, "Given "); +#line hidden + TechTalk.SpecFlow.Table table20 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName"}); + table20.AddRow(new string[] { + "Today", + "1", + "Logon", + "InvalidMerchant", + "123456781", + "Test Estate 1"}); +#line 102 + testRunner.When("I perform the following transactions", ((string)(null)), table20, "When "); +#line hidden + TechTalk.SpecFlow.Table table21 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "MerchantName", + "TransactionNumber", + "ResponseCode", + "ResponseMessage"}); + table21.AddRow(new string[] { + "Test Estate 1", + "InvalidMerchant", + "1", + "1002", + "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + + "tate [Test Estate 1]"}); +#line 106 + testRunner.Then("transaction response should contain the following information", ((string)(null)), table21, "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class FixtureData : System.IDisposable diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 3039b755..b9fc4432 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -5,6 +5,7 @@ namespace TransactionProcessor.Testing { using BusinessLogic.Requests; + using BusinessLogic.Services; using EstateManagement.DataTransferObjects.Responses; using Models; using SecurityService.DataTransferObjects.Responses; @@ -76,6 +77,18 @@ public static TransactionAggregate GetLocallyAuthorisedTransactionAggregate() return transactionAggregate; } + public static TransactionAggregate GetLocallyDeclinedTransactionAggregate(TransactionResponseCode transactionResponseCode) + { + TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); + + transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, TestData.TransactionType, TestData.EstateId, TestData.MerchantId, + TestData.DeviceIdentifier); + + transactionAggregate.DeclineTransactionLocally(TestData.GetResponseCodeAsString(transactionResponseCode), TestData.GetResponseCodeMessage(transactionResponseCode)); + + return transactionAggregate; + } + public static TransactionAggregate GetCompletedTransactionAggregate() { TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId); @@ -103,10 +116,13 @@ public static TokenResponse TokenResponse() public static Guid DeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); + 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} @@ -114,19 +130,21 @@ public static TokenResponse TokenResponse() }; public static MerchantResponse GetMerchantResponseWithNullDevices = new MerchantResponse - { - EstateId = TestData.EstateId, - MerchantId = TestData.MerchantId, - Devices = null - }; - - public static MerchantResponse GetMerchantResponseWithNoDevices = new MerchantResponse { EstateId = TestData.EstateId, MerchantId = TestData.MerchantId, - Devices = new Dictionary() + 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, @@ -139,6 +157,22 @@ public static TokenResponse TokenResponse() EstateId = TestData.EstateId }; + public static MerchantResponse GetEmptyMerchantResponse = new MerchantResponse + { + MerchantId = TestData.MerchantId, + MerchantName = null + }; + + + public static String GetResponseCodeAsString(TransactionResponseCode transactionResponseCode) + { + return ((Int32)transactionResponseCode).ToString().PadLeft(4, '0'); + } + + public static String GetResponseCodeMessage(TransactionResponseCode transactionResponseCode) + { + return transactionResponseCode.ToString(); + } } }