From 33779fbce6b4b6b380ab9ffefb36aafee3ffae4c Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 16 Oct 2026 19:31:03 +0100 Subject: [PATCH 1/3] Validate file profile processing configuration --- .../FileProfileManagerTests.cs | 2 +- .../FileProfileAggregateTests.cs | 59 +++++++++++-- .../FileProfileAggregate.cs | 33 ++++++++ .../FileProfileConfiguration.cs | 23 +++++ .../Features/FileProfile.feature | 19 +++-- .../Features/FileProfile.feature.cs | 84 +++++++++++++++---- .../Features/FileProfileSteps.cs | 17 ++++ FileProcessor.Testing/TestData.cs | 9 +- FileProcessor/Bootstrapper/FileRegistry.cs | 5 +- 9 files changed, 212 insertions(+), 39 deletions(-) create mode 100644 FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs diff --git a/FileProcessor.BusinessLogic.Tests/FileProfileManagerTests.cs b/FileProcessor.BusinessLogic.Tests/FileProfileManagerTests.cs index 8f71afe..378d29c 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProfileManagerTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileProfileManagerTests.cs @@ -89,7 +89,7 @@ private FileProfileAggregateRoot CreateArchivedAggregate() }); aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest { - FileFormatHandler = "UpdatedHandler" + FileFormatHandler = TestData.VoucherFileFormatHandler }); aggregate.ArchiveProfile(TestData.FileProfileId); return aggregate; diff --git a/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs index 6725bdf..35c1de0 100644 --- a/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs +++ b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs @@ -48,7 +48,7 @@ public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() Result updateRequestType = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest { - RequestType = "SafaricomTopupRequestUpdated" + RequestType = TestData.SafaricomRequestType }); Result updateOperatorName = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest @@ -63,7 +63,7 @@ public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() Result updateFileFormatHandler = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest { - FileFormatHandler = "SafaricomUpdatedFileFormatHandler" + FileFormatHandler = TestData.VoucherFileFormatHandler }); Result duplicateNameCreate = aggregate.CreateProfile(this.CreateProfile( @@ -105,10 +105,10 @@ public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() updatedProfile.ShouldNotBeNull(); updatedProfile.Name.ShouldBe("Safaricom Profile Updated"); updatedProfile.ListeningDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated"); - updatedProfile.RequestType.ShouldBe("SafaricomTopupRequestUpdated"); + updatedProfile.RequestType.ShouldBe(TestData.SafaricomRequestType); updatedProfile.OperatorName.ShouldBe("Safaricom Updated"); updatedProfile.LineTerminator.ShouldBe("\r\n"); - updatedProfile.FileFormatHandler.ShouldBe("SafaricomUpdatedFileFormatHandler"); + updatedProfile.FileFormatHandler.ShouldBe(TestData.VoucherFileFormatHandler); updatedProfile.ProcessedDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated/processed"); updatedProfile.FailedDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated/failed"); @@ -160,7 +160,7 @@ public void FileProfileAggregate_Archive_RetainsHistoricalVersionsButHidesActive FileProcessor.Models.FileProfile originalVersion = aggregate.GetProfile(TestData.FileProfileId); aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest { - FileFormatHandler = "UpdatedHandler" + FileFormatHandler = TestData.VoucherFileFormatHandler }); Result archiveResult = aggregate.ArchiveProfile(TestData.FileProfileId); @@ -293,7 +293,7 @@ public void FileProfileAggregate_Update_CreatesNewVersionAndPreservesPreviousVer Result updateResult = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest { - FileFormatHandler = "UpdatedHandler" + FileFormatHandler = TestData.VoucherFileFormatHandler }); FileProcessor.Models.FileProfile updatedProfile = aggregate.GetProfile(TestData.FileProfileId); @@ -305,7 +305,7 @@ public void FileProfileAggregate_Update_CreatesNewVersionAndPreservesPreviousVer updateResult.IsSuccess.ShouldBeTrue(); updatedProfile.FileProfileVersionId.ShouldNotBe(originalProfile.FileProfileVersionId); updatedProfile.FileProfileVersion.ShouldBe(2); - updatedProfile.FileFormatHandler.ShouldBe("UpdatedHandler"); + updatedProfile.FileFormatHandler.ShouldBe(TestData.VoucherFileFormatHandler); historicalProfile.FileProfileVersionId.ShouldBe(originalProfile.FileProfileVersionId); historicalProfile.FileProfileVersion.ShouldBe(1); historicalProfile.FileFormatHandler.ShouldBe(TestData.SafaricomFileFormatHandler); @@ -434,6 +434,51 @@ public void FileProfileAggregate_Create_WithDuplicateRequestType_IsRejected() duplicateCreate.Status.ShouldBe(ResultStatus.Invalid); } + [Fact] + public void FileProfileAggregate_Create_WithUnsupportedProcessingConfiguration_IsRejected() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + + Result result = aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + "UnsupportedRequestType", + TestData.SafaricomOperatorIdentifier, + LineTerminatorType.LineFeed, + "UnsupportedFileFormatHandler")); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Errors.ShouldContain(error => error.Contains("Unsupported request type")); + result.Errors.ShouldContain(error => error.Contains("Unsupported file format handler")); + } + + [Fact] + public void FileProfileAggregate_Update_WithUnsupportedProcessingConfiguration_IsRejected() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + LineTerminatorType.LineFeed, + TestData.SafaricomFileFormatHandler)); + + Result result = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + RequestType = "UnsupportedRequestType", + FileFormatHandler = "UnsupportedFileFormatHandler" + }); + + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); + result.Errors.ShouldContain(error => error.Contains("Unsupported request type")); + result.Errors.ShouldContain(error => error.Contains("Unsupported file format handler")); + } + private CreateFileProfileRequest CreateProfile(Guid fileProfileId, string name, string listeningDirectory, diff --git a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs index 4e044ad..ec96be7 100644 --- a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs +++ b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs @@ -342,6 +342,13 @@ private static Result ValidateCreateRequest(this FileProfileAggregate aggregate, return requiredFieldsResult; } + Result supportedConfigurationResult = ValidateSupportedConfiguration(request.RequestType, + request.FileFormatHandler); + if (supportedConfigurationResult.IsFailed) + { + return supportedConfigurationResult; + } + return aggregate.ValidateCreateUniqueness(request); } @@ -360,6 +367,13 @@ private static Result ValidateUpdateRequest(this FileProfileAggregate aggregate, return proposedValuesResult; } + Result supportedConfigurationResult = ValidateSupportedConfiguration(request.RequestType, + request.FileFormatHandler); + if (supportedConfigurationResult.IsFailed) + { + return supportedConfigurationResult; + } + return aggregate.ValidateUpdateUniqueness(fileProfileId, currentProfile, request); } @@ -389,6 +403,25 @@ private static Result ValidateRequiredCreateFields(CreateFileProfileRequest requ : Result.Invalid(errors); } + private static Result ValidateSupportedConfiguration(string requestType, string fileFormatHandler) + { + var errors = new List(); + + if (requestType != null && FileProfileConfiguration.IsSupportedRequestType(requestType) == false) + { + errors.Add($"Unsupported request type [{requestType}]"); + } + + if (fileFormatHandler != null && FileProfileConfiguration.IsSupportedFileFormatHandler(fileFormatHandler) == false) + { + errors.Add($"Unsupported file format handler [{fileFormatHandler}]"); + } + + return errors.Count == 0 + ? Result.Success() + : Result.Invalid(errors); + } + private static void AddIfInvalid(string? value, string errorMessage, ICollection errors) { if (string.IsNullOrWhiteSpace(value)) diff --git a/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs b/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs new file mode 100644 index 0000000..a024eea --- /dev/null +++ b/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs @@ -0,0 +1,23 @@ +using System; + +namespace FileProcessor.FileProfileAggregate; + +public static class FileProfileConfiguration +{ + public const string SafaricomTopupRequestType = "SafaricomTopupRequest"; + public const string VoucherRequestType = "VoucherRequest"; + public const string SafaricomFileFormatHandler = "SafaricomFileFormatHandler"; + public const string VoucherFileFormatHandler = "VoucherFileFormatHandler"; + + public static bool IsSupportedRequestType(string requestType) + { + return String.Equals(requestType, SafaricomTopupRequestType, StringComparison.Ordinal) || + String.Equals(requestType, VoucherRequestType, StringComparison.Ordinal); + } + + public static bool IsSupportedFileFormatHandler(string fileFormatHandler) + { + return String.Equals(fileFormatHandler, SafaricomFileFormatHandler, StringComparison.Ordinal) || + String.Equals(fileFormatHandler, VoucherFileFormatHandler, StringComparison.Ordinal); + } +} diff --git a/FileProcessor.IntegrationTests/Features/FileProfile.feature b/FileProcessor.IntegrationTests/Features/FileProfile.feature index 67f78b7..aed4d82 100644 --- a/FileProcessor.IntegrationTests/Features/FileProfile.feature +++ b/FileProcessor.IntegrationTests/Features/FileProfile.feature @@ -22,24 +22,29 @@ Background: Scenario: Create, update and list file profiles When I create the following file profiles | Alias | FileProfileId | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | - | Profile A | 11111111-1111-1111-1111-111111111111 | Profile A | /home/txnproc/bulkfiles/profile-a | ProfileARequest | Operator A | 1 | ProfileAHandler | - | Profile B | 22222222-2222-2222-2222-222222222222 | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | 2 | ProfileBHandler | + | Profile A | 11111111-1111-1111-1111-111111111111 | Profile A | /home/txnproc/bulkfiles/profile-a | SafaricomTopupRequest | Operator A | 1 | SafaricomFileFormatHandler | + | Profile B | 22222222-2222-2222-2222-222222222222 | Profile B | /home/txnproc/bulkfiles/profile-b | VoucherRequest | Operator B | 2 | VoucherFileFormatHandler | And I update the following file profiles | Alias | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | - | Profile A | Profile A Updated | /home/txnproc/bulkfiles/profile-a-updated | ProfileARequestUpdated | Operator A Updated | 2 | ProfileAHandlerUpdated | + | Profile A | Profile A Updated | /home/txnproc/bulkfiles/profile-a-updated | SafaricomTopupRequest | Operator A Updated | 2 | SafaricomFileFormatHandler | Then the file profiles list should contain 2 items And the file profiles should have the following values | Alias | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | - | Profile A | Profile A Updated | /home/txnproc/bulkfiles/profile-a-updated | ProfileARequestUpdated | Operator A Updated | 2 | ProfileAHandlerUpdated | - | Profile B | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | 2 | ProfileBHandler | + | Profile A | Profile A Updated | /home/txnproc/bulkfiles/profile-a-updated | SafaricomTopupRequest | Operator A Updated | 2 | SafaricomFileFormatHandler | + | Profile B | Profile B | /home/txnproc/bulkfiles/profile-b | VoucherRequest | Operator B | 2 | VoucherFileFormatHandler | Scenario: Reject duplicate file profile name and request type When I create the following file profiles | Alias | FileProfileId | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | - | Profile A | 33333333-3333-3333-3333-333333333333 | Profile A | /home/txnproc/bulkfiles/profile-a | ProfileARequest | Operator A | 1 | ProfileAHandler | - | Profile B | 44444444-4444-4444-4444-444444444444 | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | 2 | ProfileBHandler | + | Profile A | 33333333-3333-3333-3333-333333333333 | Profile A | /home/txnproc/bulkfiles/profile-a | SafaricomTopupRequest | Operator A | 1 | SafaricomFileFormatHandler | + | Profile B | 44444444-4444-4444-4444-444444444444 | Profile B | /home/txnproc/bulkfiles/profile-b | VoucherRequest | Operator B | 2 | VoucherFileFormatHandler | And I try to create the following duplicate file profiles | DuplicateType | BasedOn | | Name | Profile A | | RequestType | Profile B | And the file profiles list should contain 2 items + +Scenario: Reject unsupported processing configuration + When I try to create the following invalid file profiles + | Alias | FileProfileId | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | + | Invalid Profile | 55555555-5555-5555-5555-555555555555 | Invalid Profile | /home/txnproc/bulkfiles/invalid | UnsupportedRequestType | Operator | 1 | UnsupportedFileFormatHandler | diff --git a/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs b/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs index 3cce03c..2330048 100644 --- a/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs +++ b/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs @@ -25,7 +25,7 @@ namespace FileProcessor.IntegrationTests.Features [global::NUnit.Framework.CategoryAttribute("fileprofiles")] public partial class FileProfileFeature { - + private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = new string[] { @@ -166,9 +166,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and private static global::Reqnroll.Formatters.RuntimeSupport.FeatureLevelCucumberMessages InitializeCucumberMessages() { - return new global::Reqnroll.Formatters.RuntimeSupport.FeatureLevelCucumberMessages("Features/FileProfile.feature.ndjson", 4); + return new global::Reqnroll.Formatters.RuntimeSupport.FeatureLevelCucumberMessages("Features/FileProfile.feature.ndjson", 5); } - + [global::NUnit.Framework.TestAttribute()] [global::NUnit.Framework.DescriptionAttribute("Create, update and list file profiles")] [global::NUnit.Framework.CategoryAttribute("PRTest")] @@ -208,19 +208,19 @@ await testRunner.GivenAsync("I have a token to access the estate management and "11111111-1111-1111-1111-111111111111", "Profile A", "/home/txnproc/bulkfiles/profile-a", - "ProfileARequest", + "SafaricomTopupRequest", "Operator A", "1", - "ProfileAHandler"}); + "SafaricomFileFormatHandler"}); table5.AddRow(new string[] { "Profile B", "22222222-2222-2222-2222-222222222222", "Profile B", "/home/txnproc/bulkfiles/profile-b", - "ProfileBRequest", + "VoucherRequest", "Operator B", "2", - "ProfileBHandler"}); + "VoucherFileFormatHandler"}); #line 23 await testRunner.WhenAsync("I create the following file profiles", ((string)(null)), table5, "When "); #line hidden @@ -236,10 +236,10 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Profile A", "Profile A Updated", "/home/txnproc/bulkfiles/profile-a-updated", - "ProfileARequestUpdated", + "SafaricomTopupRequest", "Operator A Updated", "2", - "ProfileAHandlerUpdated"}); + "SafaricomFileFormatHandler"}); #line 27 await testRunner.AndAsync("I update the following file profiles", ((string)(null)), table6, "And "); #line hidden @@ -258,25 +258,25 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Profile A", "Profile A Updated", "/home/txnproc/bulkfiles/profile-a-updated", - "ProfileARequestUpdated", + "SafaricomTopupRequest", "Operator A Updated", "2", - "ProfileAHandlerUpdated"}); + "SafaricomFileFormatHandler"}); table7.AddRow(new string[] { "Profile B", "Profile B", "/home/txnproc/bulkfiles/profile-b", - "ProfileBRequest", + "VoucherRequest", "Operator B", "2", - "ProfileBHandler"}); + "VoucherFileFormatHandler"}); #line 31 await testRunner.AndAsync("the file profiles should have the following values", ((string)(null)), table7, "And "); #line hidden } await this.ScenarioCleanupAsync(); } - + [global::NUnit.Framework.TestAttribute()] [global::NUnit.Framework.DescriptionAttribute("Reject duplicate file profile name and request type")] public async global::System.Threading.Tasks.Task RejectDuplicateFileProfileNameAndRequestType() @@ -314,19 +314,19 @@ await testRunner.GivenAsync("I have a token to access the estate management and "33333333-3333-3333-3333-333333333333", "Profile A", "/home/txnproc/bulkfiles/profile-a", - "ProfileARequest", + "SafaricomTopupRequest", "Operator A", "1", - "ProfileAHandler"}); + "SafaricomFileFormatHandler"}); table8.AddRow(new string[] { "Profile B", "44444444-4444-4444-4444-444444444444", "Profile B", "/home/txnproc/bulkfiles/profile-b", - "ProfileBRequest", + "VoucherRequest", "Operator B", "2", - "ProfileBHandler"}); + "VoucherFileFormatHandler"}); #line 37 await testRunner.WhenAsync("I create the following file profiles", ((string)(null)), table8, "When "); #line hidden @@ -344,6 +344,54 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line hidden #line 45 await testRunner.AndAsync("the file profiles list should contain 2 items", ((string)(null)), ((global::Reqnroll.Table)(null)), "And "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + + [global::NUnit.Framework.TestAttribute()] + [global::NUnit.Framework.DescriptionAttribute("Reject unsupported processing configuration")] + public async global::System.Threading.Tasks.Task RejectUnsupportedProcessingConfiguration() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + string pickleIndex = "2"; + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Reject unsupported processing configuration", null, tagsOfScenario, argumentsOfScenario, featureTags, pickleIndex); + string[] tagsOfRule = ((string[])(null)); + global::Reqnroll.RuleInfo ruleInfo = null; +#line 47 +this.ScenarioInitialize(scenarioInfo, ruleInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + await testRunner.SkipScenarioAsync(); + } + else + { + await this.ScenarioStartAsync(); +#line 4 +await this.FeatureBackgroundAsync(); +#line hidden + global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { + "Alias", + "FileProfileId", + "Name", + "ListeningDirectory", + "RequestType", + "OperatorName", + "LineTerminator", + "FileFormatHandler"}); + table10.AddRow(new string[] { + "Invalid Profile", + "55555555-5555-5555-5555-555555555555", + "Invalid Profile", + "/home/txnproc/bulkfiles/invalid", + "UnsupportedRequestType", + "Operator", + "1", + "UnsupportedFileFormatHandler"}); +#line 48 + await testRunner.WhenAsync("I try to create the following invalid file profiles", ((string)(null)), table10, "When "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs index 023014a..d711c9b 100644 --- a/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs +++ b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs @@ -84,6 +84,23 @@ public async Task WhenITryToCreateTheFollowingDuplicateFileProfiles(DataTable ta } } + [When(@"I try to create the following invalid file profiles")] + public async Task WhenITryToCreateTheFollowingInvalidFileProfiles(DataTable table) + { + foreach (DataTableRow row in table.Rows) + { + CreateFileProfileRequest request = this.BuildCreateRequest(row); + Result result = await this.FileProcessorSteps.CreateFileProfile( + this.TestingContext.AccessToken, + request, + CancellationToken.None); + + result.IsFailed.ShouldBeTrue(result.Message); + result.Message.ShouldContain("Unsupported request type"); + result.Message.ShouldContain("Unsupported file format handler"); + } + } + [Then(@"the file profiles list should contain (.*) items")] [When(@"the file profiles list should contain (.*) items")] public async Task ThenTheFileProfilesListShouldContainItems(int expectedCount) diff --git a/FileProcessor.Testing/TestData.cs b/FileProcessor.Testing/TestData.cs index b530864..00a6f42 100644 --- a/FileProcessor.Testing/TestData.cs +++ b/FileProcessor.Testing/TestData.cs @@ -3,6 +3,7 @@ using TransactionProcessor.DataTransferObjects.Responses.Contract; using TransactionProcessor.DataTransferObjects.Responses.Merchant; using TransactionProcessor.DataTransferObjects.Responses.Operator; +using FileProcessor.FileProfileAggregate; namespace FileProcessor.Testing { @@ -156,7 +157,7 @@ public static FileProfile GetFileProfile(String operatorName) public static String SafaricomListeningDirectory = "/home/txnproc/bulkfiles/safaricom"; - public static String SafaricomRequestType = "SafaricomTopupRequest"; + public static String SafaricomRequestType = FileProfileConfiguration.SafaricomTopupRequestType; public static String SafaricomLineTerminator = "\n"; @@ -569,13 +570,13 @@ public static List GetMerchantContractsResponseNoNullValueProd public static String VoucherListeningDirectory = "/home/txnproc/bulkfiles/voucher"; - public static String VoucherRequestType = "VoucherRequest"; + public static String VoucherRequestType = FileProfileConfiguration.VoucherRequestType; public static String VoucherLineTerminator = "\n"; - public static String SafaricomFileFormatHandler = "SafaricomFileFormatHandler"; + public static String SafaricomFileFormatHandler = FileProfileConfiguration.SafaricomFileFormatHandler; - public static String VoucherFileFormatHandler = "VoucherFileFormatHandler"; + public static String VoucherFileFormatHandler = FileProfileConfiguration.VoucherFileFormatHandler; public static DateTime ImportLogStartDate = new DateTime(2021,7,1); public static DateTime ImportLogEndDate = new DateTime(2021,7,2); diff --git a/FileProcessor/Bootstrapper/FileRegistry.cs b/FileProcessor/Bootstrapper/FileRegistry.cs index f2a42ec..6667fc4 100644 --- a/FileProcessor/Bootstrapper/FileRegistry.cs +++ b/FileProcessor/Bootstrapper/FileRegistry.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using BusinessLogic.FileFormatHandlers; +using FileProcessor.FileProfileAggregate; using Lamar; using Microsoft.Extensions.DependencyInjection; @@ -20,9 +21,9 @@ public FileRegistry() this.AddSingleton(); this.AddSingleton>(container => fileFormatHandlerName => { - if (fileFormatHandlerName == "SafaricomFileFormatHandler") + if (fileFormatHandlerName == FileProfileConfiguration.SafaricomFileFormatHandler) return new SafaricomFileFormatHandler(); - if (fileFormatHandlerName == "VoucherFileFormatHandler") + if (fileFormatHandlerName == FileProfileConfiguration.VoucherFileFormatHandler) return new VoucherFileFormatHandler(); return null; From 68eb6d1caa67d6f041f023382f97ea6188dd3a6f Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 16 Oct 2026 19:34:46 +0100 Subject: [PATCH 2/3] Address Codacy constant warnings --- .../FileProfileConfiguration.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs b/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs index a024eea..eaab9a8 100644 --- a/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs +++ b/FileProcessor.FileProfileAggregate/FileProfileConfiguration.cs @@ -4,10 +4,10 @@ namespace FileProcessor.FileProfileAggregate; public static class FileProfileConfiguration { - public const string SafaricomTopupRequestType = "SafaricomTopupRequest"; - public const string VoucherRequestType = "VoucherRequest"; - public const string SafaricomFileFormatHandler = "SafaricomFileFormatHandler"; - public const string VoucherFileFormatHandler = "VoucherFileFormatHandler"; + public static string SafaricomTopupRequestType => "SafaricomTopupRequest"; + public static string VoucherRequestType => "VoucherRequest"; + public static string SafaricomFileFormatHandler => "SafaricomFileFormatHandler"; + public static string VoucherFileFormatHandler => "VoucherFileFormatHandler"; public static bool IsSupportedRequestType(string requestType) { From 6d96eca783bc410f958098fa3891d346840406bb Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 16 Oct 2026 19:36:37 +0100 Subject: [PATCH 3/3] Address PR review feedback --- .../FileProfileAggregateTests.cs | 31 +++++++++++++++---- .../FileProfileAggregate.cs | 7 +++-- .../Features/FileProfileSteps.cs | 17 ++++++++-- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs index 35c1de0..cc6cd58 100644 --- a/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs +++ b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs @@ -70,10 +70,10 @@ public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() Guid.Parse("33333333-3333-3333-3333-333333333333"), "Voucher Profile", "/home/txnproc/bulkfiles/duplicate-name", - "GammaRequest", + TestData.SafaricomRequestType, "Gamma Operator", LineTerminatorType.LineFeed, - "GammaHandler")); + TestData.SafaricomFileFormatHandler)); Result duplicateRequestTypeCreate = aggregate.CreateProfile(this.CreateProfile( Guid.Parse("44444444-4444-4444-4444-444444444444"), @@ -82,7 +82,7 @@ public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() TestData.VoucherRequestType, "Gamma Operator", LineTerminatorType.LineFeed, - "GammaHandler")); + TestData.VoucherFileFormatHandler)); List fileProfiles = aggregate.GetAllProfiles(); FileProcessor.Models.FileProfile updatedProfile = aggregate.GetProfile(TestData.FileProfileId); @@ -359,10 +359,10 @@ public void FileProfileAggregate_Create_WithDuplicateName_IsRejected() TestData.SafaricomFileProfileId, TestData.SafaricomProfileName, "/tmp/other", - "DifferentRequest", + TestData.VoucherRequestType, "Other", LineTerminatorType.LineFeed, - "OtherHandler")); + TestData.VoucherFileFormatHandler)); firstCreate.IsSuccess.ShouldBeTrue(); duplicateCreate.IsFailed.ShouldBeTrue(); @@ -427,7 +427,7 @@ public void FileProfileAggregate_Create_WithDuplicateRequestType_IsRejected() TestData.SafaricomRequestType, "Other", LineTerminatorType.LineFeed, - "OtherHandler")); + TestData.SafaricomFileFormatHandler)); firstCreate.IsSuccess.ShouldBeTrue(); duplicateCreate.IsFailed.ShouldBeTrue(); @@ -454,6 +454,25 @@ public void FileProfileAggregate_Create_WithUnsupportedProcessingConfiguration_I result.Errors.ShouldContain(error => error.Contains("Unsupported file format handler")); } + [Fact] + public void FileProfileAggregate_Create_WithWhitespaceAroundProcessingConfiguration_IsAccepted() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + + Result result = aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + $" {TestData.SafaricomRequestType} ", + TestData.SafaricomOperatorIdentifier, + LineTerminatorType.LineFeed, + $" {TestData.SafaricomFileFormatHandler} ")); + + result.IsSuccess.ShouldBeTrue(); + aggregate.GetProfile(TestData.FileProfileId).RequestType.ShouldBe(TestData.SafaricomRequestType); + aggregate.GetProfile(TestData.FileProfileId).FileFormatHandler.ShouldBe(TestData.SafaricomFileFormatHandler); + } + [Fact] public void FileProfileAggregate_Update_WithUnsupportedProcessingConfiguration_IsRejected() { diff --git a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs index ec96be7..a2d27df 100644 --- a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs +++ b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs @@ -407,12 +407,15 @@ private static Result ValidateSupportedConfiguration(string requestType, string { var errors = new List(); - if (requestType != null && FileProfileConfiguration.IsSupportedRequestType(requestType) == false) + string normalizedRequestType = requestType?.Trim(); + string normalizedFileFormatHandler = fileFormatHandler?.Trim(); + + if (normalizedRequestType != null && FileProfileConfiguration.IsSupportedRequestType(normalizedRequestType) == false) { errors.Add($"Unsupported request type [{requestType}]"); } - if (fileFormatHandler != null && FileProfileConfiguration.IsSupportedFileFormatHandler(fileFormatHandler) == false) + if (normalizedFileFormatHandler != null && FileProfileConfiguration.IsSupportedFileFormatHandler(normalizedFileFormatHandler) == false) { errors.Add($"Unsupported file format handler [{fileFormatHandler}]"); } diff --git a/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs index d711c9b..3ffa40b 100644 --- a/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs +++ b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs @@ -63,10 +63,14 @@ public async Task WhenITryToCreateTheFollowingDuplicateFileProfiles(DataTable ta FileProfileId = Guid.NewGuid(), Name = sourceProfile.Name, ListeningDirectory = $"/tmp/{Guid.NewGuid():N}", - RequestType = $"{sourceProfile.RequestType}-duplicate", + RequestType = sourceProfile.RequestType.Equals("SafaricomTopupRequest", StringComparison.Ordinal) + ? "VoucherRequest" + : "SafaricomTopupRequest", OperatorName = sourceProfile.OperatorName, LineTerminator = ParseLineTerminator(sourceProfile.LineTerminator), - FileFormatHandler = sourceProfile.FileFormatHandler + FileFormatHandler = sourceProfile.FileFormatHandler.Equals("SafaricomFileFormatHandler", StringComparison.Ordinal) + ? "VoucherFileFormatHandler" + : "SafaricomFileFormatHandler" } : new CreateFileProfileRequest { @@ -81,6 +85,15 @@ public async Task WhenITryToCreateTheFollowingDuplicateFileProfiles(DataTable ta Result duplicateResult = await this.FileProcessorSteps.CreateFileProfile(this.TestingContext.AccessToken, request, CancellationToken.None); duplicateResult.IsFailed.ShouldBeTrue(duplicateResult.Message); + + if (duplicateType.Equals("Name", StringComparison.OrdinalIgnoreCase)) + { + duplicateResult.Message.ShouldContain("A file profile with name"); + } + else + { + duplicateResult.Message.ShouldContain("A file profile with request type"); + } } }