From 11437417f08d0b8f5e5a4db2bff67192d0fda56e Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 20 Jul 2026 17:53:00 +0100 Subject: [PATCH 1/2] Add AgencyBanking operator support and integration tests Implemented AgencyBankingProxy and configuration, registered new operator, and updated transaction handling for AgencyBanking. Enhanced integration and handler tests, extended step definitions, and improved endpoint coverage. Updated merchant creation logic and test dependencies. --- Directory.Packages.props | 1 + .../AgencyBankingProxyTests.cs | 216 +++ .../AgencyBankingConfiguration.cs | 13 + .../AgencyBanking/AgencyBankingProxy.cs | 123 ++ .../AgencyBanking/BalanceEnquiryResponse.cs | 25 + .../SpecflowExtensions.cs | 41 +- ...rocessor.IntegrationTesting.Helpers.csproj | 1 + .../TransactionProcessorSteps.cs | 65 +- .../Common/DockerHelper.cs | 71 +- .../Common/GenericSteps.cs | 1 + .../Features/SaleTransactionFeature.feature | 170 ++- .../SaleTransactionFeature.feature.cs | 1185 ++++++++--------- .../Shared/SharedSteps.cs | 80 +- ...ansactionProcessor.IntegrationTests.csproj | 1 + .../HandlerTests/TransactionHandlersTests.cs | 149 +++ .../HandlerTests/WebHandlersTests.cs | 1167 ++++++++++++++++ .../Bootstrapper/MiddlewareRegistry.cs | 3 +- .../Bootstrapper/OperatorRegistry.cs | 3 + .../Endpoints/ContractEndpoints.cs | 2 + .../Endpoints/EstateEndpoints.cs | 2 + .../Endpoints/FloatEndpoints.cs | 2 + .../Endpoints/MerchantEndpoints.cs | 2 + .../Endpoints/OperatorEndpoints.cs | 2 + .../Endpoints/SettlementEndpoints.cs | 2 + .../Endpoints/TransactionEndpoints.cs | 12 +- .../Endpoints/VoucherEndpoints.cs | 2 + TransactionProcessor/Startup.cs | 4 +- TransactionProcessor/appsettings.json | 4 + 28 files changed, 2603 insertions(+), 746 deletions(-) create mode 100644 TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/AgencyBankingProxyTests.cs create mode 100644 TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingConfiguration.cs create mode 100644 TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingProxy.cs create mode 100644 TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/BalanceEnquiryResponse.cs create mode 100644 TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index c62d98aa..00a12cfe 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -84,6 +84,7 @@ + diff --git a/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/AgencyBankingProxyTests.cs b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/AgencyBankingProxyTests.cs new file mode 100644 index 00000000..05ad3ec9 --- /dev/null +++ b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/AgencyBankingProxyTests.cs @@ -0,0 +1,216 @@ +using Moq; +using Moq.Protected; +using Shared.Logger; +using Shared.Serialisation; +using Shouldly; +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using TransactionProcessor.BusinessLogic.OperatorInterfaces; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.AgencyBanking; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.SafaricomPinless; +using TransactionProcessor.Testing; +using Xunit; + +namespace TransactionProcessor.BusinessLogic.Tests.OperatorInterfaces +{ + public class AgencyBankingProxyTests + { + public AgencyBankingProxyTests() + { + Logger.Initialise(NullLogger.Instance); + StringSerialiser.Initialise(new SystemTextJsonSerializer(new JsonSerializerOptions())); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessLogonMessage_ReturnsSuccessfulResult() + { + HttpClient httpClient = SetupMockHttpClient(new HttpResponseMessage(HttpStatusCode.OK)); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + var result = await proxy.ProcessLogonMessage(CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldBeNull(); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessSaleMessage_MissingMessageType_ReturnsInvalidResult() + { + HttpClient httpClient = SetupMockHttpClient(new HttpResponseMessage(HttpStatusCode.OK)); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + Dictionary metadata = new Dictionary + { + { "AgencyBankingAccountNumber", "123456789" } + }; + + var result = await proxy.ProcessSaleMessage(TestData.TransactionId, + TestData.OperatorId, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + metadata, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Message.ShouldBe("AgencyBankingMessageType - Message Type is a required field for this transaction type"); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessSaleMessage_MissingAccountNumber_ReturnsInvalidResult() + { + HttpClient httpClient = SetupMockHttpClient(new HttpResponseMessage(HttpStatusCode.OK)); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + Dictionary metadata = new Dictionary + { + { "AgencyBankingMessageType", "balanceenquiry" } + }; + + var result = await proxy.ProcessSaleMessage(TestData.TransactionId, + TestData.OperatorId, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + metadata, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Message.ShouldBe("AgencyBankingAccountNumber - Account Number is a required field for this transaction type"); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessSaleMessage_UnsupportedMessageType_ReturnsInvalidResult() + { + HttpClient httpClient = SetupMockHttpClient(new HttpResponseMessage(HttpStatusCode.OK)); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + Dictionary metadata = new Dictionary + { + { "AgencyBankingMessageType", "cashwithdrawal" }, + { "AgencyBankingAccountNumber", "123456789" } + }; + + var result = await proxy.ProcessSaleMessage(TestData.TransactionId, + TestData.OperatorId, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + metadata, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Message.ShouldBe("AgencyBankingMessageType - Message Type cashwithdrawal is not supported for this transaction type"); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessSaleMessage_BalanceEnquiryFailure_ReturnsInvalidResult() + { + HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest) + { + Content = new StringContent("operator unavailable") + }; + + HttpRequestMessage capturedRequest = null; + HttpClient httpClient = SetupMockHttpClient(responseMessage, request => capturedRequest = request); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + Dictionary metadata = new Dictionary + { + { "AgencyBankingMessageType", "balanceenquiry" }, + { "AgencyBankingAccountNumber", "123456789" } + }; + + var result = await proxy.ProcessSaleMessage(TestData.TransactionId, + TestData.OperatorId, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + metadata, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Message.ShouldBe("Failed to process balance enquiry operator unavailable"); + capturedRequest.ShouldNotBeNull(); + capturedRequest.Method.ShouldBe(HttpMethod.Post); + capturedRequest.RequestUri.ShouldBe(new Uri("http://localhost/transactions/balance-enquiry")); + } + + [Fact] + public async Task AgencyBankingProxy_ProcessSaleMessage_BalanceEnquirySuccess_ReturnsMappedOperatorResponse() + { + HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{\"ResponseCode\":\"0000\",\"ResponseMessage\":\"Balance enquiry successful\",\"AvailableBalance\":2500}") + }; + + HttpRequestMessage capturedRequest = null; + HttpClient httpClient = SetupMockHttpClient(responseMessage, request => capturedRequest = request); + AgencyBankingConfiguration configuration = CreateConfiguration(); + IOperatorProxy proxy = new AgencyBankingProxy(httpClient, configuration); + + Dictionary metadata = new Dictionary + { + { "agencybankingmessagetype", "balanceenquiry" }, + { "agencybankingaccountnumber", "123456789" } + }; + + var result = await proxy.ProcessSaleMessage(TestData.TransactionId, + TestData.OperatorId, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + metadata, + CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data.IsSuccessful.ShouldBeTrue(); + result.Data.ResponseCode.ShouldBe("0000"); + result.Data.AdditionalTransactionResponseMetadata.ShouldContainKey("AgencyBankingBalance"); + + capturedRequest.ShouldNotBeNull(); + capturedRequest.Method.ShouldBe(HttpMethod.Post); + capturedRequest.RequestUri.ShouldBe(new Uri("http://localhost/transactions/balance-enquiry")); + + String requestBody = await capturedRequest.Content.ReadAsStringAsync(); + using JsonDocument requestJson = JsonDocument.Parse(requestBody); + requestJson.RootElement.GetProperty("agentId").GetString().ShouldBe(TestData.Merchant.MerchantId.ToString()); + requestJson.RootElement.GetProperty("accountNumber").GetString().ShouldBe("123456789"); + } + + private static AgencyBankingConfiguration CreateConfiguration() + { + return new AgencyBankingConfiguration + { + Url = "http://localhost" + }; + } + + private static HttpClient SetupMockHttpClient(HttpResponseMessage responseMessage, Action onRequest = null) + { + Mock handlerMock = new Mock(MockBehavior.Strict); + handlerMock.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .Callback((request, cancellationToken) => onRequest?.Invoke(request)) + .ReturnsAsync(responseMessage); + + return new HttpClient(handlerMock.Object); + } + } +} diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingConfiguration.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingConfiguration.cs new file mode 100644 index 00000000..db3c1671 --- /dev/null +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingConfiguration.cs @@ -0,0 +1,13 @@ +namespace TransactionProcessor.BusinessLogic.OperatorInterfaces.SafaricomPinless +{ + using System; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + [ExcludeFromCodeCoverage] + public class AgencyBankingConfiguration : BaseOperatorConfiguration + { + } +} \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingProxy.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingProxy.cs new file mode 100644 index 00000000..39871a0f --- /dev/null +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/AgencyBankingProxy.cs @@ -0,0 +1,123 @@ +using Shared.Logger; +using Shared.Serialisation; +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using TransactionProcessor.BusinessLogic.Common; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.PataPawaPostPay; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.PataPawaPrePay; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.SafaricomPinless; +using TransactionProcessor.Database.Entities; +using TransactionProcessor.Models.Merchant; +using Merchant = TransactionProcessor.Models.Merchant.Merchant; + +namespace TransactionProcessor.BusinessLogic.OperatorInterfaces.AgencyBanking +{ + public class AgencyBankingProxy:IOperatorProxy + + { + private readonly HttpClient HttpClient; + private readonly AgencyBankingConfiguration Configuration; + private static SerialiserOptions SerialiserOptions = new SerialiserOptions(SerialiserPropertyFormat.CamelCase); + public AgencyBankingProxy(HttpClient httpClient, AgencyBankingConfiguration configuration) { + HttpClient = httpClient; + this.Configuration = configuration; + } + + public async Task> ProcessLogonMessage(CancellationToken cancellationToken) { + Logger.LogInformation("AgencyBanking logon requested"); + return Result.Success(); + } + + public async Task> ProcessSaleMessage(Guid transactionId, + Guid operatorId, + Merchant merchant, + DateTime transactionDateTime, + String transactionReference, + Dictionary additionalTransactionMetadata, + CancellationToken cancellationToken) { + Logger.LogInformation($"AgencyBanking sale request received. TransactionId=[{transactionId}], OperatorId=[{operatorId}], MerchantId=[{merchant.MerchantId}], TransactionDateTime=[{transactionDateTime:o}], TransactionReference=[{transactionReference}]"); + + // Check the meta data for the message type + String messageType = additionalTransactionMetadata.ExtractFieldFromMetadata("AgencyBankingMessageType"); + + if (String.IsNullOrEmpty(messageType)) + { + Logger.LogWarning($"AgencyBanking sale request rejected. TransactionId=[{transactionId}] missing AgencyBankingMessageType"); + return Result.Invalid("AgencyBankingMessageType - Message Type is a required field for this transaction type"); + } + + //// Check the meta data for the agent id + //String agentId = additionalTransactionMetadata.ExtractFieldFromMetadata("AgencyBankingAgentId"); + + //if (String.IsNullOrEmpty(agentId)) + //{ + // return Result.Invalid("AgencyBankingAgentId - Agent Id is a required field for this transaction type"); + //} + + // Check the meta data for the account number + String accountNumber = additionalTransactionMetadata.ExtractFieldFromMetadata("AgencyBankingAccountNumber"); + + if (String.IsNullOrEmpty(accountNumber)) + { + Logger.LogWarning($"AgencyBanking sale request rejected. TransactionId=[{transactionId}] missing AgencyBankingAccountNumber"); + return Result.Invalid("AgencyBankingAccountNumber - Account Number is a required field for this transaction type"); + } + + Logger.LogInformation($"AgencyBanking sale request metadata resolved. TransactionId=[{transactionId}], MessageType=[{messageType}], AccountNumber=[{accountNumber}]"); + + if (messageType == "balanceenquiry") + { + return await ProcessBalanceEnquiry(transactionId, merchant.MerchantId.ToString(), accountNumber, cancellationToken); + } + + Logger.LogWarning($"AgencyBanking sale request rejected. TransactionId=[{transactionId}] unsupported message type [{messageType}]"); + return Result.Invalid($"AgencyBankingMessageType - Message Type {messageType} is not supported for this transaction type"); + } + + private async Task> ProcessBalanceEnquiry(Guid transactionId, + String agentId, + String accountNumber, + CancellationToken cancellationToken) { + HttpRequestMessage request = new(HttpMethod.Post, $"{this.Configuration.Url}/transactions/balance-enquiry"); + + BalanceEnquiryRequest body = new BalanceEnquiryRequest(){ + AgentId = agentId, + AccountNumber = accountNumber + }; + + String requestBody = StringSerialiser.Serialise(body, SerialiserOptions); + request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); + + Logger.LogWarning($"AgencyBanking balance enquiry request starting. TransactionId=[{transactionId}], AgentId=[{agentId}], AccountNumber=[{accountNumber}], RequestUri=[{request.RequestUri}], RequestBody=[{requestBody}]"); + + HttpResponseMessage response = await this.HttpClient.SendAsync(request, cancellationToken); + String responseContent = await response.Content.ReadAsStringAsync(cancellationToken); + + Logger.LogWarning($"AgencyBanking balance enquiry response received. TransactionId=[{transactionId}], StatusCode=[{response.StatusCode}], ContentLength=[{responseContent?.Length ?? 0}]"); + + if (response.IsSuccessStatusCode == false) { + Logger.LogWarning($"AgencyBanking balance enquiry failed. TransactionId=[{transactionId}], StatusCode=[{response.StatusCode}], ResponseBody=[{responseContent}]"); + return Result.Invalid($"Failed to process balance enquiry {responseContent}"); + } + + BalanceEnquiryResponse balanceEnquiryResponse = StringSerialiser.Deserialise(responseContent, SerialiserOptions); + Logger.LogWarning($"AgencyBanking balance enquiry completed. TransactionId=[{transactionId}], ResponseMessage=[{balanceEnquiryResponse.ResponseMessage}], AvailableBalance=[{balanceEnquiryResponse.AvailableBalance}]"); + + OperatorResponse operatorResponse = new() { + IsSuccessful = true, + ResponseCode = "0000", + ResponseMessage = balanceEnquiryResponse.ResponseMessage, + AdditionalTransactionResponseMetadata = new Dictionary() + }; + + operatorResponse.AdditionalTransactionResponseMetadata.Add("AgencyBankingBalance", balanceEnquiryResponse.AvailableBalance.ToString()); + + return Result.Success(operatorResponse); + } + } +} diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/BalanceEnquiryResponse.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/BalanceEnquiryResponse.cs new file mode 100644 index 00000000..8dc58b3c --- /dev/null +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/AgencyBanking/BalanceEnquiryResponse.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace TransactionProcessor.BusinessLogic.OperatorInterfaces.AgencyBanking +{ + [ExcludeFromCodeCoverage] + public class BalanceEnquiryRequest + { + public String AgentId { get; set; } + + public string AccountNumber { get; set; } + } + + [ExcludeFromCodeCoverage] + public class BalanceEnquiryResponse + { + public String ResponseCode { get; set; } + + public string ResponseMessage { get; set; } + + public decimal AvailableBalance { get; set; } + } +} diff --git a/TransactionProcessor.IntegrationTesting.Helpers/SpecflowExtensions.cs b/TransactionProcessor.IntegrationTesting.Helpers/SpecflowExtensions.cs index 6fdaeeae..f4f3fc01 100644 --- a/TransactionProcessor.IntegrationTesting.Helpers/SpecflowExtensions.cs +++ b/TransactionProcessor.IntegrationTesting.Helpers/SpecflowExtensions.cs @@ -574,6 +574,7 @@ public static List ToAutomaticDepositRequests(this DataTableRows tableRo "Voucher" => BuildVoucherTransactionMetaData(recipientEmail, recipientMobile, transactionAmount), "PataPawa PostPay" => BuildPataPawaPostPayMetaData(messageType, accountNumber, recipientMobile, customerName, transactionAmount), "PataPawa PrePay" => BuildPataPawaPrePayMetaData(messageType, meterNumber, customerName, transactionAmount), + "AgencyBanking" => BuildAgencyBankingMetaData(messageType, accountNumber), _ => BuildMobileTopupMetaData(transactionAmount, customerAccountNumber) }; @@ -652,6 +653,13 @@ private static Dictionary BuildMobileTopupMetaData(Decimal trans }; } + private static Dictionary BuildAgencyBankingMetaData(String messageType, String accountNumber){ + return new Dictionary{ + { "AgencyBankingMessageType", messageType }, + { "AgencyBankingAccountNumber", accountNumber } + }; + } + private static Dictionary BuildPataPawaPostPayMetaData(String messageType, String accountNumber, String recipientMobile, @@ -1022,9 +1030,9 @@ public static List ToCreateEstateRequests(this DataTableRow return requests; } - public static List<(EstateDetails estate, CreateMerchantRequest)> ToCreateMerchantRequests(this DataTableRows tableRows, List estateDetailsList) + public static List<(EstateDetails estate, CreateMerchantRequest, Boolean EnableAgencyBanking)> ToCreateMerchantRequests(this DataTableRows tableRows, List estateDetailsList) { - List<(EstateDetails estate, CreateMerchantRequest)> requests = new(); + List<(EstateDetails estate, CreateMerchantRequest, Boolean EnableAgencyBanking)> requests = new(); foreach (DataTableRow tableRow in tableRows) { String estateName = ReqnrollTableHelper.GetStringRowValue(tableRow, "EstateName"); @@ -1043,34 +1051,21 @@ public static List ToCreateEstateRequests(this DataTableRow Name = ReqnrollTableHelper.GetStringRowValue(tableRow, "MerchantName"), Contact = new Contact { - ContactName = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "ContactName"), - EmailAddress = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "EmailAddress") + ContactName = ReqnrollTableHelper.GetStringRowValue(tableRow, "ContactName"), + EmailAddress = ReqnrollTableHelper.GetStringRowValue(tableRow, "EmailAddress") }, Address = new Address { - AddressLine1 = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "AddressLine1"), - Town = - ReqnrollTableHelper.GetStringRowValue(tableRow, "Town"), - Region = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "Region"), - Country = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "Country"), - PostalCode = - ReqnrollTableHelper.GetStringRowValue(tableRow, - "PostalCode") + AddressLine1 = ReqnrollTableHelper.GetStringRowValue(tableRow, "AddressLine1"), + Town = ReqnrollTableHelper.GetStringRowValue(tableRow, "Town"), + Region = ReqnrollTableHelper.GetStringRowValue(tableRow, "Region"), + Country = ReqnrollTableHelper.GetStringRowValue(tableRow, "Country"), + PostalCode = ReqnrollTableHelper.GetStringRowValue(tableRow, "PostalCode") }, SettlementSchedule = schedule, MerchantId = Guid.NewGuid() }; - requests.Add((estateDetails, createMerchantRequest)); + requests.Add((estateDetails, createMerchantRequest, ReqnrollTableHelper.GetBooleanValue(tableRow, "AgencyBankingEnabled"))); } return requests; diff --git a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessor.IntegrationTesting.Helpers.csproj b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessor.IntegrationTesting.Helpers.csproj index 08e1ba90..cb5c6d1d 100644 --- a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessor.IntegrationTesting.Helpers.csproj +++ b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessor.IntegrationTesting.Helpers.csproj @@ -15,6 +15,7 @@ + diff --git a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs index f8100d97..aaf27977 100644 --- a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs +++ b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs @@ -1,6 +1,8 @@ using EventStore.Client; using Shared.Serialisation; using SimpleResults; +using TestHosts.Clients; +using TestHosts.DataTransferObjects.AgencyBanking; using TransactionProcessor.DataTransferObjects.Requests.Contract; using TransactionProcessor.DataTransferObjects.Requests.Estate; using TransactionProcessor.DataTransferObjects.Requests.Merchant; @@ -29,12 +31,16 @@ public class TransactionProcessorSteps private readonly HttpClient TestHostHttpClient; private readonly EventStoreProjectionManagementClient ProjectionManagementClient; + private readonly IAgencyBankingClient AgencyBankingClient; - public TransactionProcessorSteps(ITransactionProcessorClient transactionProcessorClient, HttpClient testHostHttpClient, EventStoreProjectionManagementClient projectionManagementClient) + public TransactionProcessorSteps(ITransactionProcessorClient transactionProcessorClient, + HttpClient testHostHttpClient, EventStoreProjectionManagementClient projectionManagementClient, + IAgencyBankingClient agencyBankingClient) { this.TransactionProcessorClient = transactionProcessorClient; this.TestHostHttpClient = testHostHttpClient; this.ProjectionManagementClient = projectionManagementClient; + this.AgencyBankingClient = agencyBankingClient; } public async Task WhenIGetTheEstateSettlementReportForEstateForMerchantWithTheStartDateAndTheEndDateTheFollowingDataIsReturned(String accessToken, DateTime stateDate, DateTime endDate, ReqnrollExtensions.SettlementDetails expectedSettlementDetails) @@ -295,13 +301,13 @@ public async Task WhenIMakeTheFollowingMerchantWithdrawals(String accessToken, L } } - public async Task> WhenICreateTheFollowingMerchants(string accessToken, List<(EstateDetails estate, CreateMerchantRequest request)> requests) + public async Task> WhenICreateTheFollowingMerchants(string accessToken, List<(EstateDetails estate, CreateMerchantRequest request, Boolean enableAgencyBanking)> requests) { List responses = new List(); List<(Guid, Guid, String)> merchants = new List<(Guid, Guid, String)>(); - foreach ((EstateDetails estate, CreateMerchantRequest request) request in requests) + foreach ((EstateDetails estate, CreateMerchantRequest request, Boolean enableAgencyBanking) request in requests) { Result? result = await this.TransactionProcessorClient @@ -310,6 +316,43 @@ public async Task> WhenICreateTheFollowingMerchants(strin result.IsSuccess.ShouldBeTrue(); + + if (request.enableAgencyBanking) { + CreateAgentRequest createAgentRequest = new() { + AgentId = request.request.MerchantId.Value.ToString(), + DailyLimit = 100000, + Name = request.request.Name, + MinimumFloat = 10000, + Email = request.request.Contact.EmailAddress, + Location = request.request.Address.AddressLine1, + PhoneNumber = request.request.Contact.PhoneNumber + }; + Result? enableAgencyBankingResult = await this.AgencyBankingClient.CreateRetailAgent(createAgentRequest, CancellationToken.None); + enableAgencyBankingResult.IsSuccess.ShouldBeTrue(); + + // Activate the agent + ActivateAgentRequest activateAgentRequest = new ActivateAgentRequest { ActivatedBy = "INTEGRATIONTEST" }; + var activateAgentResult = await this.AgencyBankingClient.ActivateAgent(createAgentRequest.AgentId, activateAgentRequest, CancellationToken.None); + activateAgentResult.IsSuccess.ShouldBeTrue(); + + ConfigureFloatRequest configureFloatRequest = new ConfigureFloatRequest { + AgentId = createAgentRequest.AgentId, DailyFloatLimit = 100000, MaximumFloat = 100000, MinimumFloat = 10000, + }; + var configureFloatResult = await this.AgencyBankingClient.ConfigureFloat(configureFloatRequest, CancellationToken.None); + configureFloatResult.IsSuccess.ShouldBeTrue(); + + FloatCreditRequest floatCreditRequest = new FloatCreditRequest() { + AgentId = createAgentRequest.AgentId, + Amount = 10000, + Narration = "Test Credit", + SourceAccount = "Test Account", + TransactionId = Guid.NewGuid().ToString() + }; + var floatCreditResult = await this.AgencyBankingClient.CreditFloat(floatCreditRequest, CancellationToken.None); + floatCreditResult.IsSuccess.ShouldBeTrue(); + } + + merchants.Add((request.estate.EstateId, request.request.MerchantId.Value, request.request.Name)); } @@ -352,19 +395,29 @@ private void ValidateTransactionResponse(LogonTransactionResponse logonTransacti { logonTransactionResponse.ResponseCode.ShouldBe(expectedResponseCode, $"Transaction Number {transactionNumber} verification failed"); - logonTransactionResponse.ResponseMessage.ShouldBe(expectedResponseMessage, $"Transaction Number {transactionNumber} verification failed"); + this.ValidateResponseMessage(logonTransactionResponse.ResponseMessage, transactionNumber, expectedResponseMessage); } private void ValidateTransactionResponse(SaleTransactionResponse saleTransactionResponse, String transactionNumber, String expectedResponseCode, String expectedResponseMessage) { saleTransactionResponse.ResponseCode.ShouldBe(expectedResponseCode, $"Transaction Number {transactionNumber} verification failed"); - saleTransactionResponse.ResponseMessage.ShouldBe(expectedResponseMessage, $"Transaction Number {transactionNumber} verification failed"); + this.ValidateResponseMessage(saleTransactionResponse.ResponseMessage, transactionNumber, expectedResponseMessage); } private void ValidateTransactionResponse(ReconciliationResponse reconciliationResponse, String transactionNumber, String expectedResponseCode, String expectedResponseMessage) { reconciliationResponse.ResponseCode.ShouldBe(expectedResponseCode, $"Transaction Number {transactionNumber} verification failed"); - reconciliationResponse.ResponseMessage.ShouldBe(expectedResponseMessage, $"Transaction Number {transactionNumber} verification failed"); + this.ValidateResponseMessage(reconciliationResponse.ResponseMessage, transactionNumber, expectedResponseMessage); + } + + private void ValidateResponseMessage(String actualResponseMessage, String transactionNumber, String expectedResponseMessage) + { + if (String.IsNullOrWhiteSpace(expectedResponseMessage) || expectedResponseMessage == "*") + { + return; + } + + actualResponseMessage.ShouldBe(expectedResponseMessage, $"Transaction Number {transactionNumber} verification failed"); } public async Task WhenIRequestTheReceiptIsResent(String accessToken, List transactions) diff --git a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs index e0e85fce..eb6cd143 100644 --- a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs +++ b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs @@ -1,5 +1,7 @@ using DotNet.Testcontainers.Builders; using Syncfusion.Pdf.Xmp; +using System.IO; +using TestHosts.Clients; using TransactionProcessor.Database.Contexts; namespace TransactionProcessor.IntegrationTests.Common @@ -7,6 +9,7 @@ namespace TransactionProcessor.IntegrationTests.Common using Client; using EventStore.Client; using global::Shared.IntegrationTesting; + using global::Shared.IntegrationTesting.TestContainers; using global::Shared.Serialisation; using SecurityService.Client; using Shouldly; @@ -34,7 +37,7 @@ public class DockerHelper : global::Shared.IntegrationTesting.TestContainers.Doc public static String TestBankSortCode = "112233"; public HttpClient TestHostHttpClient; - + /// /// The security service client /// @@ -49,6 +52,8 @@ public class DockerHelper : global::Shared.IntegrationTesting.TestContainers.Doc public EventStoreProjectionManagementClient ProjectionManagementClient; + public IAgencyBankingClient AgencyBankingClient; + #endregion #region Constructors @@ -89,17 +94,54 @@ await Retry.For(async () => }); } - public override ContainerBuilder SetupTransactionProcessorContainer(){ + //public override ContainerBuilder SetupTestHostContainer() + //{ + // var variables = new Dictionary(); + // variables.Add("ConnectionStrings:AgencyBankingReadModel", $"server={this.SqlServerContainerName};user id={this.SqlCredentials.usename};password={this.SqlCredentials.password};database=AgencyBankingReadModel;Encrypt=false"); + // variables.Add("ASPNETCORE_ENVIRONMENT", $"PRODUCTION"); + // this.AdditionalVariables.Add(ContainerType.TestHost, variables); + + // return base.SetupTestHostContainer(); + //} - List variables = new List(); - variables.Add($"OperatorConfiguration:PataPawaPrePay:Url=http://{this.TestHostContainerName}:{DockerPorts.TestHostPort}/api/patapawaprepay"); + public override ContainerBuilder SetupTransactionProcessorContainer() { + Dictionary additionalVariables = new(); + additionalVariables.Add("OperatorConfiguration:AgencyBanking:Url",$"http://{this.TestHostContainerName}:{DockerPorts.TestHostPort}/api/agencybanking"); - //this.AdditionalVariables.Add(ContainerType.FileProcessor, variables); - //this.SetAdditionalVariables(ContainerType.FileProcessor, variables); + this.AdditionalVariables.Add(ContainerType.TransactionProcessor, additionalVariables); return base.SetupTransactionProcessorContainer(); } + public override ContainerBuilder SetupTestHostContainer() + { + this.Trace("About to Start Test Hosts Container"); + + Dictionary environmentVariables = this.GetCommonEnvironmentVariables(); + environmentVariables.Add("ConnectionStrings:TestBankReadModel", this.SetConnectionString("TestBankReadModel", this.UseSecureSqlServerDatabase)); + environmentVariables.Add("ConnectionStrings:PataPawaReadModel", this.SetConnectionString("PataPawaReadModel", this.UseSecureSqlServerDatabase)); + environmentVariables.Add("ConnectionStrings:AgencyBankingReadModel", this.SetConnectionString("AgencyBankingReadModel", this.UseSecureSqlServerDatabase)); + //environmentVariables.Add("ASPNETCORE_ENVIRONMENT", "IntegrationTest"); + + Dictionary additionalEnvironmentVariables = this.GetAdditionalVariables(ContainerType.TestHost); + + foreach (KeyValuePair additionalEnvironmentVariable in additionalEnvironmentVariables) + { + environmentVariables.Add(additionalEnvironmentVariable.Key, additionalEnvironmentVariable.Value); + } + + (String imageName, Boolean useLatest) imageDetails = this.GetImageDetails(ContainerType.TestHost).Data; + + ContainerBuilder testHostContainer = new ContainerBuilder() + .WithName(this.TestHostContainerName) // similar to WithName() + .WithImage(imageDetails.imageName) + .WithEnvironment(environmentVariables) + .MountHostFolder(this.DockerPlatform, this.HostTraceFolder) + .WithPortBinding(DockerPorts.TestHostPort, true); + + return testHostContainer; + } + public override async Task CreateSubscriptions(){ List<(String streamName, String groupName, Int32 maxRetries)> subscriptions = new(); subscriptions.AddRange(MessagingService.IntegrationTesting.Helpers.SubscriptionsHelper.GetSubscriptions()); @@ -172,16 +214,28 @@ Object Deserialise(String arg, Type type) return StringSerialiser.DeserializeObject(arg, type, new SerialiserOptions(SerialiserPropertyFormat.SnakeCase)); } + String Serialise_CamelCase(Object arg) + { + return StringSerialiser.Serialise(arg, new SerialiserOptions(SerialiserPropertyFormat.CamelCase)); + } + + Object Deserialise_CamelCase(String arg, Type type) + { + return StringSerialiser.DeserializeObject(arg, type, new SerialiserOptions(SerialiserPropertyFormat.CamelCase)); + } + /// /// Starts the containers for scenario run. /// /// Name of the scenario. public override async Task StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices){ + this.HostTraceFolder = Path.Combine(Directory.GetCurrentDirectory(), "trace"); await base.StartContainersForScenarioRun(scenarioName, dockerServices); // Setup the base address resolvers String SecurityServiceBaseAddressResolver(String api) => $"https://127.0.0.1:{this.SecurityServicePort}"; String TransactionProcessorBaseAddressResolver(String api) => $"http://127.0.0.1:{this.TransactionProcessorPort}"; + String TestHostServiceBaseAddressResolver(String api) => $"http://127.0.0.1:{this.TestHostServicePort}"; HttpClientHandler clientHandler = new HttpClientHandler { @@ -193,11 +247,12 @@ public override async Task StartContainersForScenarioRun(String scenarioName, Do return true; } }; - HttpClient httpClient = new HttpClient(clientHandler); + HttpClient httpClient = new(clientHandler); this.SecurityServiceClient = new SecurityServiceClient(SecurityServiceBaseAddressResolver, httpClient, Serialise, Deserialise); this.TransactionProcessorClient = new TransactionProcessorClient(TransactionProcessorBaseAddressResolver, httpClient, Serialise, Deserialise); this.TestHostHttpClient= new HttpClient(clientHandler); this.TestHostHttpClient.BaseAddress = new Uri($"http://127.0.0.1:{this.TestHostServicePort}"); + this.AgencyBankingClient = new AgencyBankingClient(TestHostServiceBaseAddressResolver, httpClient, Serialise_CamelCase, this.Deserialise_CamelCase); this.Trace("About to configure Test Bank"); String callbackUrl = $"http://{this.CallbackHandlerContainerName}:{DockerPorts.CallbackHandlerDockerPort}/api/callbacks"; @@ -250,4 +305,4 @@ protected override List GetRequiredProjections() #endregion } -} \ No newline at end of file +} diff --git a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs index 8d4eca29..8e558f55 100644 --- a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs +++ b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs @@ -50,6 +50,7 @@ public async Task StartSystem() this.TestingContext.DockerHelper.SqlServerContainerName = "sharedsqlserver"; this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TransactionProcessor, ("transactionprocessor", false)); + this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TestHost, ("testhosts", false)); this.TestingContext.Logger = logger; this.TestingContext.Logger.LogInformation("About to Start Containers for Scenario Run"); diff --git a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature index 04341952..22d9f7fc 100644 --- a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature +++ b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature @@ -35,6 +35,24 @@ Background: | 00000002 | Customer 2 | | 00000003 | Customer 3 | + Given I initialise the Agency Banking Host + + Given I create the following accounts + | Type | Code | Name | Currency | + | LIABILITY | 200100 | Agent Float GL | KES | + | LIABILITY | 200200 | Settlement Suspense GL | KES | + | EXPENSE | 400100 | Agent Commission GL | KES | + | LIABILITY | 200300 | Reversal GL | KES | + | INCOME | 400200 | Fee Income GL | KES | + + Given I create the settlement account + | AccountNumber | BankCode | Currency | Name | + | 999000001 | 001 | KES | Main Settlement Account | + + Given I create the following customers + | CustomerId | FullName | PhoneNumber | NationalId | AccountNumber | + | CUST001 | Test Customer 1 | 0712345678 | 12345678 | 12345678 | + Given I have created the following estates | EstateName | | Test Estate 1 | @@ -45,6 +63,7 @@ Background: | Test Estate 1 | Voucher | False | False | | Test Estate 1 | PataPawa PostPay | False | False | | Test Estate 1 | PataPawa PrePay | False | False | + | Test Estate 1 | AgencyBanking | False | False | And I have assigned the following operators to the estates | EstateName | OperatorName | @@ -52,6 +71,7 @@ Background: | Test Estate 1 | Voucher | | Test Estate 1 | PataPawa PostPay | | Test Estate 1 | PataPawa PrePay | + | Test Estate 1 | AgencyBanking | Given I create a contract with the following values | EstateName | OperatorName | ContractDescription | @@ -59,6 +79,7 @@ Background: | Test Estate 1 | Voucher | Hospital 1 Contract | | Test Estate 1 | PataPawa PostPay | PataPawa PostPay Contract | | Test Estate 1 | PataPawa PrePay | PataPawa PrePay Contract | + | Test Estate 1 | AgencyBanking | AgencyBanking Contract | When I create the following Products | EstateName | OperatorName | ContractDescription | ProductName | DisplayText | Value | ProductType | @@ -66,6 +87,7 @@ Background: | Test Estate 1 | Voucher | Hospital 1 Contract | 10 KES | 10 KES | | Voucher | | Test Estate 1 | PataPawa PostPay | PataPawa PostPay Contract | Post Pay Bill Pay | Bill Pay (Post) | | BillPayment | | Test Estate 1 | PataPawa PrePay | PataPawa PrePay Contract | Pre Pay Bill Pay | Bill Pay (Pre) | | BillPayment | + | Test Estate 1 | AgencyBanking | AgencyBanking Contract | Balance Enquiry | Balance Enquiry | | MobileTopup | When I add the following Transaction Fees | EstateName | OperatorName | ContractDescription | ProductName | CalculationType | FeeDescription | Value | @@ -74,11 +96,11 @@ Background: | Test Estate 1 | PataPawa PrePay | PataPawa PrePay Contract | Pre Pay Bill Pay | Percentage | Merchant Commission | 0.50 | Given I create the following merchants - | MerchantName | AddressLine1 | Town | Region | PostalCode | Country | ContactName | EmailAddress | EstateName | - | Test Merchant 1 | Address Line 1 | TestTown | Test Region | TE57 1NG | United Kingdom | Test Contact 1 | testcontact1@merchant1.co.uk | Test Estate 1 | - | Test Merchant 2 | Address Line 1 | TestTown | Test Region | TE57 2NG | United Kingdom | Test Contact 2 | testcontact2@merchant2.co.uk | Test Estate 1 | - | Test Merchant 3 | Address Line 1 | TestTown | Test Region | TE57 3NG | United Kingdom | Test Contact 3 | testcontact3@merchant3.co.uk | Test Estate 1 | - | Test Merchant 4 | Address Line 1 | TestTown | Test Region | TE57 4NG | United Kingdom | Test Contact 4 | testcontact4@merchant4.co.uk | Test Estate 1 | + | MerchantName | AddressLine1 | Town | Region | PostalCode | Country | ContactName | EmailAddress | EstateName | AgencyBankingEnabled | + | Test Merchant 1 | Address Line 1 | TestTown | Test Region | TE57 1NG | United Kingdom | Test Contact 1 | testcontact1@merchant1.co.uk | Test Estate 1 | true | + | Test Merchant 2 | Address Line 1 | TestTown | Test Region | TE57 2NG | United Kingdom | Test Contact 2 | testcontact2@merchant2.co.uk | Test Estate 1 | true | + | Test Merchant 3 | Address Line 1 | TestTown | Test Region | TE57 3NG | United Kingdom | Test Contact 3 | testcontact3@merchant3.co.uk | Test Estate 1 | true | + | Test Merchant 4 | Address Line 1 | TestTown | Test Region | TE57 4NG | United Kingdom | Test Contact 4 | testcontact4@merchant4.co.uk | Test Estate 1 | true | Given I have assigned the following operator to the merchants | OperatorName | MerchantName | MerchantNumber | TerminalNumber | EstateName | @@ -86,18 +108,22 @@ Background: | Voucher | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | | PataPawa PostPay | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | | PataPawa PrePay | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | + | AgencyBanking | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | | Safaricom | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | | Voucher | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | | PataPawa PostPay | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | | PataPawa PrePay | Test Merchant 2 | 00000001 | 10000001 | Test Estate 1 | + | AgencyBanking | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | | Safaricom | Test Merchant 3 | 00000003 | 10000003 | Test Estate 1 | | Voucher | Test Merchant 3 | 00000003 | 10000003 | Test Estate 1 | | PataPawa PostPay | Test Merchant 3 | 00000003 | 10000003 | Test Estate 1 | | PataPawa PrePay | Test Merchant 3 | 00000001 | 10000001 | Test Estate 1 | + | AgencyBanking | Test Merchant 3 | 00000003 | 10000003 | Test Estate 1 | | Safaricom | Test Merchant 4 | 00000004 | 10000004 | Test Estate 1 | | Voucher | Test Merchant 4 | 00000004 | 10000004 | Test Estate 1 | | PataPawa PostPay | Test Merchant 4 | 00000004 | 10000004 | Test Estate 1 | | PataPawa PrePay | Test Merchant 4 | 00000001 | 10000001 | Test Estate 1 | + | AgencyBanking | Test Merchant 4 | 00000004 | 10000004 | Test Estate 1 | Given I have assigned the following devices to the merchants | DeviceIdentifier | MerchantName | EstateName | @@ -119,6 +145,7 @@ Background: | Test Estate 1 | Test Merchant 1 | Hospital 1 Contract | | Test Estate 1 | Test Merchant 1 | PataPawa PostPay Contract | | Test Estate 1 | Test Merchant 1 | PataPawa PrePay Contract | + | Test Estate 1 | Test Merchant 1 | AgencyBanking Contract | | Test Estate 1 | Test Merchant 2 | Safaricom Contract | | Test Estate 1 | Test Merchant 2 | Hospital 1 Contract | | Test Estate 1 | Test Merchant 2 | PataPawa PostPay Contract | @@ -132,36 +159,55 @@ Background: | Test Estate 1 | Test Merchant 4 | PataPawa PostPay Contract | | Test Estate 1 | Test Merchant 4 | PataPawa PrePay Contract | +@PRTest @PRTest Scenario: Sale Transactions When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | RecipientEmail | RecipientMobile | MessageType | AccountNumber | CustomerName | MeterNumber | - | Today | 1 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 110.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | - | Today | 2 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | - | Today | 3 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | - | Today | 4 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 90.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | - | Today | 5 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | - | Today | 6 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | | 123456789 | | | | | - | Today | 7 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | - | Today | 8 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PostPay | 0.00 | | | PataPawa PostPay Contract | Post Pay Bill Pay | test@recipient.co.uk | | VerifyAccount | 12345678 | | | - | Today | 9 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PostPay | 20.00 | | | PataPawa PostPay Contract | Post Pay Bill Pay | test@recipient.co.uk | 123456789 | ProcessBill | 12345678 | Mr Test Customer | | - | Today | 10 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PrePay | 0.00 | | | PataPawa PrePay Contract | Pre Pay Bill Pay | test@recipient.co.uk | | meter | | | 00000001 | - | Today | 11 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PrePay | 25.00 | | | PataPawa PrePay Contract | Pre Pay Bill Pay | test@recipient.co.uk | | vend | 00000001 | Customer 1 | 00000001 | - + | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | RecipientEmail | RecipientMobile | MessageType | AccountNumber | CustomerName | MeterNumber | + | Today | 1 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 110.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | + | Today | 2 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | + | Today | 3 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | + | Today | 4 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 90.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | + | Today | 5 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | + | Today | 6 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | | 123456789 | | | | | + | Today | 7 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | + | Today | 8 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PostPay | 0.00 | | | PataPawa PostPay Contract | Post Pay Bill Pay | test@recipient.co.uk | | VerifyAccount | 12345678 | | | + | Today | 9 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PostPay | 20.00 | | | PataPawa PostPay Contract | Post Pay Bill Pay | test@recipient.co.uk | 123456789 | ProcessBill | 12345678 | Mr Test Customer | | + | Today | 10 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PrePay | 0.00 | | | PataPawa PrePay Contract | Pre Pay Bill Pay | test@recipient.co.uk | | meter | | | 00000001 | + | Today | 11 | Sale | 2 | Test Merchant 1 | 123456780 | Test Estate 1 | PataPawa PrePay | 25.00 | | | PataPawa PrePay Contract | Pre Pay Bill Pay | test@recipient.co.uk | | vend | 00000001 | Customer 1 | 00000001 | + | Today | 12 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | AgencyBanking | 0.00 | | | AgencyBanking Contract | Balance Enquiry | balanceenquiry | 12345678 | balanceenquiry | 12345678 | | | + | Today | 13 | Sale | 1 | Test Merchant 1 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + | Today | 14 | Sale | 1 | Test Merchant 1 | 123456780 | InvalidEstate | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + | Today | 15 | Sale | 1 | InvalidMerchant | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + | Today | 16 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | EmptyContract | Variable Topup | | | | | | | + | Today | 17 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | InvalidContract | Variable Topup | | | | | | | + | Today | 18 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | EmptyProduct | | | | | | | + | Today | 19 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | InvalidProduct | | | | | | | + | Today | 20 | Sale | 1 | Test Merchant 4 | 123456783 | Test Estate 1 | Safaricom | 300.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 2 | 2 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 3 | 3 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 4 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 5 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 2 | 6 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 3 | 7 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 8 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 9 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 10 | 0000 | SUCCESS | - | Test Estate 1 | Test Merchant 1 | 11 | 0000 | SUCCESS | + | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | + | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 2 | 2 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 3 | 3 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 4 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 5 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 2 | 6 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 3 | 7 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 8 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 9 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 10 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 11 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 12 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 13 | 1000 | Device Identifier 123456781 not valid for Merchant Test Merchant 1 | + | InvalidEstate | Test Merchant 1 | 14 | 1001 | Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate | + | Test Estate 1 | InvalidMerchant | 15 | 1002 | Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for estate [Test Estate 1] | + | Test Estate 1 | Test Merchant 1 | 16 | 1012 | Contract Id [00000000-0000-0000-0000-000000000000] must be set for a sale transaction | + | Test Estate 1 | Test Merchant 1 | 17 | 1015 | Contract Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Merchant 1] | + | Test Estate 1 | Test Merchant 1 | 18 | 1013 | Product Id [00000000-0000-0000-0000-000000000000] must be set for a sale transaction | + | Test Estate 1 | Test Merchant 1 | 19 | 1016 | Product Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Merchant 1] | + | Test Estate 1 | Test Merchant 4 | 20 | 1009 | Merchant [Test Merchant 4] does not have enough credit available [100.00] to perform transaction amount [300.00] | Then the following entries appear in the merchants balance history for estate 'Test Estate 1' and merchant 'Test Merchant 1' | DateTime | Reference | EntryType | In | Out | ChangeAmount | Balance | @@ -196,67 +242,3 @@ Scenario: Sale Transactions #When I request the receipt is resent #| EstateName | MerchantName | TransactionNumber | #| Test Estate 1 | Test Merchant 1 | 1 | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 12 | Sale | 1 | Test Merchant 1 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 12 | 1000 | Device Identifier 123456781 not valid for Merchant Test Merchant 1 | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 13 | Sale | 1 | Test Merchant 1 | 123456780 | InvalidEstate | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | InvalidEstate | Test Merchant 1 | 13 | 1001 | Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 14 | Sale | 1 | InvalidMerchant | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | InvalidMerchant | 14 | 1002 | Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for estate [Test Estate 1] | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 15 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | EmptyContract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 15 | 1012 | Contract Id [00000000-0000-0000-0000-000000000000] must be set for a sale transaction | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 16 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | InvalidContract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 16 | 1015 | Contract Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Merchant 1] | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 17 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | EmptyProduct | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 17 | 1013 | Product Id [00000000-0000-0000-0000-000000000000] must be set for a sale transaction | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 18 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | InvalidProduct | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 18 | 1016 | Product Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Merchant 1] | - - When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 19 | Sale | 1 | Test Merchant 4 | 123456783 | Test Estate 1 | Safaricom | 300.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | - - Then transaction response should contain the following information - | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 4 | 19 | 1009 | Merchant [Test Merchant 4] does not have enough credit available [100.00] to perform transaction amount [300.00] | \ No newline at end of file diff --git a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs index 6bdb6fd9..15df35ec 100644 --- a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs +++ b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs @@ -111,169 +111,245 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table140 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table140.AddRow(new string[] { + table1.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); #line 6 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table140, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table1, "Given "); #line hidden - global::Reqnroll.Table table141 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table141.AddRow(new string[] { + table2.AddRow(new string[] { "transactionProcessor", "Estate Managememt REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); #line 11 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table141, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table2, "Given "); #line hidden - global::Reqnroll.Table table142 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table142.AddRow(new string[] { + table3.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor", "client_credentials"}); #line 16 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table142, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table3, "Given "); #line hidden - global::Reqnroll.Table table143 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table143.AddRow(new string[] { + table4.AddRow(new string[] { "serviceClient"}); #line 20 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table143, "Given "); + "s", ((string)(null)), table4, "Given "); #line hidden - global::Reqnroll.Table table144 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { "AccountNumber", "AccountName", "DueDate", "Amount"}); - table144.AddRow(new string[] { + table5.AddRow(new string[] { "12345678", "Test Account 1", "Today", "100.00"}); #line 24 - await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table144, "Given "); + await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table5, "Given "); #line hidden - global::Reqnroll.Table table145 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { "Username", "Password"}); - table145.AddRow(new string[] { + table6.AddRow(new string[] { "operatora", "1234567898"}); #line 28 - await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table145, "Given "); + await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table6, "Given "); #line hidden - global::Reqnroll.Table table146 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { "MeterNumber", "CustomerName"}); - table146.AddRow(new string[] { + table7.AddRow(new string[] { "00000001", "Customer 1"}); - table146.AddRow(new string[] { + table7.AddRow(new string[] { "00000002", "Customer 2"}); - table146.AddRow(new string[] { + table7.AddRow(new string[] { "00000003", "Customer 3"}); #line 32 - await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table146, "Given "); + await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table7, "Given "); #line hidden - global::Reqnroll.Table table147 = new global::Reqnroll.Table(new string[] { +#line 38 + await testRunner.GivenAsync("I initialise the Agency Banking Host", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + global::Reqnroll.Table table8 = new global::Reqnroll.Table(new string[] { + "Type", + "Code", + "Name", + "Currency"}); + table8.AddRow(new string[] { + "LIABILITY", + "200100", + "Agent Float GL", + "KES"}); + table8.AddRow(new string[] { + "LIABILITY", + "200200", + "Settlement Suspense GL", + "KES"}); + table8.AddRow(new string[] { + "EXPENSE", + "400100", + "Agent Commission GL", + "KES"}); + table8.AddRow(new string[] { + "LIABILITY", + "200300", + "Reversal GL", + "KES"}); + table8.AddRow(new string[] { + "INCOME", + "400200", + "Fee Income GL", + "KES"}); +#line 40 + await testRunner.GivenAsync("I create the following accounts", ((string)(null)), table8, "Given "); +#line hidden + global::Reqnroll.Table table9 = new global::Reqnroll.Table(new string[] { + "AccountNumber", + "BankCode", + "Currency", + "Name"}); + table9.AddRow(new string[] { + "999000001", + "001", + "KES", + "Main Settlement Account"}); +#line 48 + await testRunner.GivenAsync("I create the settlement account", ((string)(null)), table9, "Given "); +#line hidden + global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { + "CustomerId", + "FullName", + "PhoneNumber", + "NationalId", + "AccountNumber"}); + table10.AddRow(new string[] { + "CUST001", + "Test Customer 1", + "0712345678", + "12345678", + "12345678"}); +#line 52 + await testRunner.GivenAsync("I create the following customers", ((string)(null)), table10, "Given "); +#line hidden + global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table147.AddRow(new string[] { + table11.AddRow(new string[] { "Test Estate 1"}); -#line 38 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table147, "Given "); +#line 56 + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table11, "Given "); #line hidden - global::Reqnroll.Table table148 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table148.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "Safaricom", "False", "False"}); - table148.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "Voucher", "False", "False"}); - table148.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "False", "False"}); - table148.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "False", "False"}); -#line 42 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table148, "Given "); + table12.AddRow(new string[] { + "Test Estate 1", + "AgencyBanking", + "False", + "False"}); +#line 60 + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table12, "Given "); #line hidden - global::Reqnroll.Table table149 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table149.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table149.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "Voucher"}); - table149.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay"}); - table149.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay"}); -#line 49 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table149, "And "); + table13.AddRow(new string[] { + "Test Estate 1", + "AgencyBanking"}); +#line 68 + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table13, "And "); #line hidden - global::Reqnroll.Table table150 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table150.AddRow(new string[] { + table14.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table150.AddRow(new string[] { + table14.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); - table150.AddRow(new string[] { + table14.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract"}); - table150.AddRow(new string[] { + table14.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract"}); -#line 56 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table150, "Given "); + table14.AddRow(new string[] { + "Test Estate 1", + "AgencyBanking", + "AgencyBanking Contract"}); +#line 76 + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table14, "Given "); #line hidden - global::Reqnroll.Table table151 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -281,7 +357,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table151.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -289,7 +365,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table151.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -297,7 +373,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table151.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -305,7 +381,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Post)", "", "BillPayment"}); - table151.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -313,10 +389,18 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Pre)", "", "BillPayment"}); -#line 63 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table151, "When "); + table15.AddRow(new string[] { + "Test Estate 1", + "AgencyBanking", + "AgencyBanking Contract", + "Balance Enquiry", + "Balance Enquiry", + "", + "MobileTopup"}); +#line 84 + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table15, "When "); #line hidden - global::Reqnroll.Table table152 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -324,7 +408,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table152.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -332,7 +416,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table152.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -340,7 +424,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table152.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -348,10 +432,10 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); -#line 70 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table152, "When "); +#line 92 + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table16, "When "); #line hidden - global::Reqnroll.Table table153 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -360,8 +444,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Country", "ContactName", "EmailAddress", - "EstateName"}); - table153.AddRow(new string[] { + "EstateName", + "AgencyBankingEnabled"}); + table17.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -370,8 +455,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "United Kingdom", "Test Contact 1", "testcontact1@merchant1.co.uk", - "Test Estate 1"}); - table153.AddRow(new string[] { + "Test Estate 1", + "true"}); + table17.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -380,8 +466,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "United Kingdom", "Test Contact 2", "testcontact2@merchant2.co.uk", - "Test Estate 1"}); - table153.AddRow(new string[] { + "Test Estate 1", + "true"}); + table17.AddRow(new string[] { "Test Merchant 3", "Address Line 1", "TestTown", @@ -390,8 +477,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "United Kingdom", "Test Contact 3", "testcontact3@merchant3.co.uk", - "Test Estate 1"}); - table153.AddRow(new string[] { + "Test Estate 1", + "true"}); + table17.AddRow(new string[] { "Test Merchant 4", "Address Line 1", "TestTown", @@ -400,241 +488,270 @@ await testRunner.GivenAsync("I have a token to access the estate management and "United Kingdom", "Test Contact 4", "testcontact4@merchant4.co.uk", - "Test Estate 1"}); -#line 76 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table153, "Given "); + "Test Estate 1", + "true"}); +#line 98 + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table17, "Given "); #line hidden - global::Reqnroll.Table table154 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { + "AgencyBanking", + "Test Merchant 1", + "00000001", + "10000001", + "Test Estate 1"}); + table18.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 2", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { + "AgencyBanking", + "Test Merchant 2", + "00000002", + "10000002", + "Test Estate 1"}); + table18.AddRow(new string[] { "Safaricom", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "Voucher", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 3", "00000001", "10000001", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { + "AgencyBanking", + "Test Merchant 3", + "00000003", + "10000003", + "Test Estate 1"}); + table18.AddRow(new string[] { "Safaricom", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "Voucher", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table154.AddRow(new string[] { + table18.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 4", "00000001", "10000001", "Test Estate 1"}); -#line 83 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table154, "Given "); + table18.AddRow(new string[] { + "AgencyBanking", + "Test Merchant 4", + "00000004", + "10000004", + "Test Estate 1"}); +#line 105 + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table18, "Given "); #line hidden - global::Reqnroll.Table table155 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table155.AddRow(new string[] { + table19.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table155.AddRow(new string[] { + table19.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); - table155.AddRow(new string[] { + table19.AddRow(new string[] { "123456782", "Test Merchant 3", "Test Estate 1"}); - table155.AddRow(new string[] { + table19.AddRow(new string[] { "123456783", "Test Merchant 4", "Test Estate 1"}); -#line 102 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table155, "Given "); +#line 128 + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table19, "Given "); #line hidden - global::Reqnroll.Table table156 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table156.AddRow(new string[] { + table20.AddRow(new string[] { "Deposit1", "265.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table156.AddRow(new string[] { + table20.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 2", "Test Estate 1"}); - table156.AddRow(new string[] { + table20.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 3", "Test Estate 1"}); - table156.AddRow(new string[] { + table20.AddRow(new string[] { "Deposit1", "100.00", "Today", "Test Merchant 4", "Test Estate 1"}); -#line 109 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table156, "Given "); +#line 135 + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table20, "Given "); #line hidden - global::Reqnroll.Table table157 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PostPay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PrePay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "AgencyBanking Contract"}); + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PostPay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PrePay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "Safaricom Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "Hospital 1 Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "PataPawa PostPay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "PataPawa PrePay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "Safaricom Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "Hospital 1 Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "PataPawa PostPay Contract"}); - table157.AddRow(new string[] { + table21.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "PataPawa PrePay Contract"}); -#line 116 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table157, "When "); +#line 142 + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table21, "When "); #line hidden } @@ -646,16 +763,18 @@ await testRunner.GivenAsync("I have a token to access the estate management and [global::NUnit.Framework.TestAttribute()] [global::NUnit.Framework.DescriptionAttribute("Sale Transactions")] [global::NUnit.Framework.CategoryAttribute("PRTest")] + [global::NUnit.Framework.CategoryAttribute("PRTest")] public async global::System.Threading.Tasks.Task SaleTransactions() { string[] tagsOfScenario = new string[] { + "PRTest", "PRTest"}; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); string pickleIndex = "0"; global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Sale Transactions", null, tagsOfScenario, argumentsOfScenario, featureTags, pickleIndex); string[] tagsOfRule = ((string[])(null)); global::Reqnroll.RuleInfo ruleInfo = null; -#line 136 +#line 164 this.ScenarioInitialize(scenarioInfo, ruleInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -668,7 +787,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table158 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -688,7 +807,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "AccountNumber", "CustomerName", "MeterNumber"}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "1", "Sale", @@ -708,7 +827,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "2", "Sale", @@ -728,7 +847,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "3", "Sale", @@ -748,7 +867,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "4", "Sale", @@ -768,7 +887,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "5", "Sale", @@ -788,7 +907,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "6", "Sale", @@ -808,7 +927,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "7", "Sale", @@ -828,7 +947,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "8", "Sale", @@ -848,7 +967,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "9", "Sale", @@ -868,7 +987,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "Mr Test Customer", ""}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "10", "Sale", @@ -888,7 +1007,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", "00000001"}); - table158.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "11", "Sale", @@ -908,133 +1027,373 @@ await testRunner.GivenAsync("I have a token to access the estate management and "00000001", "Customer 1", "00000001"}); -#line 138 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table158, "When "); + table22.AddRow(new string[] { + "Today", + "12", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "AgencyBanking", + "0.00", + "", + "", + "AgencyBanking Contract", + "Balance Enquiry", + "balanceenquiry", + "12345678", + "balanceenquiry", + "12345678", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "13", + "Sale", + "1", + "Test Merchant 1", + "123456781", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "14", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "InvalidEstate", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "15", + "Sale", + "1", + "InvalidMerchant", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "16", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "EmptyContract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "17", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "InvalidContract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "18", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "EmptyProduct", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "19", + "Sale", + "1", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "InvalidProduct", + "", + "", + "", + "", + "", + ""}); + table22.AddRow(new string[] { + "Today", + "20", + "Sale", + "1", + "Test Merchant 4", + "123456783", + "Test Estate 1", + "Safaricom", + "300.00", + "123456789", + "testcustomer@customer.co.uk", + "Safaricom Contract", + "Variable Topup", + "", + "", + "", + "", + "", + ""}); +#line 166 + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table22, "When "); #line hidden - global::Reqnroll.Table table159 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "1", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "2", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "3", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "4", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "5", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "6", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "7", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "9", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "10", "0000", "SUCCESS"}); - table159.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "11", "0000", "SUCCESS"}); -#line 152 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table159, "Then "); -#line hidden - global::Reqnroll.Table table160 = new global::Reqnroll.Table(new string[] { - "DateTime", - "Reference", - "EntryType", - "In", - "Out", - "ChangeAmount", - "Balance"}); - table160.AddRow(new string[] { - "Today", - "Merchant Deposit", - "C", - "240.00", - "0.00", - "265.00", - "230.00"}); - table160.AddRow(new string[] { - "Today", - "Transaction Completed", - "D", - "0.00", - "110.00", - "110.00", - "130.00"}); - table160.AddRow(new string[] { - "Today", - "Transaction Completed", - "D", - "0.00", - "90.00", - "90.00", - "30.00"}); - table160.AddRow(new string[] { - "Today", - "Transaction Completed", - "D", - "0.00", - "10.00", - "10.00", - "20.00"}); - table160.AddRow(new string[] { - "Today", - "Transaction Completed", - "D", - "0.00", - "20.00", - "20.00", + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "12", + "0000", + "SUCCESS"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "13", + "1000", + "Device Identifier 123456781 not valid for Merchant Test Merchant 1"}); + table23.AddRow(new string[] { + "InvalidEstate", + "Test Merchant 1", + "14", + "1001", + "Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate"}); + table23.AddRow(new string[] { + "Test Estate 1", + "InvalidMerchant", + "15", + "1002", + "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + + "tate [Test Estate 1]"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "16", + "1012", + "Contract Id [00000000-0000-0000-0000-000000000000] must be set for a sale transac" + + "tion"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "17", + "1015", + "Contract Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test M" + + "erchant 1]"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "18", + "1013", + "Product Id [00000000-0000-0000-0000-000000000000] must be set for a sale transact" + + "ion"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "19", + "1016", + "Product Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Me" + + "rchant 1]"}); + table23.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 4", + "20", + "1009", + "Merchant [Test Merchant 4] does not have enough credit available [100.00] to perf" + + "orm transaction amount [300.00]"}); +#line 189 + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table23, "Then "); +#line hidden + global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { + "DateTime", + "Reference", + "EntryType", + "In", + "Out", + "ChangeAmount", + "Balance"}); + table24.AddRow(new string[] { + "Today", + "Merchant Deposit", + "C", + "240.00", + "0.00", + "265.00", + "230.00"}); + table24.AddRow(new string[] { + "Today", + "Transaction Completed", + "D", + "0.00", + "110.00", + "110.00", + "130.00"}); + table24.AddRow(new string[] { + "Today", + "Transaction Completed", + "D", + "0.00", + "90.00", + "90.00", + "30.00"}); + table24.AddRow(new string[] { + "Today", + "Transaction Completed", + "D", + "0.00", + "10.00", + "10.00", + "20.00"}); + table24.AddRow(new string[] { + "Today", + "Transaction Completed", + "D", + "0.00", + "20.00", + "20.00", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1042,7 +1401,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00", "25.00", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1050,7 +1409,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.55", "0.55", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1058,7 +1417,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.45", "0.45", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1066,7 +1425,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.01", "0.10", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1074,7 +1433,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.01", "0.10", "20.00"}); - table160.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1082,11 +1441,11 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "0.00", "20.00"}); -#line 166 +#line 212 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 1\'", ((string)(null)), table160, "Then "); + "tate 1\' and merchant \'Test Merchant 1\'", ((string)(null)), table24, "Then "); #line hidden - global::Reqnroll.Table table161 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { "DateTime", "Reference", "EntryType", @@ -1094,7 +1453,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Out", "ChangeAmount", "Balance"}); - table161.AddRow(new string[] { + table25.AddRow(new string[] { "Today", "Merchant Deposit", "C", @@ -1102,7 +1461,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "110.00", "230.00"}); - table161.AddRow(new string[] { + table25.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1110,7 +1469,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "100.00", "100.00", "130.00"}); - table161.AddRow(new string[] { + table25.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1118,7 +1477,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10.00", "10.00", "30.00"}); - table161.AddRow(new string[] { + table25.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1126,7 +1485,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.50", "0.50", "20.00"}); - table161.AddRow(new string[] { + table25.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1134,11 +1493,11 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "0.00", "20.00"}); -#line 180 +#line 226 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 2\'", ((string)(null)), table161, "Then "); + "tate 1\' and merchant \'Test Merchant 2\'", ((string)(null)), table25, "Then "); #line hidden - global::Reqnroll.Table table162 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { "DateTime", "Reference", "EntryType", @@ -1146,7 +1505,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Out", "ChangeAmount", "Balance"}); - table162.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "Merchant Deposit", "C", @@ -1154,7 +1513,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "110.00", "230.00"}); - table162.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1162,7 +1521,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "100.00", "100.00", "130.00"}); - table162.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1170,7 +1529,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10.00", "10.00", "30.00"}); - table162.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1178,7 +1537,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.85", "0.50", "20.00"}); - table162.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1186,383 +1545,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "0.00", "20.00"}); -#line 188 +#line 234 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 3\'", ((string)(null)), table162, "Then "); -#line hidden - global::Reqnroll.Table table163 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table163.AddRow(new string[] { - "Today", - "12", - "Sale", - "1", - "Test Merchant 1", - "123456781", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 200 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table163, "When "); -#line hidden - global::Reqnroll.Table table164 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table164.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "12", - "1000", - "Device Identifier 123456781 not valid for Merchant Test Merchant 1"}); -#line 204 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table164, "Then "); -#line hidden - global::Reqnroll.Table table165 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table165.AddRow(new string[] { - "Today", - "13", - "Sale", - "1", - "Test Merchant 1", - "123456780", - "InvalidEstate", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 208 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table165, "When "); -#line hidden - global::Reqnroll.Table table166 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table166.AddRow(new string[] { - "InvalidEstate", - "Test Merchant 1", - "13", - "1001", - "Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate"}); -#line 212 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table166, "Then "); -#line hidden - global::Reqnroll.Table table167 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table167.AddRow(new string[] { - "Today", - "14", - "Sale", - "1", - "InvalidMerchant", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 216 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table167, "When "); -#line hidden - global::Reqnroll.Table table168 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table168.AddRow(new string[] { - "Test Estate 1", - "InvalidMerchant", - "14", - "1002", - "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + - "tate [Test Estate 1]"}); -#line 220 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table168, "Then "); -#line hidden - global::Reqnroll.Table table169 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table169.AddRow(new string[] { - "Today", - "15", - "Sale", - "1", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "EmptyContract", - "Variable Topup"}); -#line 224 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table169, "When "); -#line hidden - global::Reqnroll.Table table170 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table170.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "15", - "1012", - "Contract Id [00000000-0000-0000-0000-000000000000] must be set for a sale transac" + - "tion"}); -#line 228 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table170, "Then "); -#line hidden - global::Reqnroll.Table table171 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table171.AddRow(new string[] { - "Today", - "16", - "Sale", - "1", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "InvalidContract", - "Variable Topup"}); -#line 232 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table171, "When "); -#line hidden - global::Reqnroll.Table table172 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table172.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "16", - "1015", - "Contract Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test M" + - "erchant 1]"}); -#line 236 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table172, "Then "); -#line hidden - global::Reqnroll.Table table173 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table173.AddRow(new string[] { - "Today", - "17", - "Sale", - "1", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "EmptyProduct"}); -#line 240 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table173, "When "); -#line hidden - global::Reqnroll.Table table174 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table174.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "17", - "1013", - "Product Id [00000000-0000-0000-0000-000000000000] must be set for a sale transact" + - "ion"}); -#line 244 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table174, "Then "); -#line hidden - global::Reqnroll.Table table175 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table175.AddRow(new string[] { - "Today", - "18", - "Sale", - "1", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "InvalidProduct"}); -#line 248 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table175, "When "); -#line hidden - global::Reqnroll.Table table176 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table176.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "18", - "1016", - "Product Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Me" + - "rchant 1]"}); -#line 252 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table176, "Then "); -#line hidden - global::Reqnroll.Table table177 = new global::Reqnroll.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "TransactionSource", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table177.AddRow(new string[] { - "Today", - "19", - "Sale", - "1", - "Test Merchant 4", - "123456783", - "Test Estate 1", - "Safaricom", - "300.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 256 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table177, "When "); -#line hidden - global::Reqnroll.Table table178 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table178.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 4", - "19", - "1009", - "Merchant [Test Merchant 4] does not have enough credit available [100.00] to perf" + - "orm transaction amount [300.00]"}); -#line 260 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table178, "Then "); + "tate 1\' and merchant \'Test Merchant 3\'", ((string)(null)), table26, "Then "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs index 8ee5a079..872bb47a 100644 --- a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs @@ -1,7 +1,9 @@ -using SimpleResults; +using NLog.LayoutRenderers.Wrappers; +using SecurityService.DataTransferObjects; +using SimpleResults; using System; using System.Collections.Generic; -using SecurityService.DataTransferObjects; +using TestHosts.DataTransferObjects.AgencyBanking; using TransactionProcessor.DataTransferObjects.Requests.Contract; using TransactionProcessor.DataTransferObjects.Requests.Merchant; using TransactionProcessor.DataTransferObjects.Requests.MerchantSchedule; @@ -46,7 +48,7 @@ public SharedSteps(ScenarioContext scenarioContext, this.TestingContext = testingContext; this.SecurityServiceSteps = new SecurityServiceSteps(testingContext.DockerHelper.SecurityServiceClient); this.TransactionProcessorSteps = new TransactionProcessorSteps(testingContext.DockerHelper.TransactionProcessorClient, testingContext.DockerHelper.TestHostHttpClient, - testingContext.DockerHelper.ProjectionManagementClient); + testingContext.DockerHelper.ProjectionManagementClient, testingContext.DockerHelper.AgencyBankingClient); } //[Given(@"I have a token to access the estate management and transaction processor resources")] @@ -302,13 +304,11 @@ public async Task WhenISwapTheMerchantDeviceTheDeviceIsSwapped(DataTable table) [When(@"I create the following merchants")] public async Task WhenICreateTheFollowingMerchants(DataTable table) { - List<(EstateDetails estate, CreateMerchantRequest)> requests = table.Rows.ToCreateMerchantRequests(this.TestingContext.Estates); + List<(EstateDetails estate, CreateMerchantRequest, Boolean)> requests = table.Rows.ToCreateMerchantRequests(this.TestingContext.Estates); List verifiedMerchants = await this.TransactionProcessorSteps.WhenICreateTheFollowingMerchants(this.TestingContext.AccessToken, requests); foreach (DataTransferObjects.Responses.Merchant.MerchantResponse verifiedMerchant in verifiedMerchants){ - //await this.TransactionProcessorSteps.WhenICreateTheFollowingMerchants(this.TestingContext.AccessToken, verifiedMerchant.EstateId, verifiedMerchant.MerchantId); - EstateDetails estateDetails = this.TestingContext.GetEstateDetails(verifiedMerchant.EstateId); estateDetails.AddMerchant(verifiedMerchant); this.TestingContext.Logger.LogInformation($"Merchant {verifiedMerchant.MerchantName} created with Id {verifiedMerchant.MerchantId} for Estate {estateDetails.EstateName}"); @@ -679,5 +679,73 @@ public async Task WhenIGetTheEstateSettlementReportForEstateForMerchantWithTheDa List settlementFeeDetailsList = table.Rows.ToSettlementFeeDetails(estateName, merchantName, settlementDateString, this.TestingContext.Estates); await this.TransactionProcessorSteps.WhenIGetTheEstateSettlementReportForEstateForMerchantWithTheDateTheFollowingFeesAreSettled(this.TestingContext.AccessToken, settlementFeeDetailsList); } + + [Given("I initialise the Agency Banking Host")] + public async Task GivenIInitialiseTheAgencyBankingHost() { + SystemInitializationRequest request = new() { + DefaultCurrency = "KES", + InstitutionCode = "BANK001", + InstitutionName = "Enterprise Bank", + Timezone = "Africa/Nairobi", + SettlementMode = "NET" + }; + var result = await this.TestingContext.DockerHelper.AgencyBankingClient.InitializeSystem(request, CancellationToken.None); + result.IsSuccess.ShouldBeTrue("Error initializing Agency Banking Host"); + + } + + [Given("I create the following accounts")] + public async Task GivenICreateTheFollowingAccounts(DataTable dataTable) + { + foreach (DataTableRow dataTableRow in dataTable.Rows) { + + CreateGlAccountRequest request = new() { + Currency = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "Currency"), + GlCode = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "Code"), + GlName = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "Name"), + GlType = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "Type") + }; + Result result = await this.TestingContext.DockerHelper.AgencyBankingClient.CreateGlAccount(request, CancellationToken.None); + result.IsSuccess.ShouldBeTrue($"Error creating GL account {request.GlCode}"); + } + } + + [Given("I create the following customers")] + public async Task GivenICreateTheFollowingCustomers(DataTable dataTable) + { + foreach (DataTableRow dataTableRow in dataTable.Rows) + { + //| CustomerId | FullName | PhoneNumber | NationalId | AccountNumber | + CreateCustomerRequest request = new CreateCustomerRequest + { + AccountNumber = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "AccountNumber"), + CustomerId = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "CustomerId"), + FullName = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "FullName"), + PhoneNumber = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "PhoneNumber"), + NationalId = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "NationalId") + }; + Result result = await this.TestingContext.DockerHelper.AgencyBankingClient.CreateCustomer(request, CancellationToken.None); + result.IsSuccess.ShouldBeTrue($"Error creating customer {request.AccountNumber}"); + } + } + + [Given("I create the settlement account")] + public async Task GivenICreateTheSettlementAccount(DataTable dataTable) + { + foreach (DataTableRow dataTableRow in dataTable.Rows) + { + CreateSettlementAccountRequest request = new() + { + AccountNumber = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "AccountNumber"), + AccountName = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "AccountName"), + BankCode = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "BankCode"), + Currency = ReqnrollTableHelper.GetStringRowValue(dataTableRow, "Currency") + }; + Result result = await this.TestingContext.DockerHelper.AgencyBankingClient.CreateSettlementAccount(request, CancellationToken.None); + result.IsSuccess.ShouldBeTrue($"Error creating settlement account {request.AccountNumber}"); + } + } + + } } diff --git a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj index b924ac78..ec786756 100644 --- a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj +++ b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj @@ -38,6 +38,7 @@ + diff --git a/TransactionProcessor.Tests/HandlerTests/TransactionHandlersTests.cs b/TransactionProcessor.Tests/HandlerTests/TransactionHandlersTests.cs index 3430768e..31fd6095 100644 --- a/TransactionProcessor.Tests/HandlerTests/TransactionHandlersTests.cs +++ b/TransactionProcessor.Tests/HandlerTests/TransactionHandlersTests.cs @@ -91,6 +91,108 @@ public async Task PerformTransaction_SalePayloadWithoutTypeMetadata_SendsSaleCom mediator.VerifyAll(); } + [Fact] + public async Task PerformTransaction_SalePayloadWithoutTransactionSource_UsesDefaultSource() + { + Mock mediator = new Mock(MockBehavior.Strict); + TransactionCommands.ProcessSaleTransactionCommand? capturedCommand = null; + SaleTransactionRequest request = new SaleTransactionRequest + { + AdditionalTransactionMetadata = new Dictionary { { "amount", "12.34" } }, + ContractId = Guid.NewGuid(), + CustomerEmailAddress = "customer@test.local", + DeviceIdentifier = "device-1", + OperatorId = Guid.NewGuid(), + ProductId = Guid.NewGuid(), + TransactionDateTime = new DateTime(2024, 1, 2, 3, 4, 5), + TransactionNumber = "000002", + TransactionType = "Sale", + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + mediator.Setup(m => m.Send(It.IsAny(), + It.IsAny())) + .Callback((IRequest> request, CancellationToken _) => capturedCommand = (TransactionCommands.ProcessSaleTransactionCommand)request) + .ReturnsAsync(Result.Success(new ProcessSaleTransactionResponse + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + ResponseCode = "0000", + ResponseMessage = "SUCCESS", + TransactionId = Guid.NewGuid() + })); + + IResult result = await TransactionHandlers.PerformSaleTransaction(mediator.Object, + new DefaultHttpContext(), + request, + CancellationToken.None); + + result.ShouldNotBeNull(); + capturedCommand.ShouldNotBeNull(); + capturedCommand!.TransactionSource.ShouldBe(1); + mediator.VerifyAll(); + } + + [Fact] + public async Task PerformTransaction_SalePayload_WhenMediatorFails_ReturnsFailure() + { + Mock mediator = new Mock(MockBehavior.Strict); + SaleTransactionRequest request = new SaleTransactionRequest + { + AdditionalTransactionMetadata = new Dictionary { { "amount", "12.34" } }, + ContractId = Guid.NewGuid(), + CustomerEmailAddress = "customer@test.local", + DeviceIdentifier = "device-1", + OperatorId = Guid.NewGuid(), + ProductId = Guid.NewGuid(), + TransactionDateTime = new DateTime(2024, 1, 2, 3, 4, 5), + TransactionNumber = "000002", + TransactionType = "Sale", + TransactionSource = 2, + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + mediator.Setup(m => m.Send(It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Failure("boom").ToResult(new ProcessSaleTransactionResponse())); + + IResult result = await TransactionHandlers.PerformSaleTransaction(mediator.Object, + new DefaultHttpContext(), + request, + CancellationToken.None); + + result.ShouldNotBeNull(); + mediator.VerifyAll(); + } + + [Fact] + public async Task PerformTransaction_LogonPayload_WhenMediatorFails_ReturnsFailure() + { + Mock mediator = new Mock(MockBehavior.Strict); + LogonTransactionRequest request = new LogonTransactionRequest + { + DeviceIdentifier = "device-1", + TransactionDateTime = DateTime.SpecifyKind(new DateTime(2024, 1, 2, 3, 4, 5), DateTimeKind.Utc), + TransactionNumber = "000001", + TransactionType = "Logon", + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId + }; + + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("boom").ToResult(new ProcessLogonTransactionResponse())); + + IResult result = await TransactionHandlers.PerformLogonTransaction(mediator.Object, + new DefaultHttpContext(), + request, + CancellationToken.None); + + result.ShouldNotBeNull(); + mediator.VerifyAll(); + } + [Fact] public async Task PerformTransaction_ReconciliationPayloadWithoutTypeMetadata_SendsReconciliationCommand() { @@ -127,6 +229,53 @@ public async Task PerformTransaction_ReconciliationPayloadWithoutTypeMetadata_Se mediator.VerifyAll(); } + [Fact] + public async Task PerformTransaction_ReconciliationPayload_WhenMediatorFails_ReturnsFailure() + { + Mock mediator = new Mock(MockBehavior.Strict); + ReconciliationRequest request = new ReconciliationRequest + { + DeviceIdentifier = "device-1", + OperatorTotals = new List(), + TransactionCount = 4, + TransactionDateTime = new DateTime(2024, 1, 2, 3, 4, 5), + TransactionValue = 42.50m, + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + TransactionType = "Reconciliation" + }; + + mediator.Setup(m => m.Send(It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Failure("boom").ToResult(new ProcessReconciliationTransactionResponse())); + + IResult result = await TransactionHandlers.PerformReconciliationTransaction(mediator.Object, + new DefaultHttpContext(), + request, + CancellationToken.None); + + result.ShouldNotBeNull(); + mediator.VerifyAll(); + } + + [Fact] + public async Task ResendTransactionReceipt_SendsCommand() + { + Mock mediator = new Mock(MockBehavior.Strict); + + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + IResult result = await TransactionHandlers.ResendTransactionReceipt(mediator.Object, + new DefaultHttpContext(), + TestData.EstateId, + Guid.NewGuid(), + CancellationToken.None); + + result.ShouldNotBeNull(); + mediator.VerifyAll(); + } + private static class TestData { public static Guid EstateId => Guid.Parse("11111111-1111-1111-1111-111111111111"); diff --git a/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs b/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs new file mode 100644 index 00000000..33dd6c8e --- /dev/null +++ b/TransactionProcessor.Tests/HandlerTests/WebHandlersTests.cs @@ -0,0 +1,1167 @@ +using MediatR; +using Microsoft.AspNetCore.Http; +using Moq; +using Shouldly; +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using TransactionProcessor.BusinessLogic.Requests; +using TransactionProcessor.DataTransferObjects; +using TransactionProcessor.DataTransferObjects.Requests.Contract; +using TransactionProcessor.DataTransferObjects.Requests.Estate; +using TransactionProcessor.DataTransferObjects.Requests.Merchant; +using TransactionProcessor.DataTransferObjects.Requests.MerchantSchedule; +using TransactionProcessor.DataTransferObjects.Requests.Operator; +using TransactionProcessor.DataTransferObjects.Responses.Contract; +using TransactionProcessor.Handlers; +using TransactionProcessor.Models; +using TransactionProcessor.Models.Contract; +using TransactionProcessor.Models.Estate; +using TransactionProcessor.Models.MerchantSchedule; +using TransactionProcessor.Models.Operator; +using TransactionProcessor.Models.Settlement; +using TransactionProcessor.ProjectionEngine.Models; +using TransactionProcessor.ProjectionEngine.State; +using Xunit; +using MerchantModel = TransactionProcessor.Models.Merchant.Merchant; +using EstateModel = TransactionProcessor.Models.Estate.Estate; +using ProjectionMerchant = TransactionProcessor.ProjectionEngine.State.Merchant; +using OperatorModel = TransactionProcessor.Models.Operator.Operator; +using RedeemVoucherModel = TransactionProcessor.Models.RedeemVoucherResponse; + +namespace TransactionProcessor.Tests.HandlerTests; + +public class WebHandlersTests +{ + private static readonly Guid EstateId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + private static readonly Guid MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222222"); + private static readonly Guid ContractId = Guid.Parse("33333333-3333-3333-3333-333333333333"); + private static readonly Guid ProductId = Guid.Parse("44444444-4444-4444-4444-444444444444"); + private static readonly Guid OperatorId = Guid.Parse("55555555-5555-5555-5555-555555555555"); + private static readonly Guid TransactionId = Guid.Parse("66666666-6666-6666-6666-666666666666"); + private static readonly Guid FloatId = Guid.Parse("77777777-7777-7777-7777-777777777777"); + private static readonly Guid SettlementId = Guid.Parse("88888888-8888-8888-8888-888888888888"); + private static readonly Guid AddressId = Guid.Parse("99999999-9999-9999-9999-999999999999"); + private static readonly Guid ContactId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"); + private static readonly Guid VoucherTransactionId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"); + private static readonly string VoucherCode = "VOUCHER-1"; + private static readonly DateTime Instant = new DateTime(2024, 1, 2, 3, 4, 5, DateTimeKind.Utc); + + [Theory] + [MemberData(nameof(ContractCases))] + public async Task ContractHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(EstateCases))] + public async Task EstateHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(FloatCases))] + public async Task FloatHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(MerchantCases))] + public async Task MerchantHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(OperatorCases))] + public async Task OperatorHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(SettlementCases))] + public async Task SettlementHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + [Theory] + [MemberData(nameof(VoucherCases))] + public async Task VoucherHandlers_AreCovered(HandlerCase testCase) + { + Mock mediator = new(MockBehavior.Strict); + testCase.Setup(mediator); + + IResult result = await testCase.Invoke(mediator.Object); + + result.ShouldNotBeNull(); + testCase.Verify?.Invoke(mediator); + if (testCase.Verify is null) + { + mediator.VerifyAll(); + } + } + + public static IEnumerable ContractCases() + { + yield return Case( + "GetContract", + SetupSuccess(new Contract + { + ContractId = ContractId, + EstateId = EstateId, + Description = "Contract" + }), + mediator => ContractHandlers.GetContract(mediator, new DefaultHttpContext(), EstateId, ContractId, CancellationToken.None)); + + yield return Case( + "GetContracts", + SetupSuccess>(new List + { + new() + { + ContractId = ContractId, + EstateId = EstateId, + Description = "Contract" + } + }), + mediator => ContractHandlers.GetContracts(mediator, new DefaultHttpContext(), EstateId, CancellationToken.None)); + + yield return Case( + "AddProductToContract", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.ContractId.ShouldBe(ContractId); + command.ProductId.ShouldNotBe(Guid.Empty); + command.RequestDTO.DisplayText.ShouldBe("Display"); + }), + mediator => ContractHandlers.AddProductToContract(mediator, + new DefaultHttpContext(), + EstateId, + ContractId, + new AddProductToContractRequest + { + DisplayText = "Display", + ProductName = "Product", + ProductType = TransactionProcessor.DataTransferObjects.Responses.Contract.ProductType.MobileTopup, + Value = 12.34m + }, + CancellationToken.None)); + + yield return Case( + "AddTransactionFeeForProductToContract", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.ContractId.ShouldBe(ContractId); + command.ProductId.ShouldBe(ProductId); + command.TransactionFeeId.ShouldNotBe(Guid.Empty); + }), + mediator => ContractHandlers.AddTransactionFeeForProductToContract(mediator, + new DefaultHttpContext(), + EstateId, + ContractId, + ProductId, + new AddTransactionFeeForProductToContractRequest + { + CalculationType = TransactionProcessor.DataTransferObjects.Responses.Contract.CalculationType.Fixed, + Description = "Fee", + FeeType = TransactionProcessor.DataTransferObjects.Responses.Contract.FeeType.Merchant, + Value = 1.23m + }, + CancellationToken.None)); + + yield return Case( + "DisableTransactionFeeForProduct", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.ContractId.ShouldBe(ContractId); + command.ProductId.ShouldBe(ProductId); + command.TransactionFeeId.ShouldBe(SettlementId); + }), + mediator => ContractHandlers.DisableTransactionFeeForProduct(mediator, + new DefaultHttpContext(), + EstateId, + ContractId, + ProductId, + SettlementId, + CancellationToken.None)); + + yield return Case( + "CreateContract", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.ContractId.ShouldNotBe(Guid.Empty); + command.RequestDTO.Description.ShouldBe("Contract"); + }), + mediator => ContractHandlers.CreateContract(mediator, + new DefaultHttpContext(), + EstateId, + new CreateContractRequest + { + OperatorId = OperatorId, + Description = "Contract" + }, + CancellationToken.None)); + } + + public static IEnumerable EstateCases() + { + yield return Case( + "CreateEstate", + SetupSuccess(command => + { + command.RequestDto.EstateId.ShouldBe(EstateId); + command.RequestDto.EstateName.ShouldBe("Estate"); + }), + mediator => EstateHandlers.CreateEstate(mediator, + new DefaultHttpContext(), + new CreateEstateRequest + { + EstateId = EstateId, + EstateName = "Estate" + }, + CancellationToken.None)); + + yield return Case( + "GetEstate", + SetupSuccess(new EstateModel + { + EstateId = EstateId, + Name = "Estate" + }), + mediator => EstateHandlers.GetEstate(mediator, new DefaultHttpContext(), EstateId, CancellationToken.None)); + + yield return Case( + "GetEstates", + SetupSuccess>(new List + { + new() + { + EstateId = EstateId, + Name = "Estate" + } + }), + mediator => EstateHandlers.GetEstates(mediator, new DefaultHttpContext(), EstateId, CancellationToken.None)); + + yield return Case( + "CreateEstateUser", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.RequestDto.EmailAddress.ShouldBe("user@test.local"); + }), + mediator => EstateHandlers.CreateEstateUser(mediator, + new DefaultHttpContext(), + EstateId, + new CreateEstateUserRequest + { + EmailAddress = "user@test.local", + Password = "password", + GivenName = "Given", + FamilyName = "Family" + }, + CancellationToken.None)); + + yield return Case( + "AssignOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.RequestDto.OperatorId.ShouldBe(OperatorId); + }), + mediator => EstateHandlers.AssignOperator(mediator, + new DefaultHttpContext(), + EstateId, + new TransactionProcessor.DataTransferObjects.Requests.Estate.AssignOperatorRequest { OperatorId = OperatorId }, + CancellationToken.None)); + + yield return Case( + "RemoveOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.OperatorId.ShouldBe(OperatorId); + }), + mediator => EstateHandlers.RemoveOperator(mediator, + new DefaultHttpContext(), + EstateId, + OperatorId, + CancellationToken.None)); + } + + public static IEnumerable FloatCases() + { + yield return Case( + "CreateFloatForContractProduct", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.ContractId.ShouldBe(ContractId); + command.ProductId.ShouldBe(ProductId); + }), + mediator => FloatHandlers.CreateFloatForContractProduct(mediator, + new DefaultHttpContext(), + EstateId, + new CreateFloatForContractProductRequest + { + ContractId = ContractId, + ProductId = ProductId, + CreateDateTime = Instant + }, + CancellationToken.None)); + + yield return Case( + "RecordFloatCreditPurchase", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.FloatId.ShouldBe(FloatId); + command.CreditAmount.ShouldBe(10m); + command.CostPrice.ShouldBe(8m); + }), + mediator => FloatHandlers.RecordFloatCreditPurchase(mediator, + new DefaultHttpContext(), + EstateId, + new RecordFloatCreditPurchaseRequest + { + FloatId = FloatId, + PurchaseDateTime = Instant, + CreditAmount = 10m, + CostPrice = 8m + }, + CancellationToken.None)); + } + + public static IEnumerable MerchantCases() + { + yield return Case( + "GetMerchantBalance", + SetupSuccess(new MerchantBalanceState + { + EstateId = EstateId, + MerchantId = MerchantId, + Balance = 12m, + AvailableBalance = 8m + }), + mediator => MerchantHandlers.GetMerchantBalance(mediator, new DefaultHttpContext(), EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetMerchantBalanceLive", + SetupSuccess( + new MerchantBalanceProjectionState1(new ProjectionMerchant(MerchantId.ToString(), "Merchant", 1, 9m))), + mediator => MerchantHandlers.GetMerchantBalanceLive(mediator, new DefaultHttpContext(), EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetMerchantBalanceHistory", + SetupSuccess>(new List + { + new() + { + EstateId = EstateId, + MerchantId = MerchantId, + ChangeAmount = 3m, + Balance = 12m, + DateTime = Instant, + DebitOrCredit = "Credit", + OriginalEventId = TransactionId, + Reference = "REF" + } + }), + mediator => MerchantHandlers.GetMerchantBalanceHistory(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + Instant.AddDays(-1), + Instant, + CancellationToken.None)); + + yield return Case( + "CreateMerchant", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.RequestDto.Name.ShouldBe("Merchant"); + }), + mediator => MerchantHandlers.CreateMerchant(mediator, + new DefaultHttpContext(), + EstateId, + new CreateMerchantRequest + { + MerchantId = MerchantId, + Name = "Merchant" + }, + CancellationToken.None)); + + yield return Case( + "AssignOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.OperatorId.ShouldBe(OperatorId); + }), + mediator => MerchantHandlers.AssignOperator(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new TransactionProcessor.DataTransferObjects.Requests.Merchant.AssignOperatorRequest + { + OperatorId = OperatorId + }, + CancellationToken.None)); + + yield return Case( + "RemoveOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.OperatorId.ShouldBe(OperatorId); + }), + mediator => MerchantHandlers.RemoveOperator(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + OperatorId, + CancellationToken.None)); + + yield return Case( + "AddDevice", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.DeviceIdentifier.ShouldBe("device-1"); + }), + mediator => MerchantHandlers.AddDevice(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new AddMerchantDeviceRequest + { + DeviceIdentifier = "device-1" + }, + CancellationToken.None)); + + yield return Case( + "SwapMerchantDevice", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.DeviceIdentifier.ShouldBe("device-old"); + command.RequestDto.NewDeviceIdentifier.ShouldBe("device-new"); + }), + mediator => MerchantHandlers.SwapMerchantDevice(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + "device-old", + new SwapMerchantDeviceRequest + { + NewDeviceIdentifier = "device-new" + }, + CancellationToken.None)); + + yield return Case( + "AddContract", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.ContractId.ShouldBe(ContractId); + }), + mediator => MerchantHandlers.AddContract(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new AddMerchantContractRequest + { + ContractId = ContractId + }, + CancellationToken.None)); + + yield return Case( + "RemoveContract", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.ContractId.ShouldBe(ContractId); + }), + mediator => MerchantHandlers.RemoveContract(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + ContractId, + CancellationToken.None)); + + yield return Case( + "CreateMerchantUser", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.EmailAddress.ShouldBe("user@test.local"); + }), + mediator => MerchantHandlers.CreateMerchantUser(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new CreateMerchantUserRequest + { + EmailAddress = "user@test.local", + Password = "password", + GivenName = "Given", + MiddleName = "Middle", + FamilyName = "Family" + }, + CancellationToken.None)); + + yield return Case( + "MakeDeposit", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.DepositSource.ShouldBe(TransactionProcessor.DataTransferObjects.Requests.Merchant.MerchantDepositSource.Manual); + command.RequestDto.Amount.ShouldBe(15.50m); + }), + mediator => MerchantHandlers.MakeDeposit(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new MakeMerchantDepositRequest + { + Amount = 15.50m, + DepositDateTime = Instant, + Reference = "DEP-1" + }, + CancellationToken.None)); + + yield return Case( + "MakeWithdrawal", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.Amount.ShouldBe(9.25m); + }), + mediator => MerchantHandlers.MakeWithdrawal(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new MakeMerchantWithdrawalRequest + { + Amount = 9.25m, + WithdrawalDateTime = Instant, + Reference = "WDL-1" + }, + CancellationToken.None)); + + yield return Case( + "UpdateMerchant", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.Name.ShouldBe("Merchant"); + }), + mediator => MerchantHandlers.UpdateMerchant(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new UpdateMerchantRequest + { + Name = "Merchant" + }, + CancellationToken.None)); + + yield return Case( + "UpdateMerchantOpening", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.Sunday.ShouldNotBeNull(); + }), + mediator => MerchantHandlers.UpdateMerchantOpening(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new MerchantOpeningRequest + { + Sunday = new TransactionProcessor.DataTransferObjects.Requests.Merchant.OpeningHours + { + Opening = "08:00", + Closing = "17:00" + } + }, + CancellationToken.None)); + + yield return Case( + "CreateMerchantSchedule", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.Year.ShouldBe(2024); + }), + mediator => MerchantHandlers.CreateMerchantSchedule(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new CreateMerchantScheduleRequest + { + Year = 2024, + Months = new List + { + new() + { + Month = 1, + ClosedDays = new List { 1, 2 } + } + } + }, + CancellationToken.None)); + + yield return Case( + "UpdateMerchantSchedule", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.Year.ShouldBe(2024); + }), + mediator => MerchantHandlers.UpdateMerchantSchedule(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + 2024, + new UpdateMerchantScheduleRequest + { + Months = new List + { + new() + { + Month = 2, + ClosedDays = new List { 10 } + } + } + }, + CancellationToken.None)); + + yield return Case( + "GetMerchantSchedule", + SetupSuccess(new TransactionProcessor.Models.MerchantSchedule.MerchantSchedule + { + MerchantScheduleId = Guid.NewGuid(), + EstateId = EstateId, + MerchantId = MerchantId, + Year = 2024, + Months = new List + { + new() + { + Month = 3, + ClosedDays = new List { 5, 6 } + } + } + }), + mediator => MerchantHandlers.GetMerchantSchedule(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + 2024, + CancellationToken.None)); + + yield return Case( + "GetMerchantScheduleFromReadModel", + SetupSuccess(new TransactionProcessor.Models.MerchantSchedule.MerchantSchedule + { + MerchantScheduleId = Guid.NewGuid(), + EstateId = EstateId, + MerchantId = MerchantId, + Year = 2025, + Months = new List + { + new() + { + Month = 8, + ClosedDays = new List { 1 } + } + } + }), + mediator => MerchantHandlers.GetMerchantScheduleFromReadModel(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + 2025, + CancellationToken.None)); + + yield return Case( + "AddMerchantAddress", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.AddressLine1.ShouldBe("Line 1"); + }), + mediator => MerchantHandlers.AddMerchantAddress(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new TransactionProcessor.DataTransferObjects.Requests.Merchant.Address + { + AddressLine1 = "Line 1", + AddressLine2 = "Line 2", + Country = "GB", + PostalCode = "PC1 1AA", + Region = "Region", + Town = "Town" + }, + CancellationToken.None)); + + yield return Case( + "UpdateMerchantAddress", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.AddressId.ShouldBe(AddressId); + command.RequestDto.AddressLine1.ShouldBe("Updated Line 1"); + }), + mediator => MerchantHandlers.UpdateMerchantAddress(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + AddressId, + new TransactionProcessor.DataTransferObjects.Requests.Merchant.Address + { + AddressLine1 = "Updated Line 1", + Country = "GB", + PostalCode = "PC1 2BB", + Region = "Updated Region", + Town = "Updated Town" + }, + CancellationToken.None)); + + yield return Case( + "AddMerchantContact", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.ContactName.ShouldBe("Contact"); + }), + mediator => MerchantHandlers.AddMerchantContact(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new TransactionProcessor.DataTransferObjects.Requests.Merchant.Contact + { + ContactName = "Contact", + EmailAddress = "contact@test.local", + PhoneNumber = "01234 567890" + }, + CancellationToken.None)); + + yield return Case( + "UpdateMerchantContact", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.ContactId.ShouldBe(ContactId); + command.RequestDto.EmailAddress.ShouldBe("updated@test.local"); + }), + mediator => MerchantHandlers.UpdateMerchantContact(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + ContactId, + new TransactionProcessor.DataTransferObjects.Requests.Merchant.Contact + { + ContactName = "Updated Contact", + EmailAddress = "updated@test.local", + PhoneNumber = "09876 543210" + }, + CancellationToken.None)); + + yield return Case( + "GetMerchant", + SetupSuccess(new MerchantModel + { + MerchantId = MerchantId, + EstateId = EstateId, + MerchantName = "Merchant" + }), + mediator => MerchantHandlers.GetMerchant(mediator, new DefaultHttpContext(), EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetMerchantContracts", + SetupSuccess>(new List + { + new() + { + ContractId = ContractId, + EstateId = EstateId, + Description = "Contract" + } + }), + mediator => MerchantHandlers.GetMerchantContracts(mediator, new DefaultHttpContext(), EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetMerchants", + SetupSuccess>(new List + { + new() + { + MerchantId = MerchantId, + EstateId = EstateId, + MerchantName = "Merchant" + } + }), + mediator => MerchantHandlers.GetMerchants(mediator, new DefaultHttpContext(), EstateId, CancellationToken.None)); + + yield return Case( + "GetTransactionFeesForProduct", + SetupSuccess>(new List()), + mediator => MerchantHandlers.GetTransactionFeesForProduct(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + ContractId, + ProductId, + CancellationToken.None)); + + yield return Case( + "GenerateMerchantStatement", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.MerchantId.ShouldBe(MerchantId); + command.RequestDto.MerchantStatementDate.ShouldBe(Instant); + }), + mediator => MerchantHandlers.GenerateMerchantStatement(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + new GenerateMerchantStatementRequest + { + MerchantStatementDate = Instant + }, + CancellationToken.None)); + } + + public static IEnumerable OperatorCases() + { + yield return Case( + "CreateOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.RequestDto.Name.ShouldBe("Operator"); + }), + mediator => OperatorHandlers.CreateOperator(mediator, + new DefaultHttpContext(), + EstateId, + new CreateOperatorRequest + { + OperatorId = OperatorId, + Name = "Operator" + }, + CancellationToken.None)); + + yield return Case( + "UpdateOperator", + SetupSuccess(command => + { + command.EstateId.ShouldBe(EstateId); + command.OperatorId.ShouldBe(OperatorId); + }), + mediator => OperatorHandlers.UpdateOperator(mediator, + new DefaultHttpContext(), + EstateId, + OperatorId, + new UpdateOperatorRequest + { + Name = "Operator" + }, + CancellationToken.None)); + + yield return Case( + "GetOperator", + SetupSuccess(new OperatorModel + { + OperatorId = OperatorId, + Name = "Operator" + }), + mediator => OperatorHandlers.GetOperator(mediator, new DefaultHttpContext(), EstateId, OperatorId, CancellationToken.None)); + + yield return Case( + "GetOperators", + SetupSuccess>(new List + { + new() + { + OperatorId = OperatorId, + Name = "Operator" + } + }), + mediator => OperatorHandlers.GetOperators(mediator, new DefaultHttpContext(), EstateId, CancellationToken.None)); + } + + public static IEnumerable SettlementCases() + { + yield return Case( + "GetPendingSettlement", + SetupSuccess(new PendingSettlementModel + { + EstateId = EstateId, + MerchantId = MerchantId, + SettlementDate = Instant, + NumberOfFeesPendingSettlement = 2, + NumberOfFeesSettled = 1, + SettlementCompleted = false + }), + mediator => SettlementHandlers.GetPendingSettlement(mediator, new DefaultHttpContext(), Instant, EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "ProcessSettlement", + SetupSuccess(SettlementId), + mediator => SettlementHandlers.ProcessSettlement(mediator, new DefaultHttpContext(), Instant, EstateId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetSettlement", + SetupSuccess(new SettlementModel + { + SettlementId = SettlementId, + SettlementDate = Instant, + IsCompleted = true, + NumberOfFeesSettled = 1, + ValueOfFeesSettled = 12m, + SettlementFees = new List + { + new() + { + TransactionId = TransactionId, + MerchantId = MerchantId, + MerchantName = "Merchant", + SettlementId = SettlementId, + SettlementDate = Instant, + CalculatedValue = 1.5m, + FeeDescription = "Fee", + IsSettled = true, + OperatorIdentifier = "OP-1" + } + } + }), + mediator => SettlementHandlers.GetSettlement(mediator, new DefaultHttpContext(), EstateId, SettlementId, MerchantId, CancellationToken.None)); + + yield return Case( + "GetSettlements", + SetupSuccess>(new List + { + new() + { + SettlementId = SettlementId, + SettlementDate = Instant, + IsCompleted = true, + NumberOfFeesSettled = 1, + ValueOfFeesSettled = 12m, + SettlementFees = new List + { + new() + { + TransactionId = TransactionId, + MerchantId = MerchantId, + MerchantName = "Merchant", + SettlementDate = Instant, + SettlementId = SettlementId, + CalculatedValue = 1.5m, + FeeDescription = "Fee", + IsSettled = true, + OperatorIdentifier = "OP-1" + } + } + } + }), + mediator => SettlementHandlers.GetSettlements(mediator, + new DefaultHttpContext(), + EstateId, + MerchantId, + "2024-01-01", + "2024-01-31", + CancellationToken.None)); + } + + public static IEnumerable VoucherCases() + { + yield return Case( + "RedeemVoucher_ExplicitDate", + SetupSuccess(new RedeemVoucherModel + { + VoucherCode = VoucherCode, + RemainingBalance = 10m, + ExpiryDate = Instant.AddDays(1) + }), + mediator => VoucherHandlers.RedeemVoucher(mediator, + new DefaultHttpContext(), + new RedeemVoucherRequest + { + EstateId = EstateId, + VoucherCode = VoucherCode, + RedeemedDateTime = Instant + }, + CancellationToken.None)); + + yield return Case( + "RedeemVoucher_DefaultDate", + SetupSuccess(new RedeemVoucherModel + { + VoucherCode = VoucherCode, + RemainingBalance = 10m, + ExpiryDate = Instant.AddDays(1) + }, command => + { + command.EstateId.ShouldBe(EstateId); + command.VoucherCode.ShouldBe(VoucherCode); + command.RedeemedDateTime.ShouldBeGreaterThan(DateTime.Now.AddSeconds(-5)); + command.RedeemedDateTime.ShouldBeLessThan(DateTime.Now.AddSeconds(5)); + }), + mediator => VoucherHandlers.RedeemVoucher(mediator, + new DefaultHttpContext(), + new RedeemVoucherRequest + { + EstateId = EstateId, + VoucherCode = VoucherCode + }, + CancellationToken.None)); + + yield return Case( + "GetVoucher_ByCode", + SetupSuccess(new Voucher + { + VoucherCode = VoucherCode, + EstateId = EstateId + }), + mediator => VoucherHandlers.GetVoucher(mediator, new DefaultHttpContext(), EstateId, VoucherCode, null, CancellationToken.None)); + + yield return Case( + "GetVoucher_ByTransactionId", + SetupSuccess(new Voucher + { + VoucherCode = VoucherCode, + EstateId = EstateId, + TransactionId = VoucherTransactionId + }), + mediator => VoucherHandlers.GetVoucher(mediator, new DefaultHttpContext(), EstateId, "", VoucherTransactionId, CancellationToken.None)); + + yield return Case( + "GetVoucher_Invalid", + mediator => { }, + mediator => VoucherHandlers.GetVoucher(mediator, new DefaultHttpContext(), EstateId, "", null, CancellationToken.None), + mediator => mediator.VerifyNoOtherCalls()); + } + + private static object[] Case(string name, Action> setup, Func> invoke, Action>? verify = null) + => new object[] { new HandlerCase(name, setup, invoke, verify) }; + + private static Action> SetupSuccess(TResponse response, Action? capture = null) + where TRequest : class, IRequest> + { + return mediator => + { + mediator.Setup(m => m.Send(It.IsAny>>(), It.IsAny())) + .Callback((IRequest> request, CancellationToken _) => capture?.Invoke((TRequest)request)) + .ReturnsAsync(Result.Success(response)); + }; + } + + private static Action> SetupSuccess(Action? capture = null) + where TRequest : class, IRequest + { + return mediator => + { + mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) + .Callback((IRequest request, CancellationToken _) => capture?.Invoke((TRequest)request)) + .ReturnsAsync(Result.Success()); + }; + } + + public sealed record HandlerCase(string Name, + Action> Setup, + Func> Invoke, + Action>? Verify = null); +} diff --git a/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs b/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs index a2b51790..469f7fff 100644 --- a/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs +++ b/TransactionProcessor/Bootstrapper/MiddlewareRegistry.cs @@ -49,7 +49,7 @@ public MiddlewareRegistry() KurrentDBClientSettings eventStoreClientSettings = KurrentDBClientSettings.Create(connectionString); this.ConfigureHealthChecks(eventStoreClientSettings); - this.ConfigureSwagger(); + //this.ConfigureSwagger(); this.ConfigureAuthentication(); this.ConfigureControllers(); this.ConfigureMvc(); @@ -187,6 +187,7 @@ private HttpClientHandler ApiEndpointHttpHandler(IServiceProvider serviceProvide #endregion } + [ExcludeFromCodeCoverage] public static class JsonSerializerConfiguration { public static void ConfigureMinimalApi(JsonSerializerOptions serializerOptions) diff --git a/TransactionProcessor/Bootstrapper/OperatorRegistry.cs b/TransactionProcessor/Bootstrapper/OperatorRegistry.cs index 6c725a00..5095c3ce 100644 --- a/TransactionProcessor/Bootstrapper/OperatorRegistry.cs +++ b/TransactionProcessor/Bootstrapper/OperatorRegistry.cs @@ -1,4 +1,5 @@ using Shared.Middleware; +using TransactionProcessor.BusinessLogic.OperatorInterfaces.AgencyBanking; namespace TransactionProcessor.Bootstrapper { @@ -33,12 +34,14 @@ public OperatorRegistry() this.ConfigureOperator("Safaricom"); this.ConfigureOperator("PataPawaPostPay"); this.ConfigureOperator("PataPawaPrePay"); + this.ConfigureOperator("AgencyBanking"); this.AddSingleton(new PataPawaPostPayServiceClient(PataPawaPostPayServiceClient.EndpointConfiguration.BasicHttpBinding_IPataPawaPostPayService)); this.For().Add().Named("Safaricom").Singleton(); this.For().Add().Named("PataPawaPostPay").Singleton(); this.For().Add().Named("PataPawaPrePay").Singleton(); this.For().Add().Named("Voucher").Singleton(); + this.For().Add().Named("AgencyBanking").Singleton(); this.AddTransient>(context => diff --git a/TransactionProcessor/Endpoints/ContractEndpoints.cs b/TransactionProcessor/Endpoints/ContractEndpoints.cs index d489ba0f..c513d925 100644 --- a/TransactionProcessor/Endpoints/ContractEndpoints.cs +++ b/TransactionProcessor/Endpoints/ContractEndpoints.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; @@ -6,6 +7,7 @@ namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class ContractEndpoints { private const string BaseRoute = "/api/estates/{estateId:guid}/contracts"; diff --git a/TransactionProcessor/Endpoints/EstateEndpoints.cs b/TransactionProcessor/Endpoints/EstateEndpoints.cs index 0d5638b9..f76cc309 100644 --- a/TransactionProcessor/Endpoints/EstateEndpoints.cs +++ b/TransactionProcessor/Endpoints/EstateEndpoints.cs @@ -4,11 +4,13 @@ using Microsoft.AspNetCore.Routing; using Shared.Authorisation; using Shared.General; +using System.Diagnostics.CodeAnalysis; using System.Security.Claims; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class EstateEndpoints { private const string BaseRoute = "/api/estates"; diff --git a/TransactionProcessor/Endpoints/FloatEndpoints.cs b/TransactionProcessor/Endpoints/FloatEndpoints.cs index 603b1cba..a1b862ed 100644 --- a/TransactionProcessor/Endpoints/FloatEndpoints.cs +++ b/TransactionProcessor/Endpoints/FloatEndpoints.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class FloatEndpoints { private const string BaseRoute = "/api/estates/{estateId:guid}/floats"; diff --git a/TransactionProcessor/Endpoints/MerchantEndpoints.cs b/TransactionProcessor/Endpoints/MerchantEndpoints.cs index eed8e18d..8b57ed89 100644 --- a/TransactionProcessor/Endpoints/MerchantEndpoints.cs +++ b/TransactionProcessor/Endpoints/MerchantEndpoints.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints; +[ExcludeFromCodeCoverage] public static class MerchantEndpoints { private const string BaseRoute = "/api/estates/{estateId:guid}/merchants"; diff --git a/TransactionProcessor/Endpoints/OperatorEndpoints.cs b/TransactionProcessor/Endpoints/OperatorEndpoints.cs index b15ce4db..6997a256 100644 --- a/TransactionProcessor/Endpoints/OperatorEndpoints.cs +++ b/TransactionProcessor/Endpoints/OperatorEndpoints.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class OperatorEndpoints { private const string BaseRoute = "/api/estates/{estateId:guid}/operators"; diff --git a/TransactionProcessor/Endpoints/SettlementEndpoints.cs b/TransactionProcessor/Endpoints/SettlementEndpoints.cs index 34d5ed4b..0678e7e6 100644 --- a/TransactionProcessor/Endpoints/SettlementEndpoints.cs +++ b/TransactionProcessor/Endpoints/SettlementEndpoints.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class SettlementEndpoints { private const string BaseRoute = "/api/estates/{estateId:guid}/settlements"; diff --git a/TransactionProcessor/Endpoints/TransactionEndpoints.cs b/TransactionProcessor/Endpoints/TransactionEndpoints.cs index db014212..5cd2121e 100644 --- a/TransactionProcessor/Endpoints/TransactionEndpoints.cs +++ b/TransactionProcessor/Endpoints/TransactionEndpoints.cs @@ -2,20 +2,20 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class TransactionEndpoints { private const string BaseRoute = "/api/transactions"; - public static IEndpointRouteBuilder MapTransactionEndpoints(this IEndpointRouteBuilder endpoints) - { - var group = endpoints.MapGroup(BaseRoute) - .WithTags("Transactions") - .RequireAuthorization() - .RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy); + public static IEndpointRouteBuilder MapTransactionEndpoints(this IEndpointRouteBuilder endpoints) { + var group = endpoints.MapGroup(BaseRoute).WithTags("Transactions"); + //.RequireAuthorization() + //.RequireAuthorization(AuthorizationExtensions.PolicyNames.ClientCredentialsOnlyPolicy); // POST /api/transactions - rejects password tokens => require client credentials group.MapPost("/logon", TransactionHandlers.PerformLogonTransaction) diff --git a/TransactionProcessor/Endpoints/VoucherEndpoints.cs b/TransactionProcessor/Endpoints/VoucherEndpoints.cs index beb051c7..66fbf015 100644 --- a/TransactionProcessor/Endpoints/VoucherEndpoints.cs +++ b/TransactionProcessor/Endpoints/VoucherEndpoints.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Shared.Authorisation; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.Handlers; namespace TransactionProcessor.Endpoints { + [ExcludeFromCodeCoverage] public static class VoucherEndpoints { private const string BaseRoute = "/api/vouchers"; diff --git a/TransactionProcessor/Startup.cs b/TransactionProcessor/Startup.cs index 8812afa6..41d34cbe 100644 --- a/TransactionProcessor/Startup.cs +++ b/TransactionProcessor/Startup.cs @@ -118,9 +118,9 @@ public void Configure(IApplicationBuilder app, }); this.ConfigureEndpoints(app); - app.UseSwagger(); + //app.UseSwagger(); - app.UseSwaggerUI(); + //app.UseSwaggerUI(); app.PreWarm(); this.TraceLoadedTokenAssemblies(); diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index b327a7b9..120e738d 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -217,6 +217,10 @@ "Username": "operatora", "Password": "1234567898", "ApiLogonRequired": true + }, + "AgencyBanking": { + "Url": "http://192.168.1.163:9000/api/agencybanking", + "ApiLogonRequired": false } } } \ No newline at end of file From 168c9ad786feab4c59412aeaa18ddf8d20f3f0a2 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 20 Jul 2026 18:30:25 +0100 Subject: [PATCH 2/2] :| --- TransactionProcessor.IntegrationTests/Common/GenericSteps.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs index 8e558f55..d272fb76 100644 --- a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs +++ b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs @@ -50,8 +50,7 @@ public async Task StartSystem() this.TestingContext.DockerHelper.SqlServerContainerName = "sharedsqlserver"; this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TransactionProcessor, ("transactionprocessor", false)); - this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TestHost, ("testhosts", false)); - + this.TestingContext.Logger = logger; this.TestingContext.Logger.LogInformation("About to Start Containers for Scenario Run"); await this.TestingContext.DockerHelper.StartContainersForScenarioRun(scenarioName, dockerServices).ConfigureAwait(false);