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..d272fb76 100644 --- a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs +++ b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs @@ -50,7 +50,7 @@ public async Task StartSystem() this.TestingContext.DockerHelper.SqlServerContainerName = "sharedsqlserver"; this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TransactionProcessor, ("transactionprocessor", 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); 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