From 7100bad4e3e071f88433f9e7fe3e7d9f296407cd Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 16 Oct 2026 18:05:06 +0100 Subject: [PATCH 1/2] Add profile version processing diagnostics --- .../FileProcessorDomainServiceTests.cs | 20 +++++++++++++ .../Services/FileProcessorDomainService.cs | 30 +++++++++++++------ FileProcessor.Testing/TestData.cs | 20 +++++++++++-- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs index 0b0e349..2257be8 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs @@ -830,6 +830,26 @@ public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_FilePr result.IsFailed.ShouldBeTrue(); result.Status.ShouldBe(ResultStatus.NotFound); } + + [Fact] + public async Task ProcessTransactionForFileLine_WhenRecordedProfileVersionIsMissing_ReturnsActionableFailure() + { + this.FileAggregateRepository.GetLatestVersion(Arg.Any(), Arg.Any()) + .ReturnsAsync(Result.Success(TestData.GetFileAggregateWithLines())); + this.FileProcessorManager.GetFileProfileVersion(Arg.Any(), + Arg.Any(), + Arg.Any()) + .ReturnsAsync(Result.NotFound()); + + Result result = await this.FileProcessorDomainService.ProcessTransactionForFileLine( + TestData.ProcessTransactionForFileLineCommand, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + result.Message.ShouldContain(TestData.FileId.ToString()); + result.Message.ShouldContain(TestData.FileProfileId.ToString()); + result.Message.ShouldContain(TestData.FileProfileVersionId.ToString()); + } [Fact] public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_FileLineIgnored_RequestIsHandled() diff --git a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs index b297229..a4ee46e 100644 --- a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs @@ -267,7 +267,8 @@ public async Task ProcessUploadedFile(FileCommands.ProcessUploadedFileCo CancellationToken cancellationToken) { Result result = await ApplyFileUpdates(async (FileAggregate fileAggregate) => { Logger.LogWarning("In ProcessUploadedFile action"); - Result operatorIdResult = await GetOperatorIdForFileProfile(command.EstateId, + Result operatorIdResult = await GetOperatorIdForFileProfile(command.FileId, + command.EstateId, command.FileProfileId, command.FileProfileVersionId, cancellationToken); @@ -317,12 +318,14 @@ private async Task MoveAcceptedFileToFailedDirectory(Guid fileProfileId, file.MoveTo($"{fileProfileResult.Data.FailedDirectory}/{file.Name}", overwrite: true); } - private async Task> GetOperatorIdForFileProfile(Guid estateId, + private async Task> GetOperatorIdForFileProfile(Guid fileId, + Guid estateId, Guid fileProfileId, Guid fileProfileVersionId, CancellationToken cancellationToken){ - Result fileProfileResult = await this.GetFileProfileForProcessing(fileProfileId, + Result fileProfileResult = await this.GetFileProfileForProcessing(fileId, + fileProfileId, fileProfileVersionId, cancellationToken); @@ -434,7 +437,8 @@ public async Task ProcessTransactionForFileLine(FileCommands.ProcessTran } processingContext.Stage = FileLineProcessingStage.FileProfileLookup; - Result fileProfileResult = await this.GetFileProfileForProcessing(fileDetails.FileProfileId, + Result fileProfileResult = await this.GetFileProfileForProcessing(command.FileId, + fileDetails.FileProfileId, fileDetails.FileProfileVersionId, cancellationToken); if (fileProfileResult.IsFailed) @@ -756,7 +760,7 @@ private async Task ProcessFile(Guid fileId, FileProfileModel fileProfile = null; Result fileProfileResult = - await this.GetFileProfileForProcessing(fileProfileId, fileProfileVersionId, cancellationToken); + await this.GetFileProfileForProcessing(fileId, fileProfileId, fileProfileVersionId, cancellationToken); if (fileProfileResult.IsFailed) return ResultHelpers.CreateFailure(fileProfileResult); @@ -913,16 +917,24 @@ private async Task ProcessFile(Guid fileId, return result; } - private async Task> GetFileProfileForProcessing(Guid fileProfileId, + private async Task> GetFileProfileForProcessing(Guid fileId, + Guid fileProfileId, Guid fileProfileVersionId, CancellationToken cancellationToken) { if (fileProfileVersionId == Guid.Empty) return await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); - return await this.FileProcessorManager.GetFileProfileVersion(fileProfileId, - fileProfileVersionId, - cancellationToken); + Result versionResult = await this.FileProcessorManager.GetFileProfileVersion(fileProfileId, + fileProfileVersionId, + cancellationToken); + if (versionResult.IsFailed == false) + return versionResult; + + if (versionResult.Status == ResultStatus.NotFound) + return Result.NotFound($"File [{fileId}] references unavailable file profile version [{fileProfileVersionId}] for profile [{fileProfileId}]"); + + return ResultHelpers.CreateFailure(versionResult); } private static Int32 TransactionNumber = 0; diff --git a/FileProcessor.Testing/TestData.cs b/FileProcessor.Testing/TestData.cs index a701bae..b530864 100644 --- a/FileProcessor.Testing/TestData.cs +++ b/FileProcessor.Testing/TestData.cs @@ -273,7 +273,15 @@ public static FileAggregate GetCreatedFileAggregate() { FileAggregate fileAggregate = new FileAggregate(); - FileAggregateExtensions.CreateFileRequest createFileRequest = new FileAggregateExtensions.CreateFileRequest(TestData.FileImportLogId, TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FileProfileId, TestData.OriginalFileName, TestData.FileUploadedDateTime, TestData.SafaricomOperatorId); + FileAggregateExtensions.CreateFileRequest createFileRequest = new FileAggregateExtensions.CreateFileRequest(TestData.FileImportLogId, + TestData.EstateId, + TestData.MerchantId, + TestData.UserId, + TestData.FileProfileId, + TestData.OriginalFileName, + TestData.FileUploadedDateTime, + TestData.SafaricomOperatorId, + TestData.FileProfileVersionId); fileAggregate.CreateFile(createFileRequest); @@ -302,7 +310,15 @@ public static FileAggregate GetFileAggregateWithLines() { FileAggregate fileAggregate = new FileAggregate(); - FileAggregateExtensions.CreateFileRequest createFileRequest = new FileAggregateExtensions.CreateFileRequest(TestData.FileImportLogId, TestData.EstateId, TestData.MerchantId, TestData.UserId, TestData.FileProfileId, TestData.OriginalFileName, TestData.FileUploadedDateTime, TestData.SafaricomOperatorId); + FileAggregateExtensions.CreateFileRequest createFileRequest = new FileAggregateExtensions.CreateFileRequest(TestData.FileImportLogId, + TestData.EstateId, + TestData.MerchantId, + TestData.UserId, + TestData.FileProfileId, + TestData.OriginalFileName, + TestData.FileUploadedDateTime, + TestData.SafaricomOperatorId, + TestData.FileProfileVersionId); fileAggregate.CreateFile(createFileRequest); fileAggregate.AddFileLine("D,1,2"); From c4f755a6714f9c147ad6d12eddb90498afcb907f Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 16 Oct 2026 18:08:05 +0100 Subject: [PATCH 2/2] Cover processing with updated active profiles --- .../FileProcessorDomainServiceTests.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs index 2257be8..dd4276f 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileProcessorDomainServiceTests.cs @@ -665,6 +665,54 @@ public async Task FileRequestHandler_ProcessTransactionForFileLineRequest_Reques result.IsSuccess.ShouldBeTrue(); } + [Fact] + public async Task ProcessTransactionForFileLine_UsesRecordedProfileVersionAfterActiveProfileChanges() + { + this.FileProcessorManager.GetFileProfile(Arg.Any(), Arg.Any()) + .ReturnsAsync(TestData.FileProfileVoucher); + this.FileProcessorManager.GetFileProfileVersion(Arg.Any(), + TestData.FileProfileVersionId, + Arg.Any()) + .ReturnsAsync(TestData.FileProfileSafaricom with + { + FileProfileId = TestData.FileProfileId, + FileProfileVersionId = TestData.FileProfileVersionId + }); + + this.FileAggregateRepository.GetLatestVersion(Arg.Any(), Arg.Any()) + .ReturnsAsync(Result.Success(TestData.GetFileAggregateWithLines())); + this.FileAggregateRepository.SaveChanges(Arg.Any(), Arg.Any()) + .ReturnsAsync(Result.Success()); + this.TransactionProcessorClient.PerformTransaction(Arg.Any(), + Arg.Any(), + Arg.Any()) + .ReturnsAsync(TestData.ClientSaleTransactionResponse); + this.TransactionProcessorClient.GetMerchant(Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator); + this.TransactionProcessorClient.GetMerchantContracts(Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()) + .ReturnsAsync(Result.Success(TestData.GetMerchantContractsResponse() + .Where(contract => contract.OperatorName == TestData.SafaricomOperatorIdentifier) + .ToList())); + this.SecurityServiceClient.GetToken(Arg.Any(), + Arg.Any(), + Arg.Any()) + .ReturnsAsync(Result.Success(TestData.TokenResponse())); + this.FileFormatHandler.FileLineCanBeIgnored(Arg.Any()).Returns(false); + this.FileFormatHandler.ParseFileLine(Arg.Any()).Returns(TestData.TransactionMetadata); + + Result result = await this.FileProcessorDomainService.ProcessTransactionForFileLine( + TestData.ProcessTransactionForFileLineCommand, + CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + } + [Theory] [InlineData("Safaricom")] [InlineData("Voucher")]