From 6eb472fbb6bca9e5b0976e836ef945d6f0deffbb Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 24 Aug 2026 20:34:12 +0100 Subject: [PATCH 1/2] Update estate reporting API workflows and tests --- .../DatabaseModels.cs | 3 +- .../ModelFactory.cs | 6 +- .../ReportingManager.cs | 108 +++-- .../MerchantDailyPerformanceSummaryRequest.cs | 1 + .../CalendarEndpointTests.cs | 6 +- .../ContractEndPointTests.cs | 78 +++- .../ControllerTestsBase.cs | 9 +- .../DatabaseHelper.cs | 32 +- .../EstateEndpointTests.cs | 15 +- .../FileImportLogsEndpointTests.cs | 189 +++++++-- .../FileProfileConfigurationEndpointTests.cs | 22 +- .../MerchantEndpointTests.cs | 137 ++++-- .../OperatorEndpointTests.cs | 22 +- .../SettlmentsEndpointTests.cs | 4 +- .../TransactionMixSummaryEndpointTests.cs | 41 +- .../TransactionsEndpointTests.cs | 399 ++++++++++++++---- .../xunit.runner.json | 4 +- .../Handlers/FileImportHandler.cs | 6 +- .../Handlers/MerchantHandler.cs | 4 + .../Handlers/TransactionHandler.cs | 3 +- _codexout/itest-run-2/failures2.trx | 177 ++++++++ _codexout/itest-run/failures.trx | 177 ++++++++ 22 files changed, 1200 insertions(+), 243 deletions(-) create mode 100644 _codexout/itest-run-2/failures2.trx create mode 100644 _codexout/itest-run/failures.trx diff --git a/EstateReportingAPI.BusinessLogic/DatabaseModels.cs b/EstateReportingAPI.BusinessLogic/DatabaseModels.cs index dad3d6d..168ec81 100644 --- a/EstateReportingAPI.BusinessLogic/DatabaseModels.cs +++ b/EstateReportingAPI.BusinessLogic/DatabaseModels.cs @@ -30,7 +30,8 @@ internal sealed class MerchantContactData { public string? PhoneNumber { get; init; } } -internal sealed class MerchantWithAddressData { +internal sealed class MerchantWithAddressAndContactData { public required TransactionProcessor.Database.Entities.Merchant Merchant { get; init; } public required TransactionProcessor.Database.Entities.MerchantAddress MerchantAddress { get; init; } + public required TransactionProcessor.Database.Entities.MerchantContact MerchantContact { get; init; } } diff --git a/EstateReportingAPI.BusinessLogic/ModelFactory.cs b/EstateReportingAPI.BusinessLogic/ModelFactory.cs index e62e4ab..6ce5abf 100644 --- a/EstateReportingAPI.BusinessLogic/ModelFactory.cs +++ b/EstateReportingAPI.BusinessLogic/ModelFactory.cs @@ -30,7 +30,7 @@ internal static Merchant ConvertFrom(MerchantData merchant, }; } - internal static Merchant ConvertFrom(MerchantWithAddressData merchant, + internal static Merchant ConvertFrom(MerchantWithAddressAndContactData merchant, decimal balance) { return new Merchant { Balance = balance, @@ -46,6 +46,10 @@ internal static Merchant ConvertFrom(MerchantWithAddressData merchant, AddressLine2 = merchant.MerchantAddress.AddressLine2, Town = merchant.MerchantAddress.Town, Country = merchant.MerchantAddress.Country, + ContactId = merchant.MerchantContact.ContactId, + ContactName = merchant.MerchantContact.Name, + ContactEmail = merchant.MerchantContact.EmailAddress, + ContactPhone = merchant.MerchantContact.PhoneNumber, MerchantReportingId = merchant.Merchant.MerchantReportingId }; } diff --git a/EstateReportingAPI.BusinessLogic/ReportingManager.cs b/EstateReportingAPI.BusinessLogic/ReportingManager.cs index 7ed86ef..6d800c3 100644 --- a/EstateReportingAPI.BusinessLogic/ReportingManager.cs +++ b/EstateReportingAPI.BusinessLogic/ReportingManager.cs @@ -231,14 +231,18 @@ public async Task>> GetContracts(ContractQueries.GetContra var baseContractsQuery = (from c in context.Contracts join o in context.Operators on c.OperatorId equals o.OperatorId into ops - from o in ops.DefaultIfEmpty() + from o in ops + join e in context.Estates on c.EstateId equals e.EstateId into estates + from e in estates select new ContractBaseData { + EstateReportingId = e.EstateReportingId, ContractId = c.ContractId, ContractReportingId = c.ContractReportingId, Description = c.Description, EstateId = c.EstateId, OperatorId = c.OperatorId, - OperatorName = o != null ? o.Name : null + OperatorReportingId = o.OperatorReportingId, + OperatorName = o.Name }).OrderByDescending(x => x.Description); var baseContractsResult = await ExecuteQuerySafeToList(baseContractsQuery, cancellationToken, "Error retrieving contracts - Step 1"); @@ -267,19 +271,22 @@ public async Task> GetContract(ContractQueries.GetContractQuery using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - // Step 1: load contract with operator name via a left-join (translatable) + // Step 1: load contract with operator name var baseContractQuery = (from c in context.Contracts join o in context.Operators on c.OperatorId equals o.OperatorId into ops - from o in ops.DefaultIfEmpty() + from o in ops + join e in context.Estates on c.EstateId equals e.EstateId into estates + from e in estates where c.ContractId == request.ContractId select new { + e.EstateReportingId, c.ContractId, c.ContractReportingId, c.Description, c.EstateId, c.OperatorId, - OperatorName = o != null ? o.Name : null - }).OrderByDescending(x => x.Description); + o.OperatorReportingId, + OperatorName = o.Name }).OrderByDescending(x => x.Description); var baseContractQueryResult = await ExecuteQuerySafeSingleOrDefault(baseContractQuery, cancellationToken, "Error retrieving contract - Step 1"); @@ -295,12 +302,14 @@ from o in ops.DefaultIfEmpty() return ResultHelpers.CreateFailure(productsResult); return Result.Success(new Contract { + EstateReportingId = baseContract.EstateReportingId, ContractId = baseContract.ContractId, ContractReportingId = baseContract.ContractReportingId, Description = baseContract.Description, EstateId = baseContract.EstateId, OperatorName = baseContract.OperatorName, OperatorId = baseContract.OperatorId, + OperatorReportingId = baseContract.OperatorReportingId, Products = productsResult.Data }); } @@ -368,21 +377,43 @@ public async Task>> GetRecentContracts(ContractQueries.Get using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable contractsQuery = context.Contracts.Select(c => new Contract() { - ContractId = c.ContractId, - ContractReportingId = c.ContractReportingId, - Description = c.Description, - EstateId = c.EstateId, - OperatorName = context.Operators.Where(o => o.OperatorId == c.OperatorId).Select(o => o.Name).FirstOrDefault(), - OperatorId = c.OperatorId, - }).OrderByDescending(c => c.Description).Take(3); + var contractsQuery = (from c in context.Contracts + join o in context.Operators on c.OperatorId equals o.OperatorId into ops + from o in ops + join e in context.Estates on c.EstateId equals e.EstateId into estates + from e in estates + select new + { + e.EstateReportingId, + c.ContractId, + c.ContractReportingId, + c.Description, + c.EstateId, + c.OperatorId, + o.OperatorReportingId, + OperatorName = o.Name + }).OrderByDescending(x => x.ContractReportingId).Take(3); - Result> result = await ExecuteQuerySafeToList(contractsQuery, cancellationToken, "Error retrieving recent contracts"); + var result = await ExecuteQuerySafeToList(contractsQuery, cancellationToken, "Error retrieving recent contracts"); if (result.IsFailed) return ResultHelpers.CreateFailure(result); - return result; + List response = new List(); + foreach (var contract in result.Data) { + response.Add(new Contract { + EstateId = contract.EstateId, + EstateReportingId = contract.EstateReportingId, + ContractId = contract.ContractId, + ContractReportingId = contract.ContractReportingId, + Description = contract.Description, + OperatorName = contract.OperatorName, + OperatorId = contract.OperatorId, + OperatorReportingId = contract.OperatorReportingId, + }); + } + + return response; } public async Task> GetTodaysFailedSales(TransactionQueries.TodaysFailedSales request, @@ -548,12 +579,13 @@ public async Task>> GetRecentMerchants(MerchantQueries.Get Reference = m.Reference, AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId) .OrderByDescending(ma => ma.CreatedDateTime) - .Select(ma => new { ma.AddressLine1, ma.AddressLine2, ma.Country, ma.PostalCode, ma.Region, ma.Town }) + .Select(ma => new { ma.AddressId, ma.AddressLine1, ma.AddressLine2, ma.Country, ma.PostalCode, ma.Region, ma.Town }) .FirstOrDefault(), ContactInfo = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId) .OrderByDescending(mc => mc.CreatedDateTime) - .Select(mc => new { mc.Name, mc.EmailAddress, mc.PhoneNumber }) - .FirstOrDefault() + .Select(mc => new { mc.ContactId, mc.Name, mc.EmailAddress, mc.PhoneNumber }) + .FirstOrDefault(), + BalanceState = context.MerchantBalanceProjectionState.Where(mb => mb.MerchantId == m.MerchantId).SingleOrDefault() }).OrderByDescending(m => m.CreatedDateTime).Take(3); var recentMerchantsResult = await ExecuteQuerySafeToList(merchantsQuery, cancellationToken, "Error retrieving recent merchants"); @@ -567,8 +599,10 @@ public async Task>> GetRecentMerchants(MerchantQueries.Get Reference = merchant.Reference, MerchantReportingId = merchant.MerchantReportingId, CreatedDateTime = merchant.CreatedDateTime, + Balance = merchant.BalanceState.Balance }; if (merchant.AddressInfo != null) { + model.AddressId = merchant.AddressInfo.AddressId; model.AddressLine1 = merchant.AddressInfo.AddressLine1; model.AddressLine2 = merchant.AddressInfo.AddressLine2; model.Country = merchant.AddressInfo.Country; @@ -577,6 +611,8 @@ public async Task>> GetRecentMerchants(MerchantQueries.Get model.Region = merchant.AddressInfo.Region; } if (merchant.ContactInfo != null) { + model.ContactId = merchant.ContactInfo.ContactId; + model.ContactName = merchant.ContactInfo.Name; model.ContactEmail = merchant.ContactInfo.EmailAddress; model.ContactPhone = merchant.ContactInfo.PhoneNumber; @@ -1079,18 +1115,23 @@ private static IQueryable BuildMerchantQuery(EstateManagementConte .Where(m => m.MerchantId == merchantId); } - private static IQueryable BuildMerchantWithAddressQuery(EstateManagementContext context, - Guid estateId) { + private static IQueryable BuildMerchantWithAddressQuery(EstateManagementContext context, + Guid estateId) { return context.Merchants .Where(m => m.EstateId == estateId) - .GroupJoin(context.MerchantAddresses, - m => m.MerchantId, - a => a.MerchantId, - (m, addresses) => new MerchantWithAddressData { Merchant = m, MerchantAddress = addresses.First() }); - } - - private static IQueryable ApplyMerchantFilters(IQueryable query, - MerchantQueries.MerchantQueryOptions queryOptions) { + .Select(m => new MerchantWithAddressAndContactData { + Merchant = m, + MerchantAddress = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId) + .OrderByDescending(ma => ma.CreatedDateTime) + .First(), + MerchantContact = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId) + .OrderByDescending(mc => mc.CreatedDateTime) + .First() + }); + } + + private static IQueryable ApplyMerchantFilters(IQueryable query, + MerchantQueries.MerchantQueryOptions queryOptions) { if (String.IsNullOrEmpty(queryOptions.Name) == false) query = query.Where(m => m.Merchant.Name.Contains(queryOptions.Name)); @@ -1717,6 +1758,7 @@ from esu in esuJoin.DefaultIfEmpty() join m in context.Merchants on f.MerchantId equals m.MerchantId into mJoin from m in mJoin.DefaultIfEmpty() join fl in context.FileLines on f.FileId equals fl.FileId + //join fp in context.FileProfileConfigurations on f.FileProfileId equals fp.FileProfileId where fil.FileImportLogId == request.FileImportLogId && (request.MerchantId == null || f.MerchantId == request.MerchantId) select new FileImportFlatData @@ -1948,8 +1990,10 @@ private static List BuildContracts(List baseContract ContractReportingId = b.ContractReportingId, Description = b.Description, EstateId = b.EstateId, + EstateReportingId = b.EstateReportingId, OperatorName = b.OperatorName, OperatorId = b.OperatorId, + OperatorReportingId = b.OperatorReportingId, Products = products.Where(p => p.ContractId == b.ContractId).Select(p => new Models.ContractProduct { ContractId = p.ContractId, ProductId = p.ContractProductId, @@ -2230,8 +2274,10 @@ private sealed class ContractBaseData { public int ContractReportingId { get; init; } public string? Description { get; init; } public Guid EstateId { get; init; } - public Guid OperatorId { get; init; } - public string? OperatorName { get; init; } + public int EstateReportingId { get; init; } + public Guid OperatorId { get; init; } + public int OperatorReportingId { get; init; } + public string? OperatorName { get; init; } } private sealed class ContractProductData { diff --git a/EstateReportingAPI.DataTrasferObjects/MerchantDailyPerformanceSummaryRequest.cs b/EstateReportingAPI.DataTrasferObjects/MerchantDailyPerformanceSummaryRequest.cs index e0bd88b..2cbdbab 100644 --- a/EstateReportingAPI.DataTrasferObjects/MerchantDailyPerformanceSummaryRequest.cs +++ b/EstateReportingAPI.DataTrasferObjects/MerchantDailyPerformanceSummaryRequest.cs @@ -27,6 +27,7 @@ public sealed class MetricItem public string Description { get; set; } public int Category { get; set; } + public Int32 Type { get; set; } } public sealed class DrillDownTransaction diff --git a/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs b/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs index c75cddf..0ff753f 100644 --- a/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs @@ -40,9 +40,9 @@ public async Task CalendarEndpoint_GetComparisonDates_DatesReturned() { dates.Select(d => d.Date).Contains(date.Date).ShouldBeTrue(); } - dates.Select(d => d.Description).Contains("Yesterday"); - dates.Select(d => d.Description).Contains("Last Week"); - dates.Select(d => d.Description).Contains("Last Month"); + dates.Select(d => d.Description).Contains("Yesterday").ShouldBeTrue(); + dates.Select(d => d.Description).Contains("Last Week").ShouldBeTrue(); + dates.Select(d => d.Description).Contains("Last Month").ShouldBeTrue(); } protected override async Task ClearStandingData() { diff --git a/EstateReportingAPI.IntegrationTests/ContractEndPointTests.cs b/EstateReportingAPI.IntegrationTests/ContractEndPointTests.cs index f59ab1c..bcf3341 100644 --- a/EstateReportingAPI.IntegrationTests/ContractEndPointTests.cs +++ b/EstateReportingAPI.IntegrationTests/ContractEndPointTests.cs @@ -13,6 +13,59 @@ namespace EstateReportingAPI.IntegrationTests; public class ContractEndPointTests : ControllerTestsBase { private String BaseRoute = "api/contracts"; + private void AssertContractMatchesDatabase(Contract contract, Guid contractId, bool includeProducts) { + var sourceContract = this.context.Contracts.Single(c => c.ContractId == contractId); + var sourceOperator = this.context.Operators.Single(o => o.OperatorId == sourceContract.OperatorId); + var sourceEstate = this.context.Estates.Single(e => e.EstateId == sourceContract.EstateId); + + contract.EstateId.ShouldBe(sourceContract.EstateId); + contract.EstateReportingId.ShouldBe(sourceEstate.EstateReportingId); + contract.ContractId.ShouldBe(sourceContract.ContractId); + contract.ContractReportingId.ShouldBe(sourceContract.ContractReportingId); + contract.Description.ShouldBe(sourceContract.Description); + contract.OperatorName.ShouldBe(sourceOperator.Name); + contract.OperatorId.ShouldBe(sourceOperator.OperatorId); + contract.OperatorReportingId.ShouldBe(sourceOperator.OperatorReportingId); + + if (includeProducts) { + var sourceProducts = this.context.ContractProducts + .Where(p => p.ContractId == contractId) + .OrderBy(p => p.ProductName) + .ToList(); + + contract.Products.ShouldNotBeNull(); + contract.Products.Count.ShouldBe(sourceProducts.Count); + + foreach (var sourceProduct in sourceProducts) { + var actualProduct = contract.Products.Single(p => p.ProductId == sourceProduct.ContractProductId); + actualProduct.ContractId.ShouldBe(sourceProduct.ContractId); + actualProduct.ContractProductReportingId.ShouldBe(sourceProduct.ContractProductReportingId); + actualProduct.ProductName.ShouldBe(sourceProduct.ProductName); + actualProduct.DisplayText.ShouldBe(sourceProduct.DisplayText); + actualProduct.ProductType.ShouldBe(sourceProduct.ProductType); + actualProduct.Value.ShouldBe(sourceProduct.Value); + + var sourceFees = this.context.ContractProductTransactionFees + .Where(f => f.ContractProductId == sourceProduct.ContractProductId) + .OrderBy(f => f.ContractProductTransactionFeeReportingId) + .ToList(); + + actualProduct.TransactionFees.Count.ShouldBe(sourceFees.Count); + foreach (var sourceFee in sourceFees) { + var actualFee = actualProduct.TransactionFees.Single(f => f.TransactionFeeId == sourceFee.ContractProductTransactionFeeId); + actualFee.ContractProductTransactionFeeReportingId.ShouldBe(sourceFee.ContractProductTransactionFeeReportingId); + actualFee.Description.ShouldBe(sourceFee.Description); + actualFee.CalculationType.ShouldBe(sourceFee.CalculationType); + actualFee.FeeType.ShouldBe(sourceFee.FeeType); + actualFee.Value.ShouldBe(sourceFee.Value); + } + } + } + else { + contract.Products.ShouldBeNull(); + } + } + [Fact] public async Task ContractEndpoint_GetRecentContracts_ContractsReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); @@ -41,9 +94,9 @@ public async Task ContractEndpoint_GetRecentContracts_ContractsReturned() { List contracts = result.Data; contracts.Count.ShouldBe(3); - contracts.SingleOrDefault(c => c.Description == "Safaricom Contract").ShouldNotBeNull(); - contracts.SingleOrDefault(c => c.Description == "PataPawa PostPay Contract").ShouldNotBeNull(); - contracts.SingleOrDefault(c => c.Description == "PataPawa PrePay Contract").ShouldNotBeNull(); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "Healthcare Centre 1 Contract"), contracts.Single(c => c.Description == "Healthcare Centre 1 Contract").ContractId, false); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "PataPawa PostPay Contract"), contracts.Single(c => c.Description == "PataPawa PostPay Contract").ContractId, false); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "PataPawa PrePay Contract"), contracts.Single(c => c.Description == "PataPawa PrePay Contract").ContractId, false); } @@ -75,14 +128,10 @@ public async Task ContractEndpoint_GetContracts_ContractsReturned() { List contracts = result.Data; contracts.Count.ShouldBe(4); - contracts.SingleOrDefault(c => c.Description == "Safaricom Contract").ShouldNotBeNull(); - contracts.SingleOrDefault(c => c.Description == "Healthcare Centre 1 Contract").ShouldNotBeNull(); - contracts.SingleOrDefault(c => c.Description == "PataPawa PostPay Contract").ShouldNotBeNull(); - contracts.SingleOrDefault(c => c.Description == "PataPawa PrePay Contract").ShouldNotBeNull(); - foreach (Contract contract in contracts) { - contract.Products.Any(cp => cp.ContractProductReportingId != 0).ShouldBeTrue(); - contract.Products.ToList().ForEach(cp => cp.TransactionFees.Any(t => t.ContractProductTransactionFeeReportingId != 0).ShouldBeTrue()); - } + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "Safaricom Contract"), contracts.Single(c => c.Description == "Safaricom Contract").ContractId, true); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "Healthcare Centre 1 Contract"), contracts.Single(c => c.Description == "Healthcare Centre 1 Contract").ContractId, true); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "PataPawa PostPay Contract"), contracts.Single(c => c.Description == "PataPawa PostPay Contract").ContractId, true); + AssertContractMatchesDatabase(contracts.Single(c => c.Description == "PataPawa PrePay Contract"), contracts.Single(c => c.Description == "PataPawa PrePay Contract").ContractId, true); } [Fact] @@ -113,10 +162,7 @@ public async Task ContractEndpoint_GetContract_ContractReturned() { Contract contract = result.Data; contract.ShouldNotBeNull(); - contract.Description.ShouldBe("PataPawa PrePay Contract"); - contract.ContractReportingId.ShouldNotBe(0); - contract.Products.Any(cp => cp.ContractProductReportingId != 0).ShouldBeTrue(); - contract.Products.ToList().ForEach(cp => cp.TransactionFees.Any(t => t.ContractProductTransactionFeeReportingId != 0).ShouldBeTrue()); + AssertContractMatchesDatabase(contract, ppprepayContractId, true); } protected override async Task ClearStandingData() { @@ -126,4 +172,4 @@ protected override async Task ClearStandingData() { protected override async Task SetupStandingData() { } -} \ No newline at end of file +} diff --git a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs index 0a559ab..1117904 100644 --- a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs +++ b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs @@ -25,10 +25,17 @@ public abstract class ControllerTestsBase : IAsyncLifetime protected List merchantsList; protected List operatorsList; protected List<(Guid contractId, String contractName, Guid operatorId, String operatorName)> contractList; - protected Dictionary> contractProducts; + protected Dictionary> contractProducts; protected DatabaseHelper helper; protected ITestOutputHelper TestOutputHelper; protected DockerHelper DockerHelper; + + protected static void AssertDecimalMatchesTo4Places(decimal actual, decimal expected) + { + Math.Round(actual, 4, MidpointRounding.AwayFromZero) + .ShouldBe(Math.Round(expected, 4, MidpointRounding.AwayFromZero)); + } + public virtual async ValueTask InitializeAsync() { this.TestId = Guid.NewGuid(); diff --git a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs index baf8f0a..8e6dc66 100644 --- a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs +++ b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs @@ -61,6 +61,20 @@ public async Task AddEstateUser(String estateName, string name, string ema return user.SecurityUserId; } + public async Task AddFileProfile(Guid operatorId, Guid fileFormatHandlerId, String fileProfileName, Guid requestTypeId, Guid fileProfileId) { + FileProfileConfiguration fileProfileConfiguration = new() { + OperatorId = operatorId, + FileFormatHandlerId = fileFormatHandlerId, + LineTerminator = "", + ListeningDirectory = "", + Name = fileProfileName, + RequestTypeId = requestTypeId, + FileProfileId = fileProfileId + }; + + await this.Context.AddAsync(fileProfileConfiguration); + await this.Context.SaveChangesAsync(CancellationToken.None); + } public async Task AddFileImportLog(Guid estateId, DateTime importDate){ FileImportLog fil = new FileImportLog { FileImportLogId = Guid.NewGuid(), @@ -75,12 +89,12 @@ public async Task AddFileImportLog(Guid estateId, DateTime importDate){ return fil.FileImportLogId; } - public async Task AddFile(Guid fileImportLogId, Guid merchantId, Guid userId, string fileLocation){ + public async Task AddFile(Guid fileImportLogId, Guid merchantId, Guid userId, string fileLocation, Guid fileProfileId){ DbFile file = new DbFile { FileId = Guid.NewGuid(), FileImportLogId = fileImportLogId, FileLocation = fileLocation, - FileProfileId = Guid.NewGuid(), + FileProfileId = fileProfileId, FileReceivedDateTime = DateTime.Now, UserId = userId, MerchantId = merchantId @@ -243,10 +257,8 @@ public async Task AddTransactionOld(DateTime dateTime, if (contractProduct.Value.HasValue){ transactionAmount = contractProduct.Value.GetValueOrDefault(); } - else{ - if (transactionAmount.HasValue == false){ - transactionAmount = 75.00m; - } + else if (transactionAmount.HasValue == false){ + throw new ArgumentNullException(nameof(transactionAmount), "Transaction amount must be provided explicitly for custom products."); } Transaction transaction = new Transaction @@ -334,7 +346,7 @@ public async Task BuildTransactionX(DateTime dateTime, //{ if (transactionAmount.HasValue == false) { - transactionAmount = 75.00m; + throw new ArgumentNullException(nameof(transactionAmount), "Transaction amount must be provided explicitly for custom products."); } //} @@ -810,14 +822,14 @@ public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalcul for (int i = 1; i <= settledTransactionCount; i++){ Transaction transaction = await this.BuildTransactionX(settlementDate.AddDays(-1), merchantId, operatorId, contractProducts.c.ContractId, - contractProducts.cp.ContractProductId,"0000", null); + contractProducts.cp.ContractProductId,"0000", 75.00m); settledTransactions.Add(transaction); settlementFees.Add((0.5m, 0.5m * i, contractProducts.f.ContractProductTransactionFeeId, true, transaction.TransactionId)); } for (int i = 1; i <= pendingSettlementTransactionCount; i++){ Transaction transaction = await this.BuildTransactionX(settlementDate.AddDays(-1), merchantId, operatorId, contractProducts.c.ContractId, - contractProducts.cp.ContractProductId, "0000", null); + contractProducts.cp.ContractProductId, "0000", 75.00m); pendingSettlementTransactions.Add(transaction); pendingSettlementFees.Add((0.5m, 0.5m * i, contractProducts.f.ContractProductTransactionFeeId, false, transaction.TransactionId)); } @@ -964,4 +976,4 @@ public static async Task SaveChangesWithIdentityInsert(this DbContext context await context.DisableIdentityInsert(); await transaction.CommitAsync(); } -} \ No newline at end of file +} diff --git a/EstateReportingAPI.IntegrationTests/EstateEndpointTests.cs b/EstateReportingAPI.IntegrationTests/EstateEndpointTests.cs index 49ddf24..006a644 100644 --- a/EstateReportingAPI.IntegrationTests/EstateEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/EstateEndpointTests.cs @@ -25,6 +25,15 @@ public async Task EstateEndpoint_GetEstates_EstateReturned() { estate.ShouldNotBeNull(); estate.EstateName.ShouldBe("Test Estate"); estate.Reference.ShouldBe("Ref1"); + estate.EstateId.ShouldBe(this.context.Estates.Single().EstateId); + estate.Operators.ShouldNotBeNull(); + estate.Merchants.ShouldNotBeNull(); + estate.Contracts.ShouldNotBeNull(); + estate.Users.ShouldNotBeNull(); + estate.Operators.ShouldBeEmpty(); + estate.Merchants.ShouldBeEmpty(); + estate.Contracts.ShouldBeEmpty(); + estate.Users.ShouldBeEmpty(); } [Fact] @@ -41,8 +50,8 @@ public async Task EstateEndpoint_GetEstateOperator_EstateOperatorsReturned() { List estateOperators = result.Data; estateOperators.Count.ShouldBe(2); - estateOperators.SingleOrDefault(e => e.Name == "Safaricom").ShouldNotBeNull(); - estateOperators.SingleOrDefault(e => e.Name == "Voucher").ShouldNotBeNull(); + estateOperators.Single(e => e.Name == "Safaricom").OperatorId.ShouldBe(this.context.Operators.Single(o => o.Name == "Safaricom").OperatorId); + estateOperators.Single(e => e.Name == "Voucher").OperatorId.ShouldBe(this.context.Operators.Single(o => o.Name == "Voucher").OperatorId); } protected override async Task ClearStandingData() { @@ -52,4 +61,4 @@ protected override async Task ClearStandingData() { protected override async Task SetupStandingData() { } -} \ No newline at end of file +} diff --git a/EstateReportingAPI.IntegrationTests/FileImportLogsEndpointTests.cs b/EstateReportingAPI.IntegrationTests/FileImportLogsEndpointTests.cs index eaf7835..b0e83db 100644 --- a/EstateReportingAPI.IntegrationTests/FileImportLogsEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/FileImportLogsEndpointTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; using System.Net; @@ -24,6 +25,33 @@ public FileImportLogsEndpointTests(ITestOutputHelper output) private String BaseRoute = "api/fileimportlogs"; + private void AssertFileLineMatchesSource(FileLine actual, int lineNumber, string contents, string status) + { + actual.LineNumber.ShouldBe(lineNumber); + actual.LineContents.ShouldBe(contents); + actual.LineStatus.ShouldBe(status); + } + + private void AssertFileDetailsMatchesSource(FileDetails actual, + Guid fileId, + string fileName, + Guid fileProfileId, + Guid userId, + string uploadedBy, + Guid merchantId, + string merchantName, + DateTime uploadedAt) + { + actual.FileId.ShouldBe(fileId); + actual.FileName.ShouldBe(fileName); + actual.FileProfile.ShouldBe(fileProfileId.ToString()); + actual.DateTimeUploaded.ShouldBe(uploadedAt, TimeSpan.FromSeconds(1)); + actual.UserId.ShouldBe(userId); + actual.UploadedBy.ShouldBe(uploadedBy); + actual.MerchantId.ShouldBe(merchantId); + actual.MerchantName.ShouldBe(merchantName); + } + [Fact] public async Task FileImportEndpoint_GetFileImportLogs_NoData_ReturnsEmptyList() { @@ -46,9 +74,13 @@ public async Task FileImportEndpoint_GetFileImportLogs_WithMerchantFilter_Return // create a merchant and use it for the file var merchant = await this.helper.AddMerchant("Test Estate", "List Filter Merchant", 10, DateTime.MinValue, DateTime.MinValue, default, default); - // create file import log, file and a line associated to the merchant + var operatorRecord = await this.context.Operators.FirstAsync(); + + // create file import log, file and a line + Guid fileProfileId = Guid.Parse("A5D66966-0E95-4F62-A530-7669284EB616"); + await this.helper.AddFileProfile(operatorRecord.OperatorId, Guid.NewGuid(), "Test Profile 1", Guid.NewGuid(), fileProfileId); var filId = await this.helper.AddFileImportLog(this.TestId, DateTime.Now); - var fileId = await this.helper.AddFile(filId, merchant, userId, "test/location/file-filter-list.csv"); + var fileId = await this.helper.AddFile(filId, merchant, userId, "test/location/file-filter-list.csv", fileProfileId); await this.helper.AddFileLine(fileId, 1, "filterlistline", "OK"); DateTime start = DateTime.Today.AddDays(-1); @@ -58,11 +90,31 @@ public async Task FileImportEndpoint_GetFileImportLogs_WithMerchantFilter_Return result.IsSuccess.ShouldBeTrue(); var list = result.Data; list.ShouldNotBeNull(); - list.Count.ShouldBeGreaterThan(0); - - var match = list.SelectMany(x => x.FileDetailsList).SelectMany(fd => fd.FileLines).SingleOrDefault(fl => fl.LineContents == "filterlistline"); - match.ShouldNotBeNull(); - match.LineStatus.ShouldBe("OK"); + list.Count.ShouldBe(1); + + var sourceLog = this.context.FileImportLogs.Single(x => x.FileImportLogId == filId); + var sourceFile = this.context.Files.Single(x => x.FileImportLogId == filId); + var sourceLine = this.context.FileLines.Single(x => x.FileId == fileId); + + var match = list.Single(); + match.FileImportLogId.ShouldBe(sourceLog.FileImportLogId); + match.ImportLogDateTime.ShouldBe(sourceLog.ImportLogDateTime, TimeSpan.FromSeconds(1)); + match.FileDetailsList.Count.ShouldBe(1); + + var fileDetail = match.FileDetailsList.Single(); + AssertFileDetailsMatchesSource( + fileDetail, + sourceFile.FileId, + sourceFile.FileLocation, + sourceFile.FileProfileId, + sourceFile.UserId, + "apiuser@example.com", + merchant, + "List Filter Merchant", + sourceFile.FileReceivedDateTime); + + fileDetail.FileLines.Count.ShouldBe(1); + AssertFileLineMatchesSource(fileDetail.FileLines.Single(), sourceLine.LineNumber, sourceLine.FileLineData, sourceLine.Status); } [Fact] @@ -74,20 +126,43 @@ public async Task FileImportEndpoint_GetFileImportLog_WithMerchantFilter_Returns // create a merchant and use it for the file var merchant = await this.helper.AddMerchant("Test Estate", "Filter Merchant", 10, DateTime.MinValue, DateTime.MinValue, default, default); - // create file import log, file and a line associated to the merchant + // create file import log, file and a line + var operatorRecord = await this.context.Operators.FirstAsync(); + + // create file import log, file and a line + Guid fileProfileId = Guid.Parse("A5D66966-0E95-4F62-A530-7669284EB616"); + await this.helper.AddFileProfile(operatorRecord.OperatorId, Guid.NewGuid(), "Test Profile 1", Guid.NewGuid(), fileProfileId); + var filId = await this.helper.AddFileImportLog(this.TestId, DateTime.Now); - var fileId = await this.helper.AddFile(filId, merchant, userId, "test/location/file-filter.csv"); + var fileId = await this.helper.AddFile(filId, merchant, userId, "test/location/file-filter.csv", fileProfileId); await this.helper.AddFileLine(fileId, 1, "filterline", "OK"); Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/{filId}?merchantId={merchant}", CancellationToken.None); result.IsSuccess.ShouldBeTrue(); var item = result.Data; item.ShouldNotBeNull(); - item.FileImportLogId.ShouldBe(filId); - - var match = item.FileDetailsList.SelectMany(fd => fd.FileLines).SingleOrDefault(fl => fl.LineContents == "filterline"); - match.ShouldNotBeNull(); - match.LineStatus.ShouldBe("OK"); + var sourceLog = this.context.FileImportLogs.Single(x => x.FileImportLogId == filId); + var sourceFile = this.context.Files.Single(x => x.FileImportLogId == filId); + var sourceLine = this.context.FileLines.Single(x => x.FileId == fileId); + + item.FileImportLogId.ShouldBe(sourceLog.FileImportLogId); + item.ImportLogDateTime.ShouldBe(sourceLog.ImportLogDateTime, TimeSpan.FromSeconds(1)); + item.FileDetailsList.Count.ShouldBe(1); + + var fileDetail = item.FileDetailsList.Single(); + AssertFileDetailsMatchesSource( + fileDetail, + sourceFile.FileId, + Path.GetFileName(sourceFile.FileLocation), + sourceFile.FileProfileId, + sourceFile.UserId, + "apiuser@example.com", + merchant, + "Filter Merchant", + sourceFile.FileReceivedDateTime); + + fileDetail.FileLines.Count.ShouldBe(1); + AssertFileLineMatchesSource(fileDetail.FileLines.Single(), sourceLine.LineNumber, sourceLine.FileLineData, sourceLine.Status); } [Fact] @@ -99,20 +174,41 @@ public async Task FileImportEndpoint_GetFileImportLog_ReturnsInsertedData() // pick a merchant var merchant = await this.context.Merchants.FirstAsync(); + var operatorRecord = await this.context.Operators.FirstAsync(); + // create file import log, file and a line + Guid fileProfileId = Guid.Parse("A5D66966-0E95-4F62-A530-7669284EB616"); + await this.helper.AddFileProfile(operatorRecord.OperatorId, Guid.NewGuid(), "Test Profile 1", Guid.NewGuid(), fileProfileId); var filId = await this.helper.AddFileImportLog(this.TestId, DateTime.Now); - var fileId = await this.helper.AddFile(filId, merchant.MerchantId, userId, "test/location/file1.csv"); + var fileId = await this.helper.AddFile(filId, merchant.MerchantId, userId, "test/location/file1.csv", fileProfileId); await this.helper.AddFileLine(fileId, 1, "line1data", "OK"); Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/{filId}", CancellationToken.None); result.IsSuccess.ShouldBeTrue(); var item = result.Data; item.ShouldNotBeNull(); - item.FileImportLogId.ShouldBe(filId); - - var match = item.FileDetailsList.SelectMany(fd => fd.FileLines).SingleOrDefault(fl => fl.LineContents == "line1data"); - match.ShouldNotBeNull(); - match.LineStatus.ShouldBe("OK"); + var sourceLog = this.context.FileImportLogs.Single(x => x.FileImportLogId == filId); + var sourceFile = this.context.Files.Single(x => x.FileImportLogId == filId); + var sourceLine = this.context.FileLines.Single(x => x.FileId == fileId); + + item.FileImportLogId.ShouldBe(sourceLog.FileImportLogId); + item.ImportLogDateTime.ShouldBe(sourceLog.ImportLogDateTime, TimeSpan.FromSeconds(1)); + item.FileDetailsList.Count.ShouldBe(1); + + var fileDetail = item.FileDetailsList.Single(); + AssertFileDetailsMatchesSource( + fileDetail, + sourceFile.FileId, + Path.GetFileName(sourceFile.FileLocation), + sourceFile.FileProfileId, + sourceFile.UserId, + "apiuser@example.com", + merchant.MerchantId, + merchant.Name, + sourceFile.FileReceivedDateTime); + + fileDetail.FileLines.Count.ShouldBe(1); + AssertFileLineMatchesSource(fileDetail.FileLines.Single(), sourceLine.LineNumber, sourceLine.FileLineData, sourceLine.Status); } [Fact] @@ -125,9 +221,14 @@ public async Task FileImportEndpoint_GetFileImportLog_WithMerchantFilter_Returns var merchant1 = await this.context.Merchants.FirstAsync(); var merchant2 = await this.helper.AddMerchant("Test Estate", "Other Merchant", 50, DateTime.MinValue, DateTime.MinValue, default, default); - // create file import log, file and a line associated to merchant1 + var operatorRecord = await this.context.Operators.FirstAsync(); + + // create file import log, file and a line + Guid fileProfileId = Guid.Parse("A5D66966-0E95-4F62-A530-7669284EB616"); + await this.helper.AddFileProfile(operatorRecord.OperatorId, Guid.NewGuid(), "Test Profile 1", Guid.NewGuid(), fileProfileId); + var filId = await this.helper.AddFileImportLog(this.TestId, DateTime.Now); - var fileId = await this.helper.AddFile(filId, merchant1.MerchantId, userId, "test/location/file1.csv"); + var fileId = await this.helper.AddFile(filId, merchant1.MerchantId, userId, "test/location/file1.csv", fileProfileId); await this.helper.AddFileLine(fileId, 1, "line1data", "OK"); var url = $"{this.BaseRoute}/{filId}?merchantId={merchant2}"; @@ -151,9 +252,14 @@ public async Task FileImportEndpoint_GetFileImportLogs_ReturnsInsertedData() // pick a merchant var merchant = await this.context.Merchants.FirstAsync(); + var operatorRecord = await this.context.Operators.FirstAsync(); + // create file import log, file and a line + Guid fileProfileId = Guid.Parse("A5D66966-0E95-4F62-A530-7669284EB616"); + await this.helper.AddFileProfile(operatorRecord.OperatorId, Guid.NewGuid(), "Test Profile 1", Guid.NewGuid(), fileProfileId); + var filId = await this.helper.AddFileImportLog(this.TestId, DateTime.Now); - var fileId = await this.helper.AddFile(filId, merchant.MerchantId, userId, "test/location/file1.csv"); + var fileId = await this.helper.AddFile(filId, merchant.MerchantId, userId, "test/location/file1.csv", fileProfileId); await this.helper.AddFileLine(fileId, 1, "line1data", "OK"); DateTime start = DateTime.Today.AddDays(-1); @@ -163,11 +269,30 @@ public async Task FileImportEndpoint_GetFileImportLogs_ReturnsInsertedData() result.IsSuccess.ShouldBeTrue(); var list = result.Data; list.ShouldNotBeNull(); - list.Count.ShouldBeGreaterThan(0); - - var match = list.SelectMany(x => x.FileDetailsList).SelectMany(f => f.FileLines).SingleOrDefault(fl => fl.LineContents == "line1data"); - match.ShouldNotBeNull(); - match.LineStatus.ShouldBe("OK"); + list.Count.ShouldBe(1); + + var sourceLog = this.context.FileImportLogs.Single(x => x.FileImportLogId == filId); + var sourceFile = this.context.Files.Single(x => x.FileImportLogId == filId); + var sourceLine = this.context.FileLines.Single(x => x.FileId == fileId); + + var match = list.Single(); + match.FileImportLogId.ShouldBe(sourceLog.FileImportLogId); + match.ImportLogDateTime.ShouldBe(sourceLog.ImportLogDateTime, TimeSpan.FromSeconds(1)); + match.FileDetailsList.Count.ShouldBe(1); + + var fileDetail = match.FileDetailsList.Single(); + AssertFileDetailsMatchesSource( + fileDetail, + sourceFile.FileId, + sourceFile.FileLocation, + sourceFile.FileProfileId, + sourceFile.UserId, + "apiuser@example.com", + merchant.MerchantId, + merchant.Name, + sourceFile.FileReceivedDateTime); + fileDetail.FileLines.Count.ShouldBe(1); + AssertFileLineMatchesSource(fileDetail.FileLines.Single(), sourceLine.LineNumber, sourceLine.FileLineData, sourceLine.Status); } [Fact] @@ -214,7 +339,13 @@ protected override async Task SetupStandingData() { sw.Stop(); this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms"); sw.Restart(); - + + // Operators + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); + await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); + // File Profile (once the table is available at the RM) } } diff --git a/EstateReportingAPI.IntegrationTests/FileProfileConfigurationEndpointTests.cs b/EstateReportingAPI.IntegrationTests/FileProfileConfigurationEndpointTests.cs index bdb7e50..6f6926e 100644 --- a/EstateReportingAPI.IntegrationTests/FileProfileConfigurationEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/FileProfileConfigurationEndpointTests.cs @@ -45,16 +45,20 @@ public async Task FileProfileConfiguration_Get_ReturnsInsertedData() await this.helper.AddFileProfileConfiguration(profileId, profileName, "C:\\listen", handlerId, requestId, operatorId, "\\n"); - var result = await this.CreateAndSendHttpRequestMessage>($"{BaseRoute}", CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - var list = result.Data; - list.ShouldNotBeNull(); - list.Count.ShouldBeGreaterThan(0); + var result = await this.CreateAndSendHttpRequestMessage>($"{BaseRoute}", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var list = result.Data; + list.ShouldNotBeNull(); + list.Count.ShouldBeGreaterThan(0); - var match = list.SingleOrDefault(f => f.FileProfileId == profileId); - match.ShouldNotBeNull(); - match.Name.ShouldBe(profileName); - } + var match = list.Single(f => f.FileProfileId == profileId); + match.Name.ShouldBe(profileName); + match.ListeningDirectory.ShouldBe("C:\\listen"); + match.RequestType.ShouldBe(requestName); + match.OperatorName.ShouldBe("Test Operator"); + match.LineTerminator.ShouldBe("\\n"); + match.FileFormatHandler.ShouldBe(handlerName); + } protected override Task ClearStandingData() { diff --git a/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs b/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs index dc48531..c2d9949 100644 --- a/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs @@ -13,6 +13,32 @@ namespace EstateReportingAPI.IntegrationTests; public class MerchantEndpointTests : ControllerTestsBase { private String BaseRoute = "api/merchants"; + private void AssertMerchantMatchesDatabase(Merchant merchant, Guid merchantId) { + var sourceMerchant = this.context.Merchants.Single(m => m.MerchantId == merchantId); + var sourceAddress = this.context.MerchantAddresses.Single(a => a.MerchantId == merchantId); + var sourceContact = this.context.MerchantContacts.Single(c => c.MerchantId == merchantId); + var sourceBalance = this.context.MerchantBalanceProjectionState.Single(s => s.MerchantId == merchantId); + + merchant.MerchantId.ShouldBe(sourceMerchant.MerchantId); + merchant.MerchantReportingId.ShouldBe(sourceMerchant.MerchantReportingId); + merchant.Name.ShouldBe(sourceMerchant.Name); + merchant.Reference.ShouldBe(sourceMerchant.Reference); + merchant.Balance.ShouldBe(sourceBalance.Balance); + merchant.SettlementSchedule.ShouldBe(sourceMerchant.SettlementSchedule); + merchant.CreatedDateTime.ShouldBe(sourceMerchant.CreatedDateTime); + merchant.AddressId.ShouldBe(sourceAddress.AddressId); + merchant.AddressLine1.ShouldBe(sourceAddress.AddressLine1); + merchant.AddressLine2.ShouldBe(sourceAddress.AddressLine2); + merchant.Town.ShouldBe(sourceAddress.Town); + merchant.Region.ShouldBe(sourceAddress.Region); + merchant.PostCode.ShouldBe(sourceAddress.PostalCode); + merchant.Country.ShouldBe(sourceAddress.Country); + merchant.ContactId.ShouldBe(sourceContact.ContactId); + merchant.ContactName.ShouldBe(sourceContact.Name); + merchant.ContactEmail.ShouldBe(sourceContact.EmailAddress); + merchant.ContactPhone.ShouldBe(sourceContact.PhoneNumber); + } + [Fact] public async Task MerchantEndpoint_GetMerchants_MerchantsReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); @@ -30,9 +56,8 @@ await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}",100 * i, DateT merchants.Count.ShouldBe(10); for (int i = 0; i < 10; i++) { - Merchant? expected = merchants.SingleOrDefault(m => m.Name == $"Test Merchant {i}"); - expected.ShouldNotBeNull(); - expected.Balance.ShouldBe(100 * i); + Merchant expected = merchants.Single(m => m.Name == $"Test Merchant {i}"); + AssertMerchantMatchesDatabase(expected, expected.MerchantId); } } @@ -49,8 +74,7 @@ public async Task MerchantEndpoint_GetMerchant_MerchantReturned() var merchant = result.Data; merchant.ShouldNotBeNull(); - merchant.Name.ShouldBe("Test Merchant 1"); - merchant.Balance.ShouldBe(100); + AssertMerchantMatchesDatabase(merchant, merchantId); } [Fact] @@ -70,9 +94,9 @@ await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}",100, DateTime. merchants.ShouldNotBeNull(); merchants.Count.ShouldBe(3); - merchants.SingleOrDefault(m => m.Name == "Test Merchant 0").ShouldNotBeNull(); - merchants.SingleOrDefault(m => m.Name == "Test Merchant 1").ShouldNotBeNull(); - merchants.SingleOrDefault(m => m.Name == "Test Merchant 2").ShouldNotBeNull(); + AssertMerchantMatchesDatabase(merchants.Single(m => m.Name == "Test Merchant 0"), merchants.Single(m => m.Name == "Test Merchant 0").MerchantId); + AssertMerchantMatchesDatabase(merchants.Single(m => m.Name == "Test Merchant 1"), merchants.Single(m => m.Name == "Test Merchant 1").MerchantId); + AssertMerchantMatchesDatabase(merchants.Single(m => m.Name == "Test Merchant 2"), merchants.Single(m => m.Name == "Test Merchant 2").MerchantId); } @@ -94,8 +118,16 @@ public async Task MerchantEndpoint_GetMerchantOperators_MerchantOperatorsReturne merchantOperators.ShouldNotBeNull(); merchantOperators.Count.ShouldBe(2); - merchantOperators.SingleOrDefault(m => m.OperatorName == "Safaricom").ShouldNotBeNull(); - merchantOperators.SingleOrDefault(m => m.OperatorName == "Voucher").ShouldNotBeNull(); + merchantOperators.Single(m => m.OperatorName == "Safaricom").ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OperatorName.ShouldBe("Safaricom"); + x.IsDeleted.ShouldBeFalse(); + }); + merchantOperators.Single(m => m.OperatorName == "Voucher").ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OperatorName.ShouldBe("Voucher"); + x.IsDeleted.ShouldBeFalse(); + }); } [Fact] @@ -123,8 +155,22 @@ public async Task MerchantEndpoint_GetMerchantContracts_MerchantContractsReturne merchantContracts.ShouldNotBeNull(); merchantContracts.Count.ShouldBe(2); - merchantContracts.SingleOrDefault(m => m.ContractName == "Safaricom Contract").ShouldNotBeNull(); - merchantContracts.SingleOrDefault(m => m.ContractName == "Healthcare Centre 1 Contract").ShouldNotBeNull(); + merchantContracts.Single(m => m.ContractName == "Safaricom Contract").ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OperatorName.ShouldBe("Safaricom"); + x.IsDeleted.ShouldBeFalse(); + x.ContractProducts.Count.ShouldBe(4); + x.ContractProducts.Select(p => p.ProductName).ShouldBe(new[] { "200 KES Topup", "100 KES Topup", "50 KES Topup", "Custom" }); + x.ContractProducts.All(p => p.ContractId == x.ContractId && p.MerchantId == merchantId).ShouldBeTrue(); + }); + merchantContracts.Single(m => m.ContractName == "Healthcare Centre 1 Contract").ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OperatorName.ShouldBe("Voucher"); + x.IsDeleted.ShouldBeFalse(); + x.ContractProducts.Count.ShouldBe(2); + x.ContractProducts.Select(p => p.ProductName).ShouldBe(new[] { "10 KES Voucher", "Custom" }); + x.ContractProducts.All(p => p.ContractId == x.ContractId && p.MerchantId == merchantId).ShouldBeTrue(); + }); } [Fact] @@ -142,7 +188,11 @@ public async Task MerchantEndpoint_GetMerchantDevices_MerchantDevicesReturned() merchantDevices.ShouldNotBeNull(); merchantDevices.Count.ShouldBe(1); - merchantDevices.SingleOrDefault(m => m.DeviceIdentifier == "123456").ShouldNotBeNull(); + merchantDevices.Single(m => m.DeviceIdentifier == "123456").ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.DeviceIdentifier.ShouldBe("123456"); + x.IsDeleted.ShouldBeFalse(); + }); } [Fact] @@ -170,13 +220,41 @@ public async Task MerchantEndpoint_GetMerchantOpeningHours_MerchantOpeningHoursR merchantOpeningHours.ShouldNotBeNull(); merchantOpeningHours.Count.ShouldBe(7); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Monday && m.OpeningTime == "09:00" && m.ClosingTime == "17:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Tuesday && m.OpeningTime == "09:00" && m.ClosingTime == "17:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Wednesday && m.OpeningTime == "09:00" && m.ClosingTime == "17:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Thursday && m.OpeningTime == "09:00" && m.ClosingTime == "17:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Friday && m.OpeningTime == "09:00" && m.ClosingTime == "17:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Saturday && m.OpeningTime == "10:00" && m.ClosingTime == "16:00").ShouldNotBeNull(); - merchantOpeningHours.SingleOrDefault(m => m.DayOfWeek == DayOfWeek.Sunday && m.OpeningTime == "09:00" && m.ClosingTime == "18:00").ShouldNotBeNull(); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Monday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("17:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Tuesday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("17:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Wednesday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("17:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Thursday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("17:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Friday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("17:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Saturday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("10:00"); + x.ClosingTime.ShouldBe("16:00"); + }); + merchantOpeningHours.Single(m => m.DayOfWeek == DayOfWeek.Sunday).ShouldSatisfyAllConditions(x => { + x.MerchantId.ShouldBe(merchantId); + x.OpeningTime.ShouldBe("09:00"); + x.ClosingTime.ShouldBe("18:00"); + }); } [Fact] @@ -196,21 +274,10 @@ public async Task MerchantEndpoint_GetMerchantSchedule_MerchantScheduleReturned( merchantSchedule.Year.ShouldBe(2026); merchantSchedule.Months.ShouldNotBeNull(); merchantSchedule.Months.Count.ShouldBe(3); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 1).ShouldNotBeNull(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 1).ClosedDays.Contains(1).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 1).ClosedDays.Contains(2).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 1).ClosedDays.Contains(15).ShouldBeTrue(); - - merchantSchedule.Months.SingleOrDefault(m => m.Month == 2).ShouldNotBeNull(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 2).ClosedDays.Contains(10).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 2).ClosedDays.Contains(14).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 2).ClosedDays.Contains(28).ShouldBeTrue(); - - merchantSchedule.Months.SingleOrDefault(m => m.Month == 12).ShouldNotBeNull(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 12).ClosedDays.Contains(24).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 12).ClosedDays.Contains(25).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 12).ClosedDays.Contains(26).ShouldBeTrue(); - merchantSchedule.Months.SingleOrDefault(m => m.Month == 12).ClosedDays.Contains(31).ShouldBeTrue(); + merchantSchedule.Months.Select(m => m.Month).ShouldBe(new[] { 1, 2, 12 }); + merchantSchedule.Months.Single(m => m.Month == 1).ClosedDays.ShouldBe(new[] { 1, 2, 15 }); + merchantSchedule.Months.Single(m => m.Month == 2).ClosedDays.ShouldBe(new[] { 10, 14, 28 }); + merchantSchedule.Months.Single(m => m.Month == 12).ClosedDays.ShouldBe(new[] { 24, 25, 26, 31 }); } [Fact] diff --git a/EstateReportingAPI.IntegrationTests/OperatorEndpointTests.cs b/EstateReportingAPI.IntegrationTests/OperatorEndpointTests.cs index d32d891..07c2936 100644 --- a/EstateReportingAPI.IntegrationTests/OperatorEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/OperatorEndpointTests.cs @@ -23,8 +23,20 @@ public async Task OperatorEndpoint_GetOperators_OperatorsReturned() { result.IsSuccess.ShouldBeTrue(); List operators = result.Data; operators.Count.ShouldBe(2); - operators.SingleOrDefault(o => o.Name == "Safaricom").ShouldNotBeNull(); - operators.SingleOrDefault(o => o.Name == "Voucher").ShouldNotBeNull(); + operators.Single(o => o.Name == "Safaricom").ShouldSatisfyAllConditions(x => { + x.OperatorId.ShouldNotBe(Guid.Empty); + x.EstateReportingId.ShouldBeGreaterThan(0); + x.Name.ShouldBe("Safaricom"); + x.RequireCustomMerchantNumber.ShouldBeFalse(); + x.RequireCustomTerminalNumber.ShouldBeFalse(); + }); + operators.Single(o => o.Name == "Voucher").ShouldSatisfyAllConditions(x => { + x.OperatorId.ShouldNotBe(Guid.Empty); + x.EstateReportingId.ShouldBeGreaterThan(0); + x.Name.ShouldBe("Voucher"); + x.RequireCustomMerchantNumber.ShouldBeFalse(); + x.RequireCustomTerminalNumber.ShouldBeFalse(); + }); } [Fact] @@ -35,7 +47,11 @@ public async Task OperatorEndpoint_GetOperator_OperatorReturned() { result.IsSuccess.ShouldBeTrue(); Operator operatorData = result.Data; operatorData.ShouldNotBeNull(); + operatorData.OperatorId.ShouldBe(operatorId); + operatorData.EstateReportingId.ShouldBeGreaterThan(0); operatorData.Name.ShouldBe("Safaricom"); + operatorData.RequireCustomMerchantNumber.ShouldBeFalse(); + operatorData.RequireCustomTerminalNumber.ShouldBeFalse(); } protected override async Task ClearStandingData() { @@ -45,4 +61,4 @@ protected override async Task ClearStandingData() { protected override async Task SetupStandingData() { } -} \ No newline at end of file +} diff --git a/EstateReportingAPI.IntegrationTests/SettlmentsEndpointTests.cs b/EstateReportingAPI.IntegrationTests/SettlmentsEndpointTests.cs index 0fcd63a..b99d143 100644 --- a/EstateReportingAPI.IntegrationTests/SettlmentsEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/SettlmentsEndpointTests.cs @@ -85,7 +85,7 @@ protected override async Task SetupStandingData() { var query1 = this.context.Contracts.GroupJoin(this.context.ContractProducts, c => c.ContractId, cp => cp.ContractId, (c, productGroup) => new { c.ContractId, Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ContractProductId, p.ProductName, p.Value }).OrderBy(p => p.ContractProductId).Select(p => new { p.ContractProductId, p.ProductName, p.Value, p.ContractProductReportingId }).ToList() }).ToList(); - this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value, i.ContractProductReportingId)).ToList()); + this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value ?? 75.00m, i.ContractProductReportingId)).ToList()); this.operatorsList = this.context.Operators.ToList(); @@ -148,4 +148,4 @@ public async Task SettlementEndpoints_TodaysSettlement_SettlementReturned() todaysSettlement.TodaysPendingSettlementCount.ShouldBe(overallTodaysPendingSettlementTransactionCount); todaysSettlement.TodaysPendingSettlementValue.ShouldBe(todayOverallTotals.Sum(c => c.pendingSettlementFees)); } -} \ No newline at end of file +} diff --git a/EstateReportingAPI.IntegrationTests/TransactionMixSummaryEndpointTests.cs b/EstateReportingAPI.IntegrationTests/TransactionMixSummaryEndpointTests.cs index 368a931..18c777e 100644 --- a/EstateReportingAPI.IntegrationTests/TransactionMixSummaryEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/TransactionMixSummaryEndpointTests.cs @@ -24,6 +24,31 @@ public TransactionMixSummaryEndpointTests(ITestOutputHelper testOutputHelper) this.TestOutputHelper = testOutputHelper; } + private void AssertTransactionMixSummaryTransactionMatches(TransactionMixSummaryTransaction actual, Transaction source) + { + var merchant = this.context.Merchants.Single(m => m.MerchantId == source.MerchantId); + var @operator = this.context.Operators.Single(o => o.OperatorId == source.OperatorId); + var product = this.context.ContractProducts.Single(p => p.ContractProductId == source.ContractProductId && p.ContractId == source.ContractId); + + actual.Id.ShouldBe(source.TransactionId); + actual.DateTime.ShouldBe(source.TransactionDateTime); + actual.Merchant.ShouldBe(merchant.Name); + actual.MerchantId.ShouldBe(merchant.MerchantId); + actual.MerchantReportingId.ShouldBe(merchant.MerchantReportingId); + actual.Operator.ShouldBe(@operator.Name); + actual.OperatorId.ShouldBe(@operator.OperatorId); + actual.OperatorReportingId.ShouldBe(@operator.OperatorReportingId); + actual.Product.ShouldBe(product.ProductName); + actual.ProductId.ShouldBe(product.ContractProductId); + actual.ProductReportingId.ShouldBe(product.ContractProductReportingId); + actual.Type.ShouldBe(source.TransactionType); + actual.Status.ShouldBe(source.IsAuthorised ? "Authorised" : "Declined"); + actual.Value.ShouldBe(source.TransactionAmount); + actual.TotalFees.ShouldBe(0m); + actual.SettlementReference.ShouldBeNull(); + actual.TransactionNumber.ShouldBe(Int32.Parse(source.TransactionNumber)); + } + protected override async Task ClearStandingData() { } @@ -52,7 +77,7 @@ protected override async Task SetupStandingData() this.contractList = this.context.Contracts.Join(this.context.Operators, c => c.OperatorId, o => o.OperatorId, (c, o) => new { c.ContractId, c.Description, o.OperatorId, o.Name }).ToList().Select(x => (x.ContractId, x.Description, x.OperatorId, x.Name)).ToList(); var query1 = this.context.Contracts.GroupJoin(this.context.ContractProducts, c => c.ContractId, cp => cp.ContractId, (c, productGroup) => new { c.ContractId, Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ContractProductId, p.ProductName, p.Value }).OrderBy(p => p.ContractProductId).Select(p => new { p.ContractProductId, p.ProductName, p.Value, p.ContractProductReportingId }).ToList() }).ToList(); - this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value, i.ContractProductReportingId)).ToList()); + this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value ?? 75.00m, i.ContractProductReportingId)).ToList()); this.operatorsList = this.context.Operators.ToList(); } @@ -88,9 +113,23 @@ public async Task TransactionMixSummary_ProductBreakdown_ReturnsGroupedResults() result.IsSuccess.ShouldBeTrue(); result.Data.ShouldNotBeNull(); + result.Data.FromDate.ShouldBe(startDate); + result.Data.ToDate.ShouldBe(endDate); + result.Data.Breakdown.ShouldBe(TransactionMixBreakdown.Product); + result.Data.Measure.ShouldBe(TransactionMixMeasure.Count); result.Data.TotalCount.ShouldBe(3); + result.Data.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); result.Data.Groups.Count.ShouldBe(1); + result.Data.Groups[0].GroupKey.ShouldBe(product.contractProductReportingId.ToString()); + result.Data.Groups[0].GroupName.ShouldBe(product.productName); result.Data.Groups[0].TransactionCount.ShouldBe(3); + result.Data.Groups[0].TransactionValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + result.Data.Transactions.Count.ShouldBe(3); + foreach (var transaction in transactions) + { + var responseTransaction = result.Data.Transactions.Single(t => t.Id == transaction.TransactionId); + AssertTransactionMixSummaryTransactionMatches(responseTransaction, transaction); + } } [Fact] diff --git a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs index 93f3f89..5610cd2 100644 --- a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs @@ -49,6 +49,131 @@ private static JsonElement GetPropertyCaseInsensitive(JsonElement element, strin throw new KeyNotFoundException($"Property '{propertyName}' was not found."); } + private static decimal CalculateAverage(IReadOnlyCollection transactions) { + if (transactions.Count == 0) + return 0m; + + return transactions.Sum(t => t.TransactionAmount) / transactions.Count; + } + + private static void AssertTodaysSales(TodaysSales actual, + IReadOnlyCollection todaysTransactions, + IReadOnlyCollection comparisonTransactions) { + decimal todaysValue = todaysTransactions.Sum(t => t.TransactionAmount); + decimal comparisonValue = comparisonTransactions.Sum(t => t.TransactionAmount); + + actual.TodaysSalesCount.ShouldBe(todaysTransactions.Count); + actual.TodaysSalesValue.ShouldBe(todaysValue); + actual.TodaysAverageSalesValue.ShouldBe(CalculateAverage(todaysTransactions)); + + actual.ComparisonSalesCount.ShouldBe(comparisonTransactions.Count); + actual.ComparisonSalesValue.ShouldBe(comparisonValue); + actual.ComparisonAverageSalesValue.ShouldBe(CalculateAverage(comparisonTransactions)); + } + + private static void AssertMerchantDailyPerformanceMetricMatches(MetricItem actual, + string title, + decimal value, + string description, + int category, + int type) { + actual.Title.ShouldBe(title); + actual.Value.ShouldBe(value); + actual.Description.ShouldBe(description); + actual.Category.ShouldBe(category); + actual.Type.ShouldBe(type); + } + + private static void AssertDrillDownTransactionMatches(DrillDownTransaction actual, Transaction source, string expectedProduct) { + actual.Reference.ShouldBe(source.TransactionNumber); + actual.Product.ShouldBe(expectedProduct); + actual.Status.ShouldBe(source.IsAuthorised ? "Successful" : "Failed"); + actual.Amount.ShouldBe(source.TransactionAmount); + actual.TransactionDateTime.ShouldBe(source.TransactionDateTime); + } + + private static void AssertRecentActivityReceiptItemMatches(JsonElement actual, + Transaction source, + string expectedProduct, + string expectedOperator, + string expectedReceiptReference) { + GetPropertyCaseInsensitive(actual, "reference").GetString().ShouldBe(source.TransactionNumber); + GetPropertyCaseInsensitive(actual, "transactionType").GetString().ShouldBe(source.TransactionType); + GetPropertyCaseInsensitive(actual, "product").GetString().ShouldBe(expectedProduct); + GetPropertyCaseInsensitive(actual, "operator").GetString().ShouldBe(expectedOperator); + GetPropertyCaseInsensitive(actual, "status").GetString().ShouldBe(source.IsAuthorised ? "Successful" : "Failed"); + GetPropertyCaseInsensitive(actual, "amount").GetDecimal().ShouldBe(source.TransactionAmount); + DateTime.Parse(GetPropertyCaseInsensitive(actual, "transactionDateTime").GetString()!).ShouldBe(source.TransactionDateTime); + GetPropertyCaseInsensitive(actual, "receiptReference").GetString().ShouldBe(expectedReceiptReference); + } + + private void AssertTransactionDetailMatches(TransactionDetail actual, Transaction source) { + var merchant = this.context.Merchants.Single(m => m.MerchantId == source.MerchantId); + var operatorRecord = this.context.Operators.Single(o => o.OperatorId == source.OperatorId); + var product = this.context.ContractProducts.Single(p => p.ContractProductId == source.ContractProductId && p.ContractId == source.ContractId); + + actual.Id.ShouldBe(source.TransactionId); + actual.DateTime.ShouldBe(source.TransactionDateTime); + actual.Merchant.ShouldBe(merchant.Name); + actual.MerchantId.ShouldBe(merchant.MerchantId); + actual.MerchantReportingId.ShouldBe(merchant.MerchantReportingId); + actual.Operator.ShouldBe(operatorRecord.Name); + actual.OperatorId.ShouldBe(operatorRecord.OperatorId); + actual.OperatorReportingId.ShouldBe(operatorRecord.OperatorReportingId); + actual.Product.ShouldBe(product.ProductName); + actual.ProductId.ShouldBe(product.ContractProductId); + actual.ProductReportingId.ShouldBe(product.ContractProductReportingId); + actual.Type.ShouldBe(source.TransactionType); + actual.Status.ShouldBe(source.IsAuthorised ? "Authorised" : "Declined"); + actual.Value.ShouldBe(source.TransactionAmount); + actual.TotalFees.ShouldBe(0m); + actual.SettlementReference.ShouldBe(Guid.Empty.ToString()); + actual.TransactionNumber.ShouldBe(Int32.Parse(source.TransactionNumber)); + } + + private void AssertMerchantSummaryRowMatchesSource(MerchantDetail actual, TransactionProcessor.Database.Entities.Merchant sourceMerchant, IReadOnlyCollection transactions) { + actual.MerchantId.ShouldBe(sourceMerchant.MerchantId); + actual.MerchantReportingId.ShouldBe(sourceMerchant.MerchantReportingId); + actual.MerchantName.ShouldBe(sourceMerchant.Name); + actual.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + actual.TotalCount.ShouldBe(transactions.Count); + actual.AuthorisedCount.ShouldBe(transactions.Count(t => t.IsAuthorised)); + actual.DeclinedCount.ShouldBe(transactions.Count(t => t.IsAuthorised == false)); + actual.AverageValue.ShouldBe(transactions.Count == 0 + ? 0m + : transactions.GroupBy(t => t.OperatorId).Select(g => g.Sum(t => t.TransactionAmount)).Average()); + AssertDecimalMatchesTo4Places(actual.AuthorisedPercentage, transactions.Count == 0 ? 0m : (decimal)transactions.Count(t => t.IsAuthorised) / transactions.Count); + } + + private void AssertOperatorSummaryRowMatchesSource(OperatorDetail actual, TransactionProcessor.Database.Entities.Operator sourceOperator, IReadOnlyCollection transactions) { + actual.OperatorId.ShouldBe(sourceOperator.OperatorId); + actual.OperatorReportingId.ShouldBe(sourceOperator.OperatorReportingId); + actual.OperatorName.ShouldBe(sourceOperator.Name); + actual.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + actual.TotalCount.ShouldBe(transactions.Count); + actual.AuthorisedCount.ShouldBe(transactions.Count(t => t.IsAuthorised)); + actual.DeclinedCount.ShouldBe(transactions.Count(t => t.IsAuthorised == false)); + actual.AverageValue.ShouldBe(transactions.Count == 0 + ? 0m + : transactions.GroupBy(t => t.MerchantId).Select(g => g.Sum(t => t.TransactionAmount)).Average()); + AssertDecimalMatchesTo4Places(actual.AuthorisedPercentage, transactions.Count == 0 ? 0m : (decimal)transactions.Count(t => t.IsAuthorised) / transactions.Count); + } + + private void AssertProductPerformanceRowMatchesSource(ProductPerformanceDetail actual, + TransactionProcessor.Database.Entities.ContractProduct sourceProduct, + TransactionProcessor.Database.Entities.Contract sourceContract, + IReadOnlyCollection transactions, + decimal expectedPercentageOfTotal) { + actual.ProductName.ShouldBe(sourceProduct.ProductName); + actual.ProductId.ShouldBe(sourceProduct.ContractProductId); + actual.ProductReportingId.ShouldBe(sourceProduct.ContractProductReportingId); + actual.ContractId.ShouldBe(sourceContract.ContractId); + actual.ContractReportingId.ShouldBe(sourceContract.ContractReportingId); + actual.TransactionCount.ShouldBe(transactions.Count); + actual.TransactionValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + AssertDecimalMatchesTo4Places(actual.PercentageOfTotal, expectedPercentageOfTotal); + } + protected override async Task ClearStandingData() { @@ -118,7 +243,7 @@ protected override async Task SetupStandingData() { var query1 = this.context.Contracts.GroupJoin(this.context.ContractProducts, c => c.ContractId, cp => cp.ContractId, (c, productGroup) => new { c.ContractId, Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ContractProductId, p.ProductName, p.Value }).OrderBy(p => p.ContractProductId).Select(p => new { p.ContractProductId, p.ProductName, p.Value, p.ContractProductReportingId }).ToList() }).ToList(); - this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value, i.ContractProductReportingId)).ToList()); + this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value ?? 75.00m, i.ContractProductReportingId)).ToList()); this.operatorsList = this.context.Operators.ToList(); @@ -177,11 +302,7 @@ public async Task TransactionsEndpoint_TodaysSales_SalesReturned() { var todaysSales = result.Data; todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + AssertTodaysSales(todaysSales, todaysTransactions, comparisonDateTransactions); } @@ -235,11 +356,10 @@ public async Task TransactionsEndpoint_TodaysSales_OperatorFilter_SalesReturned( var todaysSales = result.Data; var operatorId = await this.helper.GetOperatorId(1, CancellationToken.None); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => c.OperatorId == operatorId)); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.OperatorId == operatorId).Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.OperatorId == operatorId)); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.OperatorId == operatorId).Sum(c => c.TransactionAmount)); + AssertTodaysSales( + todaysSales, + todaysTransactions.Where(c => c.OperatorId == operatorId).ToList(), + comparisonDateTransactions.Where(c => c.OperatorId == operatorId).ToList()); } @@ -293,11 +413,10 @@ public async Task TransactionsEndpoint_TodaysSales_MerchantFilter_SalesReturned( var todaysSales = result.Data; var merchantId = await this.helper.GetMerchantId(1, CancellationToken.None); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => c.MerchantId == merchantId)); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.MerchantId == merchantId).Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.MerchantId == merchantId)); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.MerchantId == merchantId).Sum(c => c.TransactionAmount)); + AssertTodaysSales( + todaysSales, + todaysTransactions.Where(c => c.MerchantId == merchantId).ToList(), + comparisonDateTransactions.Where(c => c.MerchantId == merchantId).ToList()); } [Fact] @@ -356,11 +475,7 @@ public async Task TransactionsEndpoint_TodaysFailedSales_SalesReturned() { DataTransferObjects.TodaysSales? todaysSales = result.Data; todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + AssertTodaysSales(todaysSales, todaysTransactions, comparisonDateTransactions); } [Fact] @@ -417,11 +532,11 @@ public async Task TransactionsEndpoint_TransactionDetailReport_NoFilters_Transac transactionDetailReportResponse.Summary.ShouldNotBeNull(); transactionDetailReportResponse.Summary.TransactionCount.ShouldBe(transactions.Count); transactionDetailReportResponse.Summary.TotalValue.ShouldBe(transactions.Sum(t=> t.TransactionAmount)); + transactionDetailReportResponse.Summary.TotalFees.ShouldBe(0m); foreach (Transaction transaction in transactions) { - var foundTxn = transactionDetailReportResponse.Transactions.SingleOrDefault(t => t.Id == transaction.TransactionId); - foundTxn.ShouldNotBeNull(transaction.TransactionId.ToString()); - foundTxn.TransactionNumber.ShouldNotBe(0); + var foundTxn = transactionDetailReportResponse.Transactions.Single(t => t.Id == transaction.TransactionId); + AssertTransactionDetailMatches(foundTxn, transaction); } } @@ -490,11 +605,12 @@ public async Task TransactionsEndpoint_TransactionDetailReport_MerchantFilter_Tr transactionDetailReportResponse.Summary.ShouldNotBeNull(); transactionDetailReportResponse.Summary.TransactionCount.ShouldBe(filteredTransactions.Count()); transactionDetailReportResponse.Summary.TotalValue.ShouldBe(filteredTransactions.Sum(t => t.TransactionAmount)); + transactionDetailReportResponse.Summary.TotalFees.ShouldBe(0m); foreach (Transaction transaction in filteredTransactions) { - var foundTxn = transactionDetailReportResponse.Transactions.SingleOrDefault(t => t.Id == transaction.TransactionId); - foundTxn.ShouldNotBeNull(transaction.TransactionId.ToString()); + var foundTxn = transactionDetailReportResponse.Transactions.Single(t => t.Id == transaction.TransactionId); + AssertTransactionDetailMatches(foundTxn, transaction); } } @@ -563,11 +679,12 @@ public async Task TransactionsEndpoint_TransactionDetailReport_OperatorFilter_Tr transactionDetailReportResponse.Summary.ShouldNotBeNull(); transactionDetailReportResponse.Summary.TransactionCount.ShouldBe(filteredTransactions.Count()); transactionDetailReportResponse.Summary.TotalValue.ShouldBe(filteredTransactions.Sum(t => t.TransactionAmount)); + transactionDetailReportResponse.Summary.TotalFees.ShouldBe(0m); foreach (Transaction transaction in filteredTransactions) { - var foundTxn = transactionDetailReportResponse.Transactions.SingleOrDefault(t => t.Id == transaction.TransactionId); - foundTxn.ShouldNotBeNull(transaction.TransactionId.ToString()); + var foundTxn = transactionDetailReportResponse.Transactions.Single(t => t.Id == transaction.TransactionId); + AssertTransactionDetailMatches(foundTxn, transaction); } } @@ -610,7 +727,7 @@ public async Task TransactionsEndpoint_TransactionDetailReport_ProductFilter_Tra await this.helper.AddTransactionsX(transactions); - IEnumerable<(Guid productId, String productName, Decimal? productValue, Int32 contractProductReportingId)> productsForFilter = this.contractProducts.SelectMany(cp => cp.Value).Where(p => p.productName == "100 KES Topup"); + IEnumerable<(Guid productId, String productName, Decimal productValue, Int32 contractProductReportingId)> productsForFilter = this.contractProducts.SelectMany(cp => cp.Value).Where(p => p.productName == "100 KES Topup"); List orderedDates = transactionDates.OrderBy(x => x).ToList(); TransactionDetailReportRequest request = new TransactionDetailReportRequest @@ -635,11 +752,12 @@ public async Task TransactionsEndpoint_TransactionDetailReport_ProductFilter_Tra transactionDetailReportResponse.Summary.ShouldNotBeNull(); transactionDetailReportResponse.Summary.TransactionCount.ShouldBe(filteredTransactions.Count()); transactionDetailReportResponse.Summary.TotalValue.ShouldBe(filteredTransactions.Sum(t => t.TransactionAmount)); + transactionDetailReportResponse.Summary.TotalFees.ShouldBe(0m); foreach (Transaction transaction in filteredTransactions) { - var foundTxn = transactionDetailReportResponse.Transactions.SingleOrDefault(t => t.Id == transaction.TransactionId); - foundTxn.ShouldNotBeNull(transaction.TransactionId.ToString()); + var foundTxn = transactionDetailReportResponse.Transactions.Single(t => t.Id == transaction.TransactionId); + AssertTransactionDetailMatches(foundTxn, transaction); } } @@ -649,11 +767,11 @@ public async Task TransactionsEndpoint_TransactionSummaryByMerchantReport_NoFilt { var transactions = new List(); - TransactionProcessor.Database.Entities.Merchant merchant1 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 1"); - var merchant2 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 2"); - var merchant3 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 3"); - var safaricomContract = this.contractList.SingleOrDefault(c => c.contractName == "Safaricom Contract"); - var voucherContract = this.contractList.SingleOrDefault(c => c.contractName == "Healthcare Centre 1 Contract"); + TransactionProcessor.Database.Entities.Merchant merchant1 = this.merchantsList.Single(m => m.Name == "Test Merchant 1"); + var merchant2 = this.merchantsList.Single(m => m.Name == "Test Merchant 2"); + var merchant3 = this.merchantsList.Single(m => m.Name == "Test Merchant 3"); + var safaricomContract = this.contractList.Single(c => c.contractName == "Safaricom Contract"); + var voucherContract = this.contractList.Single(c => c.contractName == "Healthcare Centre 1 Contract"); var safaricomProduct = this.contractProducts.Single(cp => cp.Key == safaricomContract.contractId).Value.First(); var voucherProduct = this.contractProducts.Single(cp => cp.Key == voucherContract.contractId).Value.First(); @@ -690,7 +808,7 @@ public async Task TransactionsEndpoint_TransactionSummaryByMerchantReport_NoFilt foreach (TransactionProcessor.Database.Entities.Merchant merchant in merchants) { // Build merchant sales - KeyValuePair<(DateTime, TransactionProcessor.Database.Entities.Merchant), Int32> config = salesConfig.SingleOrDefault(c => c.Key == (transactionDate, merchant)); + KeyValuePair<(DateTime, TransactionProcessor.Database.Entities.Merchant), Int32> config = salesConfig.Single(c => c.Key == (transactionDate, merchant)); for (int i = 0; i < config.Value; i++) { string responseCode = i switch { @@ -755,15 +873,13 @@ public async Task TransactionsEndpoint_TransactionSummaryByMerchantReport_NoFilt transactionSummaryByMerchantResponse.Summary.TotalMerchants.ShouldBe(3); transactionSummaryByMerchantResponse.Summary.TotalCount.ShouldBe(transactions.Count); transactionSummaryByMerchantResponse.Summary.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + transactionSummaryByMerchantResponse.Summary.AverageValue.ShouldBe(transactions.Sum(t => t.TransactionAmount) / transactions.Count); transactionSummaryByMerchantResponse.Merchants.Count.ShouldBe(3); - var operators = new List { safaricomContract.operatorId, voucherContract.operatorId }; foreach (var mr in merchants) { - var row = transactionSummaryByMerchantResponse.Merchants.SingleOrDefault(m => m.MerchantId == mr.MerchantId); - row.ShouldNotBeNull(); - row.MerchantId.ShouldBe(mr.MerchantId); - row.TotalValue.ShouldBe(transactions.Where(t => t.MerchantId == mr.MerchantId).Sum(t => t.TransactionAmount)); - row.TotalCount.ShouldBe(transactions.Count(t => t.MerchantId == mr.MerchantId)); + var merchantTransactions = transactions.Where(t => t.MerchantId == mr.MerchantId).ToList(); + var row = transactionSummaryByMerchantResponse.Merchants.Single(m => m.MerchantId == mr.MerchantId); + AssertMerchantSummaryRowMatchesSource(row, mr, merchantTransactions); row.AuthorisedCount.ShouldBe(authorisedCountsByMerchant[mr]); row.DeclinedCount.ShouldBe(declinedCountsByMerchant[mr]); } @@ -775,11 +891,11 @@ public async Task TransactionsEndpoint_TransactionSummaryByOperatorReport_NoFilt var transactions = new List(); - TransactionProcessor.Database.Entities.Merchant merchant1 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 1"); - var merchant2 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 2"); - var merchant3 = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 3"); - var safaricomContract = this.contractList.SingleOrDefault(c => c.contractName == "Safaricom Contract"); - var voucherContract = this.contractList.SingleOrDefault(c => c.contractName == "Healthcare Centre 1 Contract"); + TransactionProcessor.Database.Entities.Merchant merchant1 = this.merchantsList.Single(m => m.Name == "Test Merchant 1"); + var merchant2 = this.merchantsList.Single(m => m.Name == "Test Merchant 2"); + var merchant3 = this.merchantsList.Single(m => m.Name == "Test Merchant 3"); + var safaricomContract = this.contractList.Single(c => c.contractName == "Safaricom Contract"); + var voucherContract = this.contractList.Single(c => c.contractName == "Healthcare Centre 1 Contract"); var safaricomProduct = this.contractProducts.Single(cp => cp.Key == safaricomContract.contractId).Value.First(); var voucherProduct = this.contractProducts.Single(cp => cp.Key == voucherContract.contractId).Value.First(); @@ -818,7 +934,7 @@ public async Task TransactionsEndpoint_TransactionSummaryByOperatorReport_NoFilt { // Build merchant sales - KeyValuePair<(DateTime, TransactionProcessor.Database.Entities.Merchant), Int32> config = salesConfig.SingleOrDefault(c => c.Key == (transactionDate, merchant)); + KeyValuePair<(DateTime, TransactionProcessor.Database.Entities.Merchant), Int32> config = salesConfig.Single(c => c.Key == (transactionDate, merchant)); for (int i = 0; i < config.Value; i++) { @@ -895,16 +1011,15 @@ public async Task TransactionsEndpoint_TransactionSummaryByOperatorReport_NoFilt transactionSummaryByOperatorResponse.Summary.TotalOperators.ShouldBe(2); transactionSummaryByOperatorResponse.Summary.TotalCount.ShouldBe(transactions.Count); transactionSummaryByOperatorResponse.Summary.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + transactionSummaryByOperatorResponse.Summary.AverageValue.ShouldBe(transactions.Sum(t => t.TransactionAmount) / transactions.Count); transactionSummaryByOperatorResponse.Operators.Count.ShouldBe(2); var operators = new List { safaricomContract.operatorId, voucherContract.operatorId }; foreach (var op in operators) { - var row = transactionSummaryByOperatorResponse.Operators.SingleOrDefault(o => o.OperatorId == op); - row.ShouldNotBeNull(); - row.OperatorId.ShouldBe(op); - row.TotalValue.ShouldBe(transactions.Where(t => t.OperatorId == op).Sum(t => t.TransactionAmount)); - row.TotalCount.ShouldBe(transactions.Count(t => t.OperatorId == op)); + var operatorTransactions = transactions.Where(t => t.OperatorId == op).ToList(); + var row = transactionSummaryByOperatorResponse.Operators.Single(o => o.OperatorId == op); + AssertOperatorSummaryRowMatchesSource(row, this.operatorsList.Single(o => o.OperatorId == op), operatorTransactions); row.AuthorisedCount.ShouldBe(authorisedCountsByOperator[op]); row.DeclinedCount.ShouldBe(declinedCountsByOperator[op]); } @@ -925,7 +1040,7 @@ public async Task TransactionsEndpoint_ProductPerformanceReport_SummaryDataRetur var transactions = new List(); - TransactionProcessor.Database.Entities.Merchant merchant = this.merchantsList.SingleOrDefault(m => m.Name == "Test Merchant 1"); + TransactionProcessor.Database.Entities.Merchant merchant = this.merchantsList.Single(m => m.Name == "Test Merchant 1"); List transactionDates = [ DateTime.Now.Date, @@ -937,9 +1052,9 @@ public async Task TransactionsEndpoint_ProductPerformanceReport_SummaryDataRetur foreach (DateTime transactionDate in transactionDates) { foreach (var productData in productPerformanceData) { var transactionCount = productData.Value; - var contract = this.contractList.SingleOrDefault(c => c.operatorName == productData.Key.@operator); + var contract = this.contractList.Single(c => c.operatorName == productData.Key.@operator); var productList = this.contractProducts.Where(cp => cp.Key == contract.contractId).SelectMany(cp => cp.Value).ToList(); - var product = productList.SingleOrDefault(p => p.productName == productData.Key.product); + var product = productList.Single(p => p.productName == productData.Key.product); for (int i = 0; i < transactionCount; i++) { Transaction transaction = await this.helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); transactions.Add(transaction); @@ -958,16 +1073,16 @@ public async Task TransactionsEndpoint_ProductPerformanceReport_SummaryDataRetur productPerformanceResponse.Summary.TotalProducts.ShouldBe(8); productPerformanceResponse.Summary.TotalCount.ShouldBe(transactions.Count); productPerformanceResponse.Summary.TotalValue.ShouldBe(transactions.Sum(t => t.TransactionAmount)); + productPerformanceResponse.Summary.AveragePerProduct.ShouldBe(transactions.Sum(t => t.TransactionAmount) / productPerformanceData.Count); foreach (KeyValuePair<(String @operator, String product), Int32> productData in productPerformanceData) { - var contract = this.contractList.SingleOrDefault(c => c.operatorName == productData.Key.@operator); + var contract = this.contractList.Single(c => c.operatorName == productData.Key.@operator); var productList = this.contractProducts.Single(cp => cp.Key == contract.contractId).Value; - var product = productList.SingleOrDefault(p => p.productName == productData.Key.product); - var productPerformanceResponseDetail = productPerformanceResponse.ProductDetails.SingleOrDefault(p => p.ProductId == product.productId); - productPerformanceResponseDetail.ShouldNotBeNull(); - productPerformanceResponseDetail.TransactionCount.ShouldBe(transactions.Count(t => t.ContractProductId == product.productId)); - productPerformanceResponseDetail.TransactionValue.ShouldBe(transactions.Where(t => t.ContractProductId == product.productId).Sum(t => t.TransactionAmount)); - productPerformanceResponseDetail.PercentageOfTotal.ShouldNotBe(0); + var product = productList.Single(p => p.productName == productData.Key.product); + var productTransactions = transactions.Where(t => t.ContractProductId == product.productId).ToList(); + var productPerformanceResponseDetail = productPerformanceResponse.ProductDetails.Single(p => p.ProductId == product.productId); + var expectedPercentageOfTotal = 100m * productTransactions.Sum(t => t.TransactionAmount) / transactions.Sum(t => t.TransactionAmount); + AssertProductPerformanceRowMatchesSource(productPerformanceResponseDetail, this.context.ContractProducts.Single(cp => cp.ContractProductId == product.productId), this.context.Contracts.Single(c => c.ContractId == contract.contractId), productTransactions, expectedPercentageOfTotal); } } @@ -984,6 +1099,8 @@ private static int RandomizeCount(int baseCount, Random rnd, double variability public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned() { Stopwatch sw = Stopwatch.StartNew(); + DateTime now = DateTime.Now; + int safeHourLimit = Math.Max(0, now.Hour - 2); List todaysTransactions = new List(); List comparisonDateTransactions = new List(); @@ -991,9 +1108,9 @@ public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned() Dictionary transactionCounts = new() { { "Test Merchant 1", 3 }, { "Test Merchant 2", 6 }, { "Test Merchant 3", 2 }, { "Test Merchant 4", 0 } }; // TODO: make counts dynamic - DateTime todaysDateTime = DateTime.Now; + DateTime todaysDateTime = now; - for (int hour = 0; hour < 24; hour++) + for (int hour = 0; hour <= safeHourLimit; hour++) { List localList = new List(); DateTime date = new DateTime(todaysDateTime.Year, todaysDateTime.Month, todaysDateTime.Day, hour, 0, 0); @@ -1040,7 +1157,7 @@ public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned() sw.Restart(); DateTime comparisonDate = todaysDateTime.AddDays(-1); - for (int hour = 0; hour < 24; hour++) + for (int hour = 0; hour <= safeHourLimit; hour++) { List localList = new List(); DateTime date = new DateTime(comparisonDate.Year, comparisonDate.Month, comparisonDate.Day, hour, 0, 0); @@ -1091,13 +1208,28 @@ public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned() var todaysSalesByHour = result.Data; todaysSalesByHour.ShouldNotBeNull(); - foreach (var hour in todaysSalesByHour) { + var expectedHours = todaysTransactions + .GroupBy(t => t.TransactionDateTime.Hour) + .Select(g => g.Key) + .Union(comparisonDateTransactions + .GroupBy(t => t.TransactionDateTime.Hour) + .Select(g => g.Key)) + .OrderBy(x => x) + .ToList(); + + var orderedTodaysSalesByHour = todaysSalesByHour.OrderBy(x => x.Hour).ToList(); + orderedTodaysSalesByHour.Select(x => x.Hour).ShouldBe(expectedHours); + + foreach (var hour in orderedTodaysSalesByHour) { hour.ShouldNotBeNull(); - hour.TodaysSalesCount.ShouldBe(todaysTransactions.Count(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= DateTime.Now.TimeOfDay), hour.Hour.ToString()); - hour.TodaysSalesValue.ShouldBe(todaysTransactions.Where(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= DateTime.Now.TimeOfDay).Sum(t => t.TransactionAmount), hour.Hour.ToString()); - hour.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= DateTime.Now.TimeOfDay), hour.Hour.ToString()); - hour.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= DateTime.Now.TimeOfDay).Sum(t => t.TransactionAmount), hour.Hour.ToString()); + var todaysHourTransactions = todaysTransactions.Where(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= now.TimeOfDay).ToList(); + var comparisonHourTransactions = comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == hour.Hour && t.TransactionTime <= now.TimeOfDay).ToList(); + + hour.TodaysSalesCount.ShouldBe(todaysHourTransactions.Count, hour.Hour.ToString()); + hour.TodaysSalesValue.ShouldBe(todaysHourTransactions.Sum(t => t.TransactionAmount), hour.Hour.ToString()); + hour.ComparisonSalesCount.ShouldBe(comparisonHourTransactions.Count, hour.Hour.ToString()); + hour.ComparisonSalesValue.ShouldBe(comparisonHourTransactions.Sum(t => t.TransactionAmount), hour.Hour.ToString()); } } @@ -1106,7 +1238,8 @@ public async Task TransactionsEndpoint_MerchantDailyPerformanceSummary_ReturnsMe { TransactionProcessor.Database.Entities.Merchant merchant = this.merchantsList.Single(m => m.Name == "Test Merchant 1"); var contract = this.contractList.Single(c => c.contractName == "Safaricom Contract"); - var product = this.contractProducts.Single(cp => cp.Key == contract.contractId).Value.First(); + var topProduct = this.contractProducts.Single(cp => cp.Key == contract.contractId).Value.Single(p => p.productName == "50 KES Topup"); + var secondaryProduct = this.contractProducts.Single(cp => cp.Key == contract.contractId).Value.Single(p => p.productName == "100 KES Topup"); var operatorRecord = this.operatorsList.Single(o => o.OperatorId == contract.operatorId); DateTime transactionDate = DateTime.Now.Date; List transactions = new(); @@ -1121,14 +1254,32 @@ public async Task TransactionsEndpoint_MerchantDailyPerformanceSummary_ReturnsMe merchant.MerchantId, contract.operatorId, contract.contractId, - product.productId, + topProduct.productId, responseCode, - product.productValue, + topProduct.productValue, i); transactions.Add(transaction); } + for (int i = 1; i <= 4; i++) + { + DateTime transactionDateTime = transactionDate.AddHours(15 + i); + string responseCode = i % 2 == 0 ? "1009" : "0000"; + + Transaction transaction = await this.helper.BuildTransactionX( + transactionDateTime, + merchant.MerchantId, + contract.operatorId, + contract.contractId, + secondaryProduct.productId, + responseCode, + secondaryProduct.productValue, + 100 + i); + + transactions.Add(transaction); + } + await this.helper.AddTransactionsX(transactions); MerchantDailyPerformanceSummaryRequest request = new() @@ -1150,16 +1301,89 @@ public async Task TransactionsEndpoint_MerchantDailyPerformanceSummary_ReturnsMe response.Metrics.ShouldNotBeNull(); response.DrillDownTransactions.ShouldNotBeNull(); + decimal totalSalesValue = transactions.Sum(t => t.TransactionAmount); + decimal successfulSalesValue = transactions.Where(t => t.IsAuthorised).Sum(t => t.TransactionAmount); + decimal failedSalesValue = transactions.Where(t => t.IsAuthorised == false).Sum(t => t.TransactionAmount); + decimal averageSalesValue = transactions.Count == 0 ? 0m : totalSalesValue / transactions.Count; + response.Metrics.Count.ShouldBe(9); - response.Metrics.Single(m => m.Title == "Total Sales Count").Value.ShouldBe(6); - response.Metrics.Single(m => m.Title == "Successful Sales Count").Value.ShouldBe(3); - response.Metrics.Single(m => m.Title == "Failed Sales Count").Value.ShouldBe(3); - response.Metrics.Single(m => m.Title == "Top Product Sales Count").Value.ShouldBe(6); - response.Metrics.Single(m => m.Title == "Top Product Sales Count").Description.ShouldBe($"{operatorRecord.Name} {product.productName}"); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Total Sales Count"), + "Total Sales Count", + 10, + "All sales transactions in the range", + 1, + 0); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Total Sales Value"), + "Total Sales Value", + totalSalesValue, + "All sales value in the range", + 1, + 1); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Successful Sales Count"), + "Successful Sales Count", + 5, + "Authorised sales count in the range", + 2, + 2); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Successful Sales Value"), + "Successful Sales Value", + successfulSalesValue, + "Authorised sales value in the range", + 2, + 3); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Failed Sales Count"), + "Failed Sales Count", + 5, + "Declined sales count in the range", + 3, + 4); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Failed Sales Value"), + "Failed Sales Value", + failedSalesValue, + "Declined sales value in the range", + 3, + 5); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Average Sales Count"), + "Average Sales Count", + 10m, + "Average sales count per day in the range", + 4, + 6); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Average Sales Value"), + "Average Sales Value", + averageSalesValue, + "Average value per sale in the range", + 4, + 7); + AssertMerchantDailyPerformanceMetricMatches( + response.Metrics.Single(m => m.Title == "Top Product Sales Count"), + "Top Product Sales Count", + 6, + $"{operatorRecord.Name} {topProduct.productName}", + 5, + 8); + + var expectedDrillDownTransactions = transactions + .OrderByDescending(t => t.TransactionDateTime) + .Take(5) + .ToList(); response.DrillDownTransactions.Count.ShouldBe(5); - response.DrillDownTransactions.Select(x => x.Reference).ShouldBe(new[] { "0006", "0005", "0004", "0003", "0002" }); - response.DrillDownTransactions.Select(x => x.Status).ShouldBe(new[] { "Failed", "Successful", "Failed", "Successful", "Failed" }); + for (int i = 0; i < expectedDrillDownTransactions.Count; i++) { + var expectedTransactionProduct = this.context.ContractProducts.Single(p => p.ContractProductId == expectedDrillDownTransactions[i].ContractProductId && p.ContractId == expectedDrillDownTransactions[i].ContractId).ProductName; + AssertDrillDownTransactionMatches( + response.DrillDownTransactions[i], + expectedDrillDownTransactions[i], + $"{operatorRecord.Name} {expectedTransactionProduct}"); + } } [Fact] @@ -1238,12 +1462,8 @@ static JsonElement GetPropertyCaseInsensitive(JsonElement element, string proper JsonElement items = GetPropertyCaseInsensitive(document.RootElement, "items"); items.GetArrayLength().ShouldBe(2); - GetPropertyCaseInsensitive(items[0], "reference").GetString().ShouldBe("0012"); - GetPropertyCaseInsensitive(items[1], "reference").GetString().ShouldBe("0011"); - GetPropertyCaseInsensitive(items[0], "receiptReference").GetString().ShouldBe("receipt-002"); - GetPropertyCaseInsensitive(items[1], "receiptReference").GetString().ShouldBe("receipt-001"); - GetPropertyCaseInsensitive(items[0], "operator").GetString().ShouldBe(@operator.Name); - DateTime.Parse(GetPropertyCaseInsensitive(items[0], "transactionDateTime").GetString()!).ShouldBe(reportDate.AddHours(10)); + AssertRecentActivityReceiptItemMatches(items[0], transactions[1], product.productName, @operator.Name, "receipt-002"); + AssertRecentActivityReceiptItemMatches(items[1], transactions[0], product.productName, @operator.Name, "receipt-001"); } [Fact] @@ -1290,7 +1510,6 @@ await this.helper.BuildTransactionX(reportDate.AddHours(9), otherMerchant.Mercha GetPropertyCaseInsensitive(document.RootElement, "totalCount").GetInt32().ShouldBe(2); JsonElement items = GetPropertyCaseInsensitive(document.RootElement, "items"); items.GetArrayLength().ShouldBe(1); - GetPropertyCaseInsensitive(items[0], "reference").GetString().ShouldBe("0012"); - GetPropertyCaseInsensitive(items[0], "receiptReference").GetString().ShouldBe("receipt-002"); + AssertRecentActivityReceiptItemMatches(items[0], transactions[1], product.productName, contract.operatorName, "receipt-002"); } } diff --git a/EstateReportingAPI.IntegrationTests/xunit.runner.json b/EstateReportingAPI.IntegrationTests/xunit.runner.json index a4c61e5..44798cd 100644 --- a/EstateReportingAPI.IntegrationTests/xunit.runner.json +++ b/EstateReportingAPI.IntegrationTests/xunit.runner.json @@ -1,4 +1,4 @@ { - "maxParallelThreads": 1, - "parallelizeTestCollections": false + "maxParallelThreads": 3, + "parallelizeTestCollections": true } \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/FileImportHandler.cs b/EstateReportingAPI/Handlers/FileImportHandler.cs index d4a9ff1..6b29f5c 100644 --- a/EstateReportingAPI/Handlers/FileImportHandler.cs +++ b/EstateReportingAPI/Handlers/FileImportHandler.cs @@ -81,11 +81,7 @@ public static async Task GetFileImportLog([FromHeader] Guid estateId, DateTimeUploaded = fd.DateTimeUploaded, FileId = fd.FileId, FileName = Path.GetFileName(fd.FileName), - FileProfile = fd.FileProfile.ToUpper() switch { - "B2A59ABF-293D-4A6B-B81B-7007503C3476" => "Safaricom Topup", - "8806EDBC-3ED6-406B-9E5F-A9078356BE99" => "Voucher Issue", - _ => "Unknown" - }, + FileProfile = fd.FileProfile, MerchantId = fd.MerchantId, MerchantName = fd.MerchantName, UploadedBy = fd.UploadedBy, diff --git a/EstateReportingAPI/Handlers/MerchantHandler.cs b/EstateReportingAPI/Handlers/MerchantHandler.cs index d3238c1..40524ad 100644 --- a/EstateReportingAPI/Handlers/MerchantHandler.cs +++ b/EstateReportingAPI/Handlers/MerchantHandler.cs @@ -29,12 +29,14 @@ public static async Task GetRecentMerchants([FromHeader] Guid estateId, SettlementSchedule = m.SettlementSchedule, CreatedDateTime = m.CreatedDateTime, Balance = m.Balance, + AddressId = m.AddressId, AddressLine1 = m.AddressLine1, AddressLine2 = m.AddressLine2, Town = m.Town, Region = m.Region, PostCode = m.PostCode, Country = m.Country, + ContactId = m.ContactId, ContactName = m.ContactName, ContactEmail = m.ContactEmail, ContactPhone = m.ContactPhone @@ -62,12 +64,14 @@ public static async Task GetMerchants([FromHeader] Guid estateId, SettlementSchedule = m.SettlementSchedule, CreatedDateTime = m.CreatedDateTime, Balance = m.Balance, + AddressId = m.AddressId, AddressLine1 = m.AddressLine1, AddressLine2 = m.AddressLine2, Town = m.Town, Region = m.Region, PostCode = m.PostCode, Country = m.Country, + ContactId = m.ContactId, ContactName = m.ContactName, ContactEmail = m.ContactEmail, ContactPhone = m.ContactPhone diff --git a/EstateReportingAPI/Handlers/TransactionHandler.cs b/EstateReportingAPI/Handlers/TransactionHandler.cs index adec68a..2e29eee 100644 --- a/EstateReportingAPI/Handlers/TransactionHandler.cs +++ b/EstateReportingAPI/Handlers/TransactionHandler.cs @@ -328,7 +328,8 @@ MerchantDailyPerformanceSummaryResponse SuccessFactory(Models.MerchantDailyPerfo Title = metric.Title, Value = metric.Value, Description = metric.Description, - Category = metric.Category + Category = metric.Category, + Type = metric.Type }).ToList() ?? [], DrillDownTransactions = r.DrillDownTransactions?.Select(transaction => new DrillDownTransaction { diff --git a/_codexout/itest-run-2/failures2.trx b/_codexout/itest-run-2/failures2.trx new file mode 100644 index 0000000..e2e44c5 --- /dev/null +++ b/_codexout/itest-run-2/failures2.trx @@ -0,0 +1,177 @@ +System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\55061736-6809-44a9-aabe-feae67441d09' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\faac0c06-87c1-47b8-aff9-ba3f7fa5b0ea' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\8c0370bb-83e1-4521-8a23-a30f5b930cdc' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\96f1dff5-9c1f-4b02-ae65-5e55fac5f9f8' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\190499de-d587-47bd-9616-cfbb0bfe849e' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\a2261e01-04dc-4e91-859b-97dd7201c18c' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\8952832b-777e-401d-aa18-a61580b49c2c' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\bcf4a266-b757-43f1-ad3c-9303f955a33a' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\d2b1ef96-6bc3-4f73-9058-008652955a66' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\9915eeb7-41b8-4e71-bb13-0ea7850f3e03' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\86c4eec4-790c-4a88-b807-ca2abdaacab2' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\d880067f-98b1-466b-8715-d053983bc000' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\10530f42-61ee-47ba-9184-163ed775f6c2' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\51d266a0-3848-4dbc-86c7-62d1cde26223' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\fab6b34d-c066-471f-9fdb-9bee118a0f29' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\34920c17-77a2-4a19-8ea4-d46579138068' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\962a8133-cf4f-4965-a986-46c1c1ba065f' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\2aade7e0-37f4-4e13-91a9-dc0d9c9628fa' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\fa900f78-add9-4b3d-aa27-16106b0b8cb0' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\bce81e67-919e-48dc-bb49-6a18be53fcb9' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\58dc0750-a54e-4122-aa99-07731ef17aba' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\79abcc29-58b1-4ed6-9a63-e65ef7f0592a' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\2b4ded8b-99b0-4ebd-85e6-acdf93e75f16' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\059a6b78-73fa-46c3-9020-6e8708cb26a3' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\0c9394c1-6de0-4d4b-8c0c-bb2674cd90ce' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\c127b63c-de30-4da7-bd99-20cd5751d3e9' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\29564c57-e5ea-47db-be92-da484f2789e1' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\d753e658-6cec-48d4-8595-04fc09dac565' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\6a2ca34f-ab95-4db2-9d4f-b6144cc67eb4' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\c36749dc-3a94-4e05-8cd9-79fd030a47af' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\fd5bb758-5261-49a0-bd4d-c7c67edd6f0f' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\78391306-ced0-4768-8b41-3ee66e2f688c' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\662db42b-32f2-495a-86b0-377a8f2af296' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\b1b143a9-ace3-40bb-b31e-04c8cccf2be6' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\a94062e7-5fad-4bf9-ae57-1fded8f26add' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\23994f64-97d4-43c0-ace2-f3a1d3bb2096' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\a9ac960a-40ef-4e2e-a354-9c229d843c60' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\2d320ab8-8811-4fe4-9771-0fc13545439b' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\89502828-1b25-4648-a127-4ec9325f5905' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\ae619421-6efe-4ed8-99b6-4554e22f8990' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\6fcb20e8-a2dc-4ab4-8c39-53221e3031e4' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\9c0a18df-3716-47e1-9ed3-a5ea3381632c' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\c11f6eea-2ed5-4d45-ba9a-f715201dd909' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\393356fa-b31b-49bb-bcbe-12ca1005704f' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43 \ No newline at end of file diff --git a/_codexout/itest-run/failures.trx b/_codexout/itest-run/failures.trx new file mode 100644 index 0000000..26a6711 --- /dev/null +++ b/_codexout/itest-run/failures.trx @@ -0,0 +1,177 @@ +System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\eda33c99-fe28-4152-ab0c-e4e7038ab012' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\fd79c1bc-a617-4379-837c-144fa17d06a8' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\1ae1a9d4-4ccf-485a-96d7-c984834672d6' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\663a86f7-e403-4483-a2ca-16a042cbef22' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\71bd80c5-0c78-44e9-a858-9c1cfd2d3bb0' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\0c9f69b5-d54a-403f-ac63-736e420ebee2' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\fddbf57c-43ae-44e9-96e1-35d83030d2fc' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\07c02624-c349-44ad-972e-a18bbf1a25ea' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\deb12001-ccf9-44e1-87d3-c2286cb77bd8' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\edbab0e5-0f68-4bc0-b216-bb1ec55aad8d' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\8227315e-eb01-4c84-a9da-78e63ac3f245' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\d5dda6b7-e5a1-40fb-9802-2644095c94b7' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\914e258e-70ae-4b03-b7a9-d4c4ab5ba672' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\cfeb0caa-82bf-437c-b584-58f566d8068f' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\8d0a8813-e6f5-4fb3-8376-eed4e617f100' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\0052332e-efee-4133-9f06-0ecab99ca9b1' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\46195287-a88a-44df-9e74-0412fc5bd73a' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\b7d40f8b-8f15-4a14-8b4d-30aed50f93f2' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\08846868-8ad9-425a-9a11-9c2d5d9dde1a' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\b41430f9-29c9-4f00-8896-cf9a4e0d5682' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\03c0951b-4a08-4079-81f3-517ac0fa9ee4' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\e4d4ae90-cb07-46ed-bdf1-33125f39bd77' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\69fcdc6e-53b3-4cbd-b34c-fc2c28ac6d51' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\a02b16b6-7797-426a-9857-51aa507c79aa' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\e0f68f20-7e5a-4c2a-be00-8f1005595e29' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\9a065640-d175-4bb2-a9a1-13449c91a03e' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\474405ab-430a-4911-b310-7f7fd55b5706' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\e266a238-d463-4960-aee6-148ff3909adc' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\6f3248bb-b20d-45b7-b17c-6a86cdb83163' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\546b1ea6-34a5-4399-89b7-2e4558f0c884' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\80f9b7ed-4ae2-4db9-a445-6978cc915958' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\19808c0b-0ce2-459e-ac95-f22d81b10116' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\d5d54740-e9c5-4531-882a-6f9232db5e86' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\c2fd9a60-0b43-4801-8a76-45f57b31b64b' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\c0e55f84-38d6-463d-bad4-28de87f2e803' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\351a12d0-cc4f-4618-a853-1627687ebc11' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\5bbc53a6-fc61-43e2-99f0-de5c29c3e7ed' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\cb7b2d0b-ef27-4038-ab0f-7110ac797210' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\8094ef8f-47ce-4908-a8e2-9866f8c840ce' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\9aeeb859-e12a-443b-b090-8703c91fc935' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\35dee806-6754-4e7f-a11f-016b757e3adf' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\816b5ef8-c966-48e9-8b12-5fb168b54b35' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\7dfa9774-954e-45ea-96b7-aefc39b3042d' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43System.UnauthorizedAccessException : Access to the path 'C:\home\txnproc\trace\7aebb9bc-87d4-4047-a67e-69650bec4e4b' is denied. at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor) + at System.IO.Directory.CreateDirectory(String path) + at Shared.IntegrationTesting.TestContainers.DockerHelper.SetHostTraceFolder(String scenarioName) + at Shared.IntegrationTesting.TestContainers.DockerHelper.StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + at EstateReportingAPI.IntegrationTests.ControllerTestsBase.InitializeAsync() in G:\Git\TransactionProcessing\EstateReportingAPI\EstateReportingAPI.IntegrationTests\ControllerTestsBase.cs:line 43 \ No newline at end of file From 5b2d8012a9a6738e9921c7f1dd95a168d22de222 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 25 Aug 2026 06:08:30 +0100 Subject: [PATCH 2/2] test debugging --- .../TransactionsEndpointTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs index 5610cd2..9b039f7 100644 --- a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using EstateReportingAPI.DataTransferObjects; +using Microsoft.EntityFrameworkCore; using Shared.Serialisation; using Shouldly; using SimpleResults; @@ -1203,6 +1204,20 @@ public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned() await this.helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); await this.helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + var todaysReadModelRows = await this.context.TodayTransactions + .Where(t => t.TransactionDate == todaysDateTime.Date) + .Select(t => new { t.TransactionDateTime, t.Hour }) + .ToListAsync(); + todaysReadModelRows.ShouldNotBeEmpty(); + todaysReadModelRows.ShouldAllBe(x => x.Hour == x.TransactionDateTime.Hour); + + var comparisonReadModelRows = await this.context.TransactionHistory + .Where(t => t.TransactionDate == comparisonDate.Date) + .Select(t => new { t.TransactionDateTime, t.Hour }) + .ToListAsync(); + comparisonReadModelRows.ShouldNotBeEmpty(); + comparisonReadModelRows.ShouldAllBe(x => x.Hour == x.TransactionDateTime.Hour); + var result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/todayssalesbyhour?comparisondate={comparisonDate:yyyy-MM-dd}", CancellationToken.None); result.IsSuccess.ShouldBeTrue(); var todaysSalesByHour = result.Data;