From 15fabece5f78d0256f725783b6680ac85f908076 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 10 Jul 2026 16:57:07 +0100 Subject: [PATCH 1/3] file profile management --- .../DummyFileProcessorDomainService.cs | 9 +- .../FileProcessingManagerTests.cs | 28 +- .../GlobalUsings.cs | 1 + .../FileProcessor.BusinessLogic.csproj | 1 + FileProcessor.BusinessLogic/GlobalUsings.cs | 1 + .../Managers/FileProcessorManager.cs | 32 +- .../Managers/FileProfileManager.cs | 182 ++++++++ .../Managers/IFileProcessorManager.cs | 11 +- .../Managers/IFileProfileManager.cs | 22 + .../FileProfileRequestHandler.cs | 50 +++ .../Requests/FileProfileCommands.cs | 19 + .../Requests/FileProfileQueries.cs | 17 + .../Services/FileProcessorDomainService.cs | 23 +- .../FileProcessor.Client.csproj | 2 + FileProcessor.Client/FileProcessorClient.cs | 121 +++++- FileProcessor.Client/IFileProcessorClient.cs | 44 +- .../Requests/CreateFileProfileRequest.cs | 20 + .../Requests/UpdateFileProfileRequest.cs | 16 + ...eProcessor.FileProfile.DomainEvents.csproj | 12 + .../FileProfileDomainEvents.cs | 27 ++ ...rocessor.FileProfileAggregate.Tests.csproj | 28 ++ .../FileProfileAggregateTests.cs | 252 +++++++++++ .../FileProcessor.FileProfileAggregate.csproj | 20 + .../FileProfileAggregate.cs | 403 ++++++++++++++++++ ...rocessor.IntegrationTesting.Helpers.csproj | 2 + .../FileProcessorSteps.cs | 29 +- .../Features/FileProfile.feature | 44 ++ .../Features/FileProfile.feature.cs | 346 +++++++++++++++ .../Features/FileProfileSteps.cs | 170 ++++++++ .../Features/GetFileImportDetails.feature.cs | 236 +++++----- .../Features/ProcessTopupCSV.feature.cs | 242 +++++------ .../Features/ProcessVoucherCSV.feature.cs | 230 +++++----- .../FileProcessor.IntegrationTests.csproj | 1 + FileProcessor.sln | 19 + FileProcessor/Bootstrapper/FileRegistry.cs | 8 +- .../Bootstrapper/MiddlewareRegistry.cs | 1 + .../Bootstrapper/RepositoryRegistry.cs | 6 +- FileProcessor/Common/Extensions.cs | 10 +- .../Endpoints/FileProfileEndpoints.cs | 34 ++ FileProcessor/GlobalUsings.cs | 1 + FileProcessor/Handlers/FileProfileHandlers.cs | 71 +++ FileProcessor/Startup.cs | 1 + 42 files changed, 2360 insertions(+), 432 deletions(-) create mode 100644 FileProcessor.BusinessLogic.Tests/GlobalUsings.cs create mode 100644 FileProcessor.BusinessLogic/GlobalUsings.cs create mode 100644 FileProcessor.BusinessLogic/Managers/FileProfileManager.cs create mode 100644 FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs create mode 100644 FileProcessor.BusinessLogic/RequestHandlers/FileProfileRequestHandler.cs create mode 100644 FileProcessor.BusinessLogic/Requests/FileProfileCommands.cs create mode 100644 FileProcessor.BusinessLogic/Requests/FileProfileQueries.cs create mode 100644 FileProcessor.DataTransferObjects/Requests/CreateFileProfileRequest.cs create mode 100644 FileProcessor.DataTransferObjects/Requests/UpdateFileProfileRequest.cs create mode 100644 FileProcessor.FileProfile.DomainEvents/FileProcessor.FileProfile.DomainEvents.csproj create mode 100644 FileProcessor.FileProfile.DomainEvents/FileProfileDomainEvents.cs create mode 100644 FileProcessor.FileProfileAggregate.Tests/FileProcessor.FileProfileAggregate.Tests.csproj create mode 100644 FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs create mode 100644 FileProcessor.FileProfileAggregate/FileProcessor.FileProfileAggregate.csproj create mode 100644 FileProcessor.FileProfileAggregate/FileProfileAggregate.cs create mode 100644 FileProcessor.IntegrationTests/Features/FileProfile.feature create mode 100644 FileProcessor.IntegrationTests/Features/FileProfile.feature.cs create mode 100644 FileProcessor.IntegrationTests/Features/FileProfileSteps.cs create mode 100644 FileProcessor/Endpoints/FileProfileEndpoints.cs create mode 100644 FileProcessor/GlobalUsings.cs create mode 100644 FileProcessor/Handlers/FileProfileHandlers.cs diff --git a/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs index e161290..6be3735 100644 --- a/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; using FileProcessor.BusinessLogic.Managers; using FileProcessor.DataTransferObjects; -using FileProcessor.Models; +using FileProfileModel = global::FileProcessor.Models.FileProfile; +using FileDetails = global::FileProcessor.Models.FileDetails; using SimpleResults; namespace FileProcessor.BusinessLogic.Tests; @@ -26,11 +27,11 @@ public async Task ProcessTransactionForFileLine(FileCommands.ProcessTran } public class DummyFileProcessorManager : IFileProcessorManager { - public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) { + public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) { return Result.Success(); } - public async Task> GetFileProfile(Guid fileProfileId, + public async Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken) { return Result.Success(); } @@ -55,4 +56,4 @@ public async Task> GetFile(Guid fileId, CancellationToken cancellationToken) { return Result.Success(); } -} \ No newline at end of file +} diff --git a/FileProcessor.BusinessLogic.Tests/FileProcessingManagerTests.cs b/FileProcessor.BusinessLogic.Tests/FileProcessingManagerTests.cs index a869332..7483506 100644 --- a/FileProcessor.BusinessLogic.Tests/FileProcessingManagerTests.cs +++ b/FileProcessor.BusinessLogic.Tests/FileProcessingManagerTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using TransactionProcessor.Database.Contexts; using TransactionProcessor.Database.Entities; +using FileProfileModel = global::FileProcessor.Models.FileProfile; namespace FileProcessor.BusinessLogic.Tests { @@ -33,12 +34,25 @@ namespace FileProcessor.BusinessLogic.Tests using FileLine = FileProcessor.Models.FileLine; public class FileProcessingManagerTests { + private Mock FileProfileManager; private Mock> FileAggregateRepository; private FileProcessorManager Manager; private Mock> DbContextFactory; private EstateManagementContext Context; public FileProcessingManagerTests() { - List fileProfiles = TestData.FileProfiles; + List fileProfiles = TestData.FileProfiles; + this.FileProfileManager = new Mock(); + this.FileProfileManager.Setup(f => f.GetAllFileProfiles(It.IsAny())).ReturnsAsync(Result.Success(fileProfiles)); + this.FileProfileManager.Setup(f => f.GetFileProfile(It.IsAny(), It.IsAny())).ReturnsAsync((Guid fileProfileId, CancellationToken cancellationToken) => + { + FileProfileModel fileProfile = fileProfiles.SingleOrDefault(f => f.FileProfileId == fileProfileId); + if (fileProfile == null) + { + return Result.NotFound($"No file profile found for File Profile Id {fileProfileId}"); + } + + return Result.Success(fileProfile); + }); this.DbContextFactory = new Mock>(); this.Context = this.GetContext(Guid.NewGuid().ToString("N")); ServiceCollection services = new ServiceCollection(); @@ -49,7 +63,7 @@ public FileProcessingManagerTests() { var modelFactory= new Common.ModelFactory(); this.FileAggregateRepository = new Mock>(); - this.Manager = new FileProcessorManager(fileProfiles, this.DbContextFactory.Object, modelFactory, this.FileAggregateRepository.Object); + this.Manager = new FileProcessorManager(this.FileProfileManager.Object, this.DbContextFactory.Object, modelFactory, this.FileAggregateRepository.Object); } private EstateManagementContext GetContext(String databaseName) @@ -62,7 +76,7 @@ private EstateManagementContext GetContext(String databaseName) [Fact] public async Task FileProcessingManager_GetAllFileProfiles_AllFileProfilesReturned() { - Result> allFileProfiles = await this.Manager.GetAllFileProfiles(CancellationToken.None); + Result> allFileProfiles = await this.Manager.GetAllFileProfiles(CancellationToken.None); allFileProfiles.ShouldNotBeNull(); allFileProfiles.IsSuccess.ShouldBeTrue(); allFileProfiles.Data.ShouldNotBeEmpty(); @@ -71,7 +85,7 @@ public async Task FileProcessingManager_GetAllFileProfiles_AllFileProfilesReturn [Fact] public async Task FileProcessingManager_GetFileProfile_FileProfileReturned() { - Result fileProfile = await this.Manager.GetFileProfile(TestData.SafaricomFileProfileId, CancellationToken.None); + Result fileProfile = await this.Manager.GetFileProfile(TestData.SafaricomFileProfileId, CancellationToken.None); fileProfile.ShouldNotBeNull(); fileProfile.IsSuccess.ShouldBeTrue(); fileProfile.Data.FileProfileId.ShouldBe(TestData.SafaricomFileProfileId); @@ -140,7 +154,7 @@ public async Task FileProcessingManager_GetFileImportLog_WithMerchantId_ImportLo [Fact] public async Task FileProcessingManager_GetFile_FileReturned() { - List fileProfiles = new List + List fileProfiles = new List { TestData.FileProfile }; @@ -206,9 +220,9 @@ public async Task FileProcessingManager_GetFile_FileReturnedWithUserEmailAddress [Fact] public async Task FileProcessingManager_GetFile_FileReturnedWithFileProfileName() { - List fileProfiles = new List + List fileProfiles = new List { - new FileProfile(TestData.FileProfileId, + new FileProfileModel(TestData.FileProfileId, TestData.SafaricomProfileName, TestData.SafaricomListeningDirectory, TestData.SafaricomRequestType, diff --git a/FileProcessor.BusinessLogic.Tests/GlobalUsings.cs b/FileProcessor.BusinessLogic.Tests/GlobalUsings.cs new file mode 100644 index 0000000..51067b2 --- /dev/null +++ b/FileProcessor.BusinessLogic.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using FileProfile = global::FileProcessor.Models.FileProfile; diff --git a/FileProcessor.BusinessLogic/FileProcessor.BusinessLogic.csproj b/FileProcessor.BusinessLogic/FileProcessor.BusinessLogic.csproj index ba33f3d..f24e003 100644 --- a/FileProcessor.BusinessLogic/FileProcessor.BusinessLogic.csproj +++ b/FileProcessor.BusinessLogic/FileProcessor.BusinessLogic.csproj @@ -23,6 +23,7 @@ + diff --git a/FileProcessor.BusinessLogic/GlobalUsings.cs b/FileProcessor.BusinessLogic/GlobalUsings.cs new file mode 100644 index 0000000..51067b2 --- /dev/null +++ b/FileProcessor.BusinessLogic/GlobalUsings.cs @@ -0,0 +1 @@ +global using FileProfile = global::FileProcessor.Models.FileProfile; diff --git a/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs b/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs index 44c1b46..b33db6d 100644 --- a/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs +++ b/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs @@ -1,4 +1,4 @@ -using FileProcessor.Models; +using FileProcessor.Models; using Shared.Results; using SimpleResults; using TransactionProcessor.Database.Contexts; @@ -21,6 +21,7 @@ namespace FileProcessor.BusinessLogic.Managers using System.Threading; using System.Threading.Tasks; using FileImportLog = FileProcessor.Models.FileImportLog; + using FileProfileModel = global::FileProcessor.Models.FileProfile; /// /// @@ -29,8 +30,6 @@ namespace FileProcessor.BusinessLogic.Managers public class FileProcessorManager : IFileProcessorManager { #region Fields - private readonly List FileProfiles; - private readonly IDbContextResolver Resolver; private static readonly String EstateManagementDatabaseName = "TransactionProcessorReadModel"; @@ -38,6 +37,8 @@ public class FileProcessorManager : IFileProcessorManager private readonly IAggregateRepository FileAggregateRepository; + private readonly IFileProfileManager FileProfileManager; + #endregion @@ -46,15 +47,15 @@ public class FileProcessorManager : IFileProcessorManager /// /// Initializes a new instance of the class. /// - /// The file profiles. + /// The file profile manager. /// The database context factory. /// The model factory. - public FileProcessorManager(List fileProfiles, + public FileProcessorManager(IFileProfileManager fileProfileManager, IDbContextResolver resolver, IModelFactory modelFactory, IAggregateRepository fileAggregateRepository) { - this.FileProfiles = fileProfiles; + this.FileProfileManager = fileProfileManager; this.Resolver = resolver; this.ModelFactory = modelFactory; this.FileAggregateRepository = fileAggregateRepository; @@ -64,20 +65,15 @@ public FileProcessorManager(List fileProfiles, #region Methods - public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) + public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) { - return Result.Success(this.FileProfiles); + return await this.FileProfileManager.GetAllFileProfiles(cancellationToken); } - public async Task> GetFileProfile(Guid fileProfileId, + public async Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken) { - FileProfile fileProfile = this.FileProfiles.SingleOrDefault(f => f.FileProfileId == fileProfileId); - if (fileProfile == null) - return Result.NotFound($"No file profile found for File Profile Id {fileProfileId}"); - - return Result.Success(fileProfile); - + return await this.FileProfileManager.GetFileProfile(fileProfileId, cancellationToken); } private async Task GetContext(Guid estateId) @@ -201,12 +197,12 @@ public async Task> GetFile(Guid fileId, fileDetails.UserEmailAddress = userDetails.EmailAddress; } - Result getFileProfile = await this.GetFileProfile(fileDetails.FileProfileId, cancellationToken); + Result getFileProfile = await this.GetFileProfile(fileDetails.FileProfileId, cancellationToken); if (getFileProfile.IsFailed) { return ResultHelpers.CreateFailure(getFileProfile); } - FileProfile fileProfile = getFileProfile.Data; + FileProfileModel fileProfile = getFileProfile.Data; if (fileProfile != null) { fileDetails.FileProfileName = fileProfile.Name; @@ -217,4 +213,4 @@ public async Task> GetFile(Guid fileId, #endregion } -} \ No newline at end of file +} diff --git a/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs b/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs new file mode 100644 index 0000000..c7d875a --- /dev/null +++ b/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FileProcessor.DataTransferObjects.Requests; +using FileProcessor.Models; +using FileProcessor.FileProfileAggregate; +using Shared.DomainDrivenDesign.EventSourcing; +using Shared.EventStore.Aggregate; +using Shared.Results; +using SimpleResults; +using FileProfileAggregateRoot = FileProcessor.FileProfileAggregate.FileProfileAggregate; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.BusinessLogic.Managers; + +public class FileProfileManager : IFileProfileManager +{ + private readonly List SeedProfiles; + + private readonly IAggregateRepository FileProfileAggregateRepository; + + public FileProfileManager(List seedProfiles, + IAggregateRepository fileProfileAggregateRepository) + { + this.SeedProfiles = seedProfiles; + this.FileProfileAggregateRepository = fileProfileAggregateRepository; + } + + public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + return Result.Success(aggregateResult.Data.GetAllProfiles()); + } + + public async Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + FileProfileModel fileProfile = aggregateResult.Data.GetProfile(fileProfileId); + if (fileProfile == null) + { + return Result.NotFound($"No file profile found for File Profile Id {fileProfileId}"); + } + + return Result.Success(fileProfile); + } + + public async Task> CreateFileProfile(CreateFileProfileRequest request, CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + Result createResult = aggregateResult.Data.CreateProfile(request); + if (createResult.IsFailed) + { + return ResultHelpers.CreateFailure(createResult); + } + + Result saveResult = await this.FileProfileAggregateRepository.SaveChanges(aggregateResult.Data, cancellationToken); + if (saveResult.IsFailed) + { + return ResultHelpers.CreateFailure(saveResult); + } + + Result fileProfileResult = await this.GetFileProfile(request.FileProfileId, cancellationToken); + return fileProfileResult; + } + + public async Task> UpdateFileProfile(Guid fileProfileId, UpdateFileProfileRequest request, CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + Result updateResult = aggregateResult.Data.UpdateProfile(fileProfileId, request); + if (updateResult.IsFailed) + { + return ResultHelpers.CreateFailure(updateResult); + } + + Result saveResult = await this.FileProfileAggregateRepository.SaveChanges(aggregateResult.Data, cancellationToken); + if (saveResult.IsFailed) + { + return ResultHelpers.CreateFailure(saveResult); + } + + Result fileProfileResult = await this.GetFileProfile(fileProfileId, cancellationToken); + return fileProfileResult; + } + + public async Task ArchiveFileProfile(Guid fileProfileId, CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + Result archiveResult = aggregateResult.Data.ArchiveProfile(fileProfileId); + if (archiveResult.IsFailed) + { + return ResultHelpers.CreateFailure(archiveResult); + } + + return await this.FileProfileAggregateRepository.SaveChanges(aggregateResult.Data, cancellationToken); + } + + private async Task> LoadAggregate(CancellationToken cancellationToken, bool seedIfMissing) + { + Result aggregateResult = await this.FileProfileAggregateRepository.GetLatestVersion(FileProfileAggregateRoot.FileProfileCollectionId, cancellationToken); + if (aggregateResult.IsSuccess) + { + return aggregateResult; + } + + if (aggregateResult.Status != ResultStatus.NotFound) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + if (seedIfMissing && this.SeedProfiles.Any()) + { + Result seeded = this.SeedAggregate(aggregate); + if (seeded.IsFailed) + { + return seeded; + } + + Result saveResult = await this.FileProfileAggregateRepository.SaveChanges(seeded.Data, cancellationToken); + if (saveResult.IsFailed) + { + return ResultHelpers.CreateFailure(saveResult); + } + + return Result.Success(seeded.Data); + } + + return Result.Success(aggregate); + } + + private Result SeedAggregate(FileProfileAggregateRoot aggregate) + { + foreach (FileProfileModel fileProfile in this.SeedProfiles) + { + CreateFileProfileRequest request = new CreateFileProfileRequest + { + FileProfileId = fileProfile.FileProfileId, + Name = fileProfile.Name, + ListeningDirectory = fileProfile.ListeningDirectory, + RequestType = fileProfile.RequestType, + OperatorName = fileProfile.OperatorName, + LineTerminator = fileProfile.LineTerminator, + FileFormatHandler = fileProfile.FileFormatHandler + }; + + Result createResult = aggregate.CreateProfile(request); + if (createResult.IsFailed) + { + return ResultHelpers.CreateFailure(createResult); + } + } + + return Result.Success(aggregate); + } +} diff --git a/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs b/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs index cefa0e8..dad8041 100644 --- a/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs +++ b/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs @@ -1,4 +1,4 @@ -using FileProcessor.Models; +using FileProcessor.Models; using SimpleResults; namespace FileProcessor.BusinessLogic.Managers @@ -7,7 +7,8 @@ namespace FileProcessor.BusinessLogic.Managers using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - + using FileProfileModel = global::FileProcessor.Models.FileProfile; + /// /// /// @@ -15,9 +16,9 @@ public interface IFileProcessorManager { #region Methods - Task>> GetAllFileProfiles(CancellationToken cancellationToken); + Task>> GetAllFileProfiles(CancellationToken cancellationToken); - Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken); + Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken); Task>> GetFileImportLogs(Guid estateId, DateTime startDateTime, DateTime endDateTime, Guid? merchantId, CancellationToken cancellationToken); @@ -27,4 +28,4 @@ public interface IFileProcessorManager #endregion } -} \ No newline at end of file +} diff --git a/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs b/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs new file mode 100644 index 0000000..a5ff4ce --- /dev/null +++ b/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FileProcessor.DataTransferObjects.Requests; +using SimpleResults; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.BusinessLogic.Managers; + +public interface IFileProfileManager +{ + Task>> GetAllFileProfiles(CancellationToken cancellationToken); + + Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken); + + Task> CreateFileProfile(CreateFileProfileRequest request, CancellationToken cancellationToken); + + Task> UpdateFileProfile(Guid fileProfileId, UpdateFileProfileRequest request, CancellationToken cancellationToken); + + Task ArchiveFileProfile(Guid fileProfileId, CancellationToken cancellationToken); +} diff --git a/FileProcessor.BusinessLogic/RequestHandlers/FileProfileRequestHandler.cs b/FileProcessor.BusinessLogic/RequestHandlers/FileProfileRequestHandler.cs new file mode 100644 index 0000000..5b1cc29 --- /dev/null +++ b/FileProcessor.BusinessLogic/RequestHandlers/FileProfileRequestHandler.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FileProcessor.BusinessLogic.Managers; +using FileProcessor.BusinessLogic.Requests; +using FileProcessor.Models; +using MediatR; +using SimpleResults; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.BusinessLogic.RequestHandlers; + +public class FileProfileRequestHandler : IRequestHandler>, + IRequestHandler>, + IRequestHandler, + IRequestHandler>>, + IRequestHandler> +{ + private readonly IFileProfileManager FileProfileManager; + + public FileProfileRequestHandler(IFileProfileManager fileProfileManager) + { + this.FileProfileManager = fileProfileManager; + } + + public Task> Handle(FileProfileCommands.CreateFileProfileCommand request, CancellationToken cancellationToken) + { + return this.FileProfileManager.CreateFileProfile(request.Request, cancellationToken); + } + + public Task> Handle(FileProfileCommands.UpdateFileProfileCommand request, CancellationToken cancellationToken) + { + return this.FileProfileManager.UpdateFileProfile(request.FileProfileId, request.Request, cancellationToken); + } + + public Task Handle(FileProfileCommands.ArchiveFileProfileCommand request, CancellationToken cancellationToken) + { + return this.FileProfileManager.ArchiveFileProfile(request.FileProfileId, cancellationToken); + } + + public Task>> Handle(FileProfileQueries.GetFileProfilesQuery request, CancellationToken cancellationToken) + { + return this.FileProfileManager.GetAllFileProfiles(cancellationToken); + } + + public Task> Handle(FileProfileQueries.GetFileProfileQuery request, CancellationToken cancellationToken) + { + return this.FileProfileManager.GetFileProfile(request.FileProfileId, cancellationToken); + } +} diff --git a/FileProcessor.BusinessLogic/Requests/FileProfileCommands.cs b/FileProcessor.BusinessLogic/Requests/FileProfileCommands.cs new file mode 100644 index 0000000..65e0f4a --- /dev/null +++ b/FileProcessor.BusinessLogic/Requests/FileProfileCommands.cs @@ -0,0 +1,19 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using FileProcessor.DataTransferObjects.Requests; +using FileProcessor.Models; +using MediatR; +using SimpleResults; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.BusinessLogic.Requests; + +[ExcludeFromCodeCoverage] +public record FileProfileCommands +{ + public record CreateFileProfileCommand(CreateFileProfileRequest Request) : IRequest>; + + public record UpdateFileProfileCommand(Guid FileProfileId, UpdateFileProfileRequest Request) : IRequest>; + + public record ArchiveFileProfileCommand(Guid FileProfileId) : IRequest; +} diff --git a/FileProcessor.BusinessLogic/Requests/FileProfileQueries.cs b/FileProcessor.BusinessLogic/Requests/FileProfileQueries.cs new file mode 100644 index 0000000..0bdcd54 --- /dev/null +++ b/FileProcessor.BusinessLogic/Requests/FileProfileQueries.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using FileProcessor.Models; +using MediatR; +using SimpleResults; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.BusinessLogic.Requests; + +[ExcludeFromCodeCoverage] +public record FileProfileQueries +{ + public record GetFileProfilesQuery : IRequest>>; + + public record GetFileProfileQuery(Guid FileProfileId) : IRequest>; +} diff --git a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs index e26bf24..a0d9b86 100644 --- a/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic/Services/FileProcessorDomainService.cs @@ -35,6 +35,7 @@ namespace FileProcessor.BusinessLogic.Services; using TransactionProcessor.DataTransferObjects; using FileDetails = Models.FileDetails; using FileLine = Models.FileLine; +using FileProfileModel = global::FileProcessor.Models.FileProfile; public interface IFileProcessorDomainService { @@ -159,12 +160,12 @@ public async Task UploadFile(FileCommands.UploadFileCommand command, } // Move the file - Result getFileProfileResult = await this.FileProcessorManager.GetFileProfile(command.FileProfileId, cancellationToken); + Result getFileProfileResult = await this.FileProcessorManager.GetFileProfile(command.FileProfileId, cancellationToken); if (getFileProfileResult.IsFailed) { return ResultHelpers.CreateFailure(getFileProfileResult); } - FileProfile fileProfile = getFileProfileResult.Data; + FileProfileModel fileProfile = getFileProfileResult.Data; if (fileProfile == null) { return Result.NotFound($"No file profile found with Id {command.FileProfileId}"); } @@ -269,13 +270,13 @@ public async Task ProcessUploadedFile(FileCommands.ProcessUploadedFileCo private async Task> GetOperatorIdForFileProfile(Guid estateId, Guid fileProfileId, CancellationToken cancellationToken){ - Result fileProfileResult = await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); + Result fileProfileResult = await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); if (fileProfileResult.IsFailed){ Logger.LogInformation($"file profile {fileProfileId} not found"); return ResultHelpers.CreateFailure(fileProfileResult); } - FileProfile fileProfile = fileProfileResult.Data; + FileProfileModel fileProfile = fileProfileResult.Data; Result getTokenResult = await this.GetToken(cancellationToken); if (getTokenResult.IsFailed) { @@ -316,12 +317,12 @@ public async Task ProcessTransactionForFileLine(FileCommands.ProcessTran return Result.Success(); } - Result fileProfileResult = await this.FileProcessorManager.GetFileProfile(fileDetails.FileProfileId, cancellationToken); + Result fileProfileResult = await this.FileProcessorManager.GetFileProfile(fileDetails.FileProfileId, cancellationToken); if (fileProfileResult.IsFailed) return ResultHelpers.CreateFailure(fileProfileResult); - FileProfile fileProfile = fileProfileResult.Data; + FileProfileModel fileProfile = fileProfileResult.Data; Result stateResult; @@ -369,7 +370,7 @@ public async Task ProcessTransactionForFileLine(FileCommands.ProcessTran private Result> BuildTransactionMetadata(FileCommands.ProcessTransactionForFileLineCommand command, FileLine fileLine, - FileProfile fileProfile, + FileProfileModel fileProfile, out String operatorName) { // need to now parse the line (based on the file format), this builds the metadata Dictionary transactionMetadata = this.ParseFileLine(fileLine.LineData, fileProfile.FileFormatHandler); @@ -431,7 +432,7 @@ private async Task> SendSaleTransaction(FileDeta private async Task> GetDetailsForTransaction(CancellationToken cancellationToken, FileDetails fileDetails, - FileProfile fileProfile, + FileProfileModel fileProfile, String operatorName) { Result getTokenResult = await this.GetToken(cancellationToken); @@ -484,9 +485,9 @@ private async Task ProcessFile(Guid fileId, String fileName, CancellationToken cancellationToken) { IFileInfo inProgressFile = null; - FileProfile fileProfile = null; + FileProfileModel fileProfile = null; - Result fileProfileResult = + Result fileProfileResult = await this.FileProcessorManager.GetFileProfile(fileProfileId, cancellationToken); if (fileProfileResult.IsFailed) @@ -634,4 +635,4 @@ private async Task> GetToken(CancellationToken cancellatio return this.TokenResponse; } -} \ No newline at end of file +} diff --git a/FileProcessor.Client/FileProcessor.Client.csproj b/FileProcessor.Client/FileProcessor.Client.csproj index 07bb79d..7611e8b 100644 --- a/FileProcessor.Client/FileProcessor.Client.csproj +++ b/FileProcessor.Client/FileProcessor.Client.csproj @@ -12,11 +12,13 @@ + + diff --git a/FileProcessor.Client/FileProcessorClient.cs b/FileProcessor.Client/FileProcessorClient.cs index 9855bb2..3ac4d6d 100644 --- a/FileProcessor.Client/FileProcessorClient.cs +++ b/FileProcessor.Client/FileProcessorClient.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Security.Cryptography; +using System.Net.Http.Headers; using System.Text; using Shared.Results; @@ -11,7 +12,11 @@ namespace FileProcessor.Client { using System.Threading.Tasks; using ClientProxyBase; using DataTransferObjects; + using DataTransferObjects.Requests; + using ApiFileDetails = DataTransferObjects.Responses.FileDetails; + using ApiFileImportLog = DataTransferObjects.Responses.FileImportLog; using DataTransferObjects.Responses; + using FileProcessor.Models; using SimpleResults; /// @@ -27,6 +32,12 @@ public class FileProcessorClient : ClientProxyBase, IFileProcessorClient { /// private readonly Func BaseAddressResolver; + private readonly HttpClient HttpClient; + + private readonly Func Serialise; + + private readonly Func Deserialise; + #endregion #region Constructors @@ -42,12 +53,62 @@ public FileProcessorClient(Func baseAddressResolver, Func deserialise) : base(httpClient, serialise, deserialise) { this.BaseAddressResolver = baseAddressResolver; + this.HttpClient = httpClient; + this.Serialise = serialise; + this.Deserialise = deserialise; } #endregion #region Methods + public async Task>> GetFileProfiles(String accessToken, + CancellationToken cancellationToken) + { + String requestUri = this.BuildRequestUrl("/api/file-profiles"); + return await this.SendRequest>(HttpMethod.Get, requestUri, accessToken, null, cancellationToken); + } + + public async Task> GetFileProfile(String accessToken, + Guid fileProfileId, + CancellationToken cancellationToken) + { + String requestUri = this.BuildRequestUrl($"/api/file-profiles/{fileProfileId}"); + return await this.SendRequest(HttpMethod.Get, requestUri, accessToken, null, cancellationToken); + } + + public async Task> CreateFileProfile(String accessToken, + CreateFileProfileRequest request, + CancellationToken cancellationToken) + { + String requestUri = this.BuildRequestUrl("/api/file-profiles"); + return await this.SendRequest(HttpMethod.Post, requestUri, accessToken, request, cancellationToken); + } + + public async Task> UpdateFileProfile(String accessToken, + Guid fileProfileId, + UpdateFileProfileRequest request, + CancellationToken cancellationToken) + { + String requestUri = this.BuildRequestUrl($"/api/file-profiles/{fileProfileId}"); + return await this.SendRequest(HttpMethod.Patch, requestUri, accessToken, request, cancellationToken); + } + + public async Task ArchiveFileProfile(String accessToken, + Guid fileProfileId, + CancellationToken cancellationToken) + { + String requestUri = this.BuildRequestUrl($"/api/file-profiles/{fileProfileId}"); + Result result = await this.SendRequest(HttpMethod.Delete, requestUri, accessToken, null, cancellationToken); + + if (result.IsFailed) + { + return ResultHelpers.CreateFailure(result); + } + + return Result.Success(); + } + /// /// Gets the file. /// @@ -56,14 +117,14 @@ public FileProcessorClient(Func baseAddressResolver, /// The file identifier. /// The cancellation token. /// - public async Task> GetFile(String accessToken, - Guid estateId, - Guid fileId, - CancellationToken cancellationToken) { + public async Task> GetFile(String accessToken, + Guid estateId, + Guid fileId, + CancellationToken cancellationToken) { String requestUri = this.BuildRequestUrl($"/api/files/{fileId}?estateId={estateId}"); try { - Result result = await this.Get(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -87,11 +148,11 @@ public async Task> GetFile(String accessToken, /// The merchant identifier. /// The cancellation token. /// - public async Task> GetFileImportLog(String accessToken, - Guid fileImportLogId, - Guid estateId, - Guid? merchantId, - CancellationToken cancellationToken) { + public async Task> GetFileImportLog(String accessToken, + Guid fileImportLogId, + Guid estateId, + Guid? merchantId, + CancellationToken cancellationToken) { String requestUri = this.BuildRequestUrl($"/api/fileImportLogs/{fileImportLogId}?estateId={estateId}"); if (merchantId.HasValue) { @@ -99,7 +160,7 @@ public async Task> GetFileImportLog(String accessToken, } try { - Result result = await this.Get(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -222,6 +283,42 @@ private String BuildRequestUrl(String route) { return requestUri; } + private async Task> SendRequest(HttpMethod method, + String requestUri, + String accessToken, + Object body, + CancellationToken cancellationToken) + { + using HttpRequestMessage request = new(method, requestUri); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + + if (body != null) + { + String payload = this.Serialise(body); + request.Content = new StringContent(payload, Encoding.UTF8, "application/json"); + } + + HttpResponseMessage response = await this.HttpClient.SendAsync(request, cancellationToken); + String responseContent = response.Content == null ? null : await response.Content.ReadAsStringAsync(cancellationToken); + + if (response.IsSuccessStatusCode == false) + { + String message = String.IsNullOrWhiteSpace(responseContent) + ? $"Request failed with status code {(Int32)response.StatusCode}" + : responseContent; + + return Result.Failure(message); + } + + if (String.IsNullOrWhiteSpace(responseContent)) + { + return Result.Success(default(T)); + } + + Object deserialised = this.Deserialise(responseContent, typeof(T)); + return Result.Success((T)deserialised); + } + #endregion } -} \ No newline at end of file +} diff --git a/FileProcessor.Client/IFileProcessorClient.cs b/FileProcessor.Client/IFileProcessorClient.cs index 555df46..3d9b516 100644 --- a/FileProcessor.Client/IFileProcessorClient.cs +++ b/FileProcessor.Client/IFileProcessorClient.cs @@ -3,10 +3,14 @@ namespace FileProcessor.Client { using System; + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using DataTransferObjects; + using ApiFileDetails = DataTransferObjects.Responses.FileDetails; + using ApiFileImportLog = DataTransferObjects.Responses.FileImportLog; using DataTransferObjects.Responses; + using FileProcessor.Models; /// /// @@ -15,6 +19,26 @@ public interface IFileProcessorClient { #region Methods + Task>> GetFileProfiles(String accessToken, + CancellationToken cancellationToken); + + Task> GetFileProfile(String accessToken, + Guid fileProfileId, + CancellationToken cancellationToken); + + Task> CreateFileProfile(String accessToken, + DataTransferObjects.Requests.CreateFileProfileRequest request, + CancellationToken cancellationToken); + + Task> UpdateFileProfile(String accessToken, + Guid fileProfileId, + DataTransferObjects.Requests.UpdateFileProfileRequest request, + CancellationToken cancellationToken); + + Task ArchiveFileProfile(String accessToken, + Guid fileProfileId, + CancellationToken cancellationToken); + /// /// Gets the file. /// @@ -23,10 +47,10 @@ public interface IFileProcessorClient /// The file identifier. /// The cancellation token. /// - Task> GetFile(String accessToken, - Guid estateId, - Guid fileId, - CancellationToken cancellationToken); + Task> GetFile(String accessToken, + Guid estateId, + Guid fileId, + CancellationToken cancellationToken); /// /// Gets the file import log. @@ -37,11 +61,11 @@ Task> GetFile(String accessToken, /// The merchant identifier. /// The cancellation token. /// - Task> GetFileImportLog(String accessToken, - Guid fileImportLogId, - Guid estateId, - Guid? merchantId, - CancellationToken cancellationToken); + Task> GetFileImportLog(String accessToken, + Guid fileImportLogId, + Guid estateId, + Guid? merchantId, + CancellationToken cancellationToken); /// /// Gets the file import logs. @@ -77,4 +101,4 @@ Task> UploadFile(String accessToken, #endregion } -} \ No newline at end of file +} diff --git a/FileProcessor.DataTransferObjects/Requests/CreateFileProfileRequest.cs b/FileProcessor.DataTransferObjects/Requests/CreateFileProfileRequest.cs new file mode 100644 index 0000000..2cfe059 --- /dev/null +++ b/FileProcessor.DataTransferObjects/Requests/CreateFileProfileRequest.cs @@ -0,0 +1,20 @@ +using System; + +namespace FileProcessor.DataTransferObjects.Requests; + +public class CreateFileProfileRequest +{ + public Guid FileProfileId { get; set; } + + public string Name { get; set; } + + public string ListeningDirectory { get; set; } + + public string RequestType { get; set; } + + public string OperatorName { get; set; } + + public string LineTerminator { get; set; } + + public string FileFormatHandler { get; set; } +} diff --git a/FileProcessor.DataTransferObjects/Requests/UpdateFileProfileRequest.cs b/FileProcessor.DataTransferObjects/Requests/UpdateFileProfileRequest.cs new file mode 100644 index 0000000..a7c2905 --- /dev/null +++ b/FileProcessor.DataTransferObjects/Requests/UpdateFileProfileRequest.cs @@ -0,0 +1,16 @@ +namespace FileProcessor.DataTransferObjects.Requests; + +public class UpdateFileProfileRequest +{ + public string Name { get; set; } + + public string ListeningDirectory { get; set; } + + public string RequestType { get; set; } + + public string OperatorName { get; set; } + + public string LineTerminator { get; set; } + + public string FileFormatHandler { get; set; } +} diff --git a/FileProcessor.FileProfile.DomainEvents/FileProcessor.FileProfile.DomainEvents.csproj b/FileProcessor.FileProfile.DomainEvents/FileProcessor.FileProfile.DomainEvents.csproj new file mode 100644 index 0000000..ebe960f --- /dev/null +++ b/FileProcessor.FileProfile.DomainEvents/FileProcessor.FileProfile.DomainEvents.csproj @@ -0,0 +1,12 @@ + + + + net10.0 + None + + + + + + + diff --git a/FileProcessor.FileProfile.DomainEvents/FileProfileDomainEvents.cs b/FileProcessor.FileProfile.DomainEvents/FileProfileDomainEvents.cs new file mode 100644 index 0000000..917e6c7 --- /dev/null +++ b/FileProcessor.FileProfile.DomainEvents/FileProfileDomainEvents.cs @@ -0,0 +1,27 @@ +using System; +using Shared.DomainDrivenDesign.EventSourcing; + +namespace FileProcessor.FileProfile.DomainEvents; + +public record FileProfileCreatedEvent(Guid FileProfileCollectionId, + Guid FileProfileId, + String Name, + String ListeningDirectory, + String RequestType, + String OperatorName, + String LineTerminator, + String FileFormatHandler) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileNameUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String Name) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileListeningDirectoryUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String ListeningDirectory) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileRequestTypeUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String RequestType) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileOperatorNameUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String OperatorName) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileLineTerminatorUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String LineTerminator) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileFileFormatHandlerUpdatedEvent(Guid FileProfileCollectionId, Guid FileProfileId, String FileFormatHandler) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); + +public record FileProfileArchivedEvent(Guid FileProfileCollectionId, Guid FileProfileId) : DomainEvent(FileProfileCollectionId, Guid.NewGuid()); diff --git a/FileProcessor.FileProfileAggregate.Tests/FileProcessor.FileProfileAggregate.Tests.csproj b/FileProcessor.FileProfileAggregate.Tests/FileProcessor.FileProfileAggregate.Tests.csproj new file mode 100644 index 0000000..ea93824 --- /dev/null +++ b/FileProcessor.FileProfileAggregate.Tests/FileProcessor.FileProfileAggregate.Tests.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + None + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs new file mode 100644 index 0000000..0220604 --- /dev/null +++ b/FileProcessor.FileProfileAggregate.Tests/FileProfileAggregateTests.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FileProcessor.DataTransferObjects.Requests; +using FileProcessor.FileProfileAggregate; +using FileProcessor.Testing; +using Shouldly; +using SimpleResults; +using Xunit; +using FileProfileAggregateRoot = FileProcessor.FileProfileAggregate.FileProfileAggregate; + +namespace FileProcessor.FileProfileAggregate.Tests; + +public class FileProfileAggregateTests +{ + [Fact] + public void FileProfileAggregate_CreateUpdateAndUniquenessFlow_WorksAsExpected() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + + Result firstCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + TestData.SafaricomLineTerminator, + TestData.SafaricomFileFormatHandler)); + + Result secondCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.SafaricomFileProfileId, + "Voucher Profile", + "/home/txnproc/bulkfiles/voucher", + TestData.VoucherRequestType, + TestData.VoucherOperatorIdentifier, + "\r\n", + TestData.VoucherFileFormatHandler)); + + Result updateName = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + Name = "Safaricom Profile Updated" + }); + + Result updateListeningDirectory = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + ListeningDirectory = "/home/txnproc/bulkfiles/safaricom-updated" + }); + + Result updateRequestType = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + RequestType = "SafaricomTopupRequestUpdated" + }); + + Result updateOperatorName = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + OperatorName = "Safaricom Updated" + }); + + Result updateLineTerminator = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + LineTerminator = "\r\n" + }); + + Result updateFileFormatHandler = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + FileFormatHandler = "SafaricomUpdatedFileFormatHandler" + }); + + Result duplicateNameCreate = aggregate.CreateProfile(this.CreateProfile( + Guid.Parse("33333333-3333-3333-3333-333333333333"), + "Voucher Profile", + "/home/txnproc/bulkfiles/duplicate-name", + "GammaRequest", + "Gamma Operator", + "\n", + "GammaHandler")); + + Result duplicateRequestTypeCreate = aggregate.CreateProfile(this.CreateProfile( + Guid.Parse("44444444-4444-4444-4444-444444444444"), + "Gamma Profile", + "/home/txnproc/bulkfiles/duplicate-request", + TestData.VoucherRequestType, + "Gamma Operator", + "\n", + "GammaHandler")); + + List fileProfiles = aggregate.GetAllProfiles(); + FileProcessor.Models.FileProfile updatedProfile = aggregate.GetProfile(TestData.FileProfileId); + FileProcessor.Models.FileProfile secondProfile = aggregate.GetProfile(TestData.SafaricomFileProfileId); + + firstCreate.IsSuccess.ShouldBeTrue(); + secondCreate.IsSuccess.ShouldBeTrue(); + updateName.IsSuccess.ShouldBeTrue(); + updateListeningDirectory.IsSuccess.ShouldBeTrue(); + updateRequestType.IsSuccess.ShouldBeTrue(); + updateOperatorName.IsSuccess.ShouldBeTrue(); + updateLineTerminator.IsSuccess.ShouldBeTrue(); + updateFileFormatHandler.IsSuccess.ShouldBeTrue(); + duplicateNameCreate.IsFailed.ShouldBeTrue(); + duplicateRequestTypeCreate.IsFailed.ShouldBeTrue(); + fileProfiles.Count.ShouldBe(2); + fileProfiles.Select(profile => profile.Name).ShouldContain("Safaricom Profile Updated"); + fileProfiles.Select(profile => profile.Name).ShouldContain("Voucher Profile"); + + updatedProfile.ShouldNotBeNull(); + updatedProfile.Name.ShouldBe("Safaricom Profile Updated"); + updatedProfile.ListeningDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated"); + updatedProfile.RequestType.ShouldBe("SafaricomTopupRequestUpdated"); + updatedProfile.OperatorName.ShouldBe("Safaricom Updated"); + updatedProfile.LineTerminator.ShouldBe("\r\n"); + updatedProfile.FileFormatHandler.ShouldBe("SafaricomUpdatedFileFormatHandler"); + updatedProfile.ProcessedDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated/processed"); + updatedProfile.FailedDirectory.ShouldBe("/home/txnproc/bulkfiles/safaricom-updated/failed"); + + secondProfile.ShouldNotBeNull(); + secondProfile.Name.ShouldBe("Voucher Profile"); + secondProfile.RequestType.ShouldBe(TestData.VoucherRequestType); + secondProfile.ListeningDirectory.ShouldBe("/home/txnproc/bulkfiles/voucher"); + } + + [Fact] + public void FileProfileAggregate_Update_WithNoEffectiveChanges_DoesNotChangeAppliedEventCount() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + TestData.SafaricomLineTerminator, + TestData.SafaricomFileFormatHandler)); + + int eventCountBefore = aggregate.AppliedEventCount; + + Result updateResult = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + Name = TestData.SafaricomProfileName, + RequestType = TestData.SafaricomRequestType, + ListeningDirectory = TestData.SafaricomListeningDirectory, + OperatorName = TestData.SafaricomOperatorIdentifier, + LineTerminator = TestData.SafaricomLineTerminator, + FileFormatHandler = TestData.SafaricomFileFormatHandler + }); + + updateResult.IsSuccess.ShouldBeTrue(); + aggregate.AppliedEventCount.ShouldBe(eventCountBefore); + } + + [Fact] + public void FileProfileAggregate_Update_ListeningDirectoryUpdatesDerivedDirectories() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + TestData.SafaricomLineTerminator, + TestData.SafaricomFileFormatHandler)); + + Result updateResult = aggregate.UpdateProfile(TestData.FileProfileId, new UpdateFileProfileRequest + { + ListeningDirectory = "/home/txnproc/bulkfiles/new-safaricom" + }); + + FileProcessor.Models.FileProfile updatedProfile = aggregate.GetProfile(TestData.FileProfileId); + + updateResult.IsSuccess.ShouldBeTrue(); + updatedProfile.ShouldNotBeNull(); + updatedProfile.ListeningDirectory.ShouldBe("/home/txnproc/bulkfiles/new-safaricom"); + updatedProfile.ProcessedDirectory.ShouldBe("/home/txnproc/bulkfiles/new-safaricom/processed"); + updatedProfile.FailedDirectory.ShouldBe("/home/txnproc/bulkfiles/new-safaricom/failed"); + } + + [Fact] + public void FileProfileAggregate_Create_WithDuplicateName_IsRejected() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + + Result firstCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + TestData.SafaricomLineTerminator, + TestData.SafaricomFileFormatHandler)); + + Result duplicateCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.SafaricomFileProfileId, + TestData.SafaricomProfileName, + "/tmp/other", + "DifferentRequest", + "Other", + TestData.SafaricomLineTerminator, + "OtherHandler")); + + firstCreate.IsSuccess.ShouldBeTrue(); + duplicateCreate.IsFailed.ShouldBeTrue(); + duplicateCreate.Status.ShouldBe(ResultStatus.Invalid); + } + + [Fact] + public void FileProfileAggregate_Create_WithDuplicateRequestType_IsRejected() + { + FileProfileAggregateRoot aggregate = FileProfileAggregateRoot.Create(); + + Result firstCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.FileProfileId, + TestData.SafaricomProfileName, + TestData.SafaricomListeningDirectory, + TestData.SafaricomRequestType, + TestData.SafaricomOperatorIdentifier, + TestData.SafaricomLineTerminator, + TestData.SafaricomFileFormatHandler)); + + Result duplicateCreate = aggregate.CreateProfile(this.CreateProfile( + TestData.SafaricomFileProfileId, + "Different Name", + "/tmp/other", + TestData.SafaricomRequestType, + "Other", + TestData.SafaricomLineTerminator, + "OtherHandler")); + + firstCreate.IsSuccess.ShouldBeTrue(); + duplicateCreate.IsFailed.ShouldBeTrue(); + duplicateCreate.Status.ShouldBe(ResultStatus.Invalid); + } + + private CreateFileProfileRequest CreateProfile(Guid fileProfileId, + string name, + string listeningDirectory, + string requestType, + string operatorName, + string lineTerminator, + string fileFormatHandler) + { + return new CreateFileProfileRequest + { + FileProfileId = fileProfileId, + Name = name, + ListeningDirectory = listeningDirectory, + RequestType = requestType, + OperatorName = operatorName, + LineTerminator = lineTerminator, + FileFormatHandler = fileFormatHandler + }; + } +} diff --git a/FileProcessor.FileProfileAggregate/FileProcessor.FileProfileAggregate.csproj b/FileProcessor.FileProfileAggregate/FileProcessor.FileProfileAggregate.csproj new file mode 100644 index 0000000..5a1d65a --- /dev/null +++ b/FileProcessor.FileProfileAggregate/FileProcessor.FileProfileAggregate.csproj @@ -0,0 +1,20 @@ + + + + net10.0 + + + + + + + + + + + + + + + + diff --git a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs new file mode 100644 index 0000000..e946762 --- /dev/null +++ b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using FileProcessor.DataTransferObjects.Requests; +using FileProcessor.Models; +using FileProcessor.FileProfile.DomainEvents; +using Shared.DomainDrivenDesign.EventSourcing; +using Shared.EventStore.Aggregate; +using Shared.General; +using SimpleResults; +using FileProfileModel = FileProcessor.Models.FileProfile; + +namespace FileProcessor.FileProfileAggregate; + +public static class FileProfileAggregateExtensions +{ + private static readonly StringComparer Comparer = StringComparer.OrdinalIgnoreCase; + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileCreatedEvent domainEvent) + { + aggregate.IsCreated = true; + aggregate.FileProfiles[domainEvent.FileProfileId] = new FileProfileState + { + FileProfileId = domainEvent.FileProfileId, + Name = domainEvent.Name, + ListeningDirectory = domainEvent.ListeningDirectory, + RequestType = domainEvent.RequestType, + OperatorName = domainEvent.OperatorName, + LineTerminator = domainEvent.LineTerminator, + FileFormatHandler = domainEvent.FileFormatHandler, + IsArchived = false + }; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileNameUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.Name = domainEvent.Name; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileListeningDirectoryUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.ListeningDirectory = domainEvent.ListeningDirectory; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileRequestTypeUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.RequestType = domainEvent.RequestType; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileOperatorNameUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.OperatorName = domainEvent.OperatorName; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileLineTerminatorUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.LineTerminator = domainEvent.LineTerminator; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileFileFormatHandlerUpdatedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.FileFormatHandler = domainEvent.FileFormatHandler; + aggregate.AppliedEventCount++; + } + + public static void PlayEvent(this FileProfileAggregate aggregate, FileProfileArchivedEvent domainEvent) + { + FileProfileState fileProfile = aggregate.GetRequiredProfileState(domainEvent.FileProfileId); + fileProfile.IsArchived = true; + aggregate.AppliedEventCount++; + } + + public static Result CreateProfile(this FileProfileAggregate aggregate, CreateFileProfileRequest request) + { + Result validationResult = aggregate.ValidateCreateRequest(request); + if (validationResult.IsFailed) + { + return validationResult; + } + + if (aggregate.FileProfiles.TryGetValue(request.FileProfileId, out FileProfileState existingProfile)) + { + if (aggregate.IsSameProfile(existingProfile, request)) + { + return Result.Success(); + } + + return Result.Invalid($"File profile [{request.FileProfileId}] already exists"); + } + + FileProfileCreatedEvent fileProfileCreatedEvent = new(aggregate.AggregateId, + request.FileProfileId, + request.Name.Trim(), + request.ListeningDirectory.Trim(), + request.RequestType.Trim(), + request.OperatorName.Trim(), + request.LineTerminator, + request.FileFormatHandler.Trim()); + + aggregate.ApplyAndAppend(fileProfileCreatedEvent); + return Result.Success(); + } + + public static Result UpdateProfile(this FileProfileAggregate aggregate, Guid fileProfileId, UpdateFileProfileRequest request) + { + if (aggregate.FileProfiles.TryGetValue(fileProfileId, out FileProfileState profile) == false || profile.IsArchived) + { + return Result.NotFound($"File profile with Id [{fileProfileId}] not found"); + } + + Result validationResult = aggregate.ValidateUpdateRequest(fileProfileId, request); + if (validationResult.IsFailed) + { + return validationResult; + } + + if (request.Name != null && Comparer.Equals(profile.Name, request.Name.Trim()) == false) + { + aggregate.ApplyAndAppend(new FileProfileNameUpdatedEvent(aggregate.AggregateId, fileProfileId, request.Name.Trim())); + } + + if (request.ListeningDirectory != null && Comparer.Equals(profile.ListeningDirectory, request.ListeningDirectory.Trim()) == false) + { + aggregate.ApplyAndAppend(new FileProfileListeningDirectoryUpdatedEvent(aggregate.AggregateId, fileProfileId, request.ListeningDirectory.Trim())); + } + + if (request.RequestType != null && Comparer.Equals(profile.RequestType, request.RequestType.Trim()) == false) + { + aggregate.ApplyAndAppend(new FileProfileRequestTypeUpdatedEvent(aggregate.AggregateId, fileProfileId, request.RequestType.Trim())); + } + + if (request.OperatorName != null && Comparer.Equals(profile.OperatorName, request.OperatorName.Trim()) == false) + { + aggregate.ApplyAndAppend(new FileProfileOperatorNameUpdatedEvent(aggregate.AggregateId, fileProfileId, request.OperatorName.Trim())); + } + + if (request.LineTerminator != null && Comparer.Equals(profile.LineTerminator, request.LineTerminator) == false) + { + aggregate.ApplyAndAppend(new FileProfileLineTerminatorUpdatedEvent(aggregate.AggregateId, fileProfileId, request.LineTerminator)); + } + + if (request.FileFormatHandler != null && Comparer.Equals(profile.FileFormatHandler, request.FileFormatHandler.Trim()) == false) + { + aggregate.ApplyAndAppend(new FileProfileFileFormatHandlerUpdatedEvent(aggregate.AggregateId, fileProfileId, request.FileFormatHandler.Trim())); + } + + return Result.Success(); + } + + public static Result ArchiveProfile(this FileProfileAggregate aggregate, Guid fileProfileId) + { + if (aggregate.FileProfiles.TryGetValue(fileProfileId, out FileProfileState profile) == false) + { + return Result.NotFound($"File profile with Id [{fileProfileId}] not found"); + } + + if (profile.IsArchived) + { + return Result.Success(); + } + + aggregate.ApplyAndAppend(new FileProfileArchivedEvent(aggregate.AggregateId, fileProfileId)); + return Result.Success(); + } + + public static List GetAllProfiles(this FileProfileAggregate aggregate) + { + return aggregate.FileProfiles.Values + .Where(fileProfile => fileProfile.IsArchived == false) + .Select(fileProfile => fileProfile.ToModel()) + .OrderBy(fileProfile => fileProfile.Name) + .ToList(); + } + + public static FileProfileModel GetProfile(this FileProfileAggregate aggregate, Guid fileProfileId) + { + if (aggregate.FileProfiles.TryGetValue(fileProfileId, out FileProfileState profile) == false || profile.IsArchived) + { + return null; + } + + return profile.ToModel(); + } + + private static Result ValidateCreateRequest(this FileProfileAggregate aggregate, CreateFileProfileRequest request) + { + if (request == null) + { + return Result.Invalid("No file profile data provided"); + } + + if (request.FileProfileId == Guid.Empty) + { + return Result.Invalid("No file profile Id provided"); + } + + if (string.IsNullOrWhiteSpace(request.Name)) + { + return Result.Invalid("No file profile name provided"); + } + + if (string.IsNullOrWhiteSpace(request.ListeningDirectory)) + { + return Result.Invalid("No listening directory provided"); + } + + if (string.IsNullOrWhiteSpace(request.RequestType)) + { + return Result.Invalid("No request type provided"); + } + + if (string.IsNullOrWhiteSpace(request.OperatorName)) + { + return Result.Invalid("No operator name provided"); + } + + if (string.IsNullOrEmpty(request.LineTerminator)) + { + return Result.Invalid("No line terminator provided"); + } + + if (string.IsNullOrWhiteSpace(request.FileFormatHandler)) + { + return Result.Invalid("No file format handler provided"); + } + + if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.Name, request.Name.Trim()))) + { + return Result.Invalid($"A file profile with name [{request.Name}] already exists"); + } + + if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.RequestType, request.RequestType.Trim()))) + { + return Result.Invalid($"A file profile with request type [{request.RequestType}] already exists"); + } + + return Result.Success(); + } + + private static Result ValidateUpdateRequest(this FileProfileAggregate aggregate, Guid fileProfileId, UpdateFileProfileRequest request) + { + if (request == null) + { + return Result.Invalid("No file profile data provided"); + } + + FileProfileState currentProfile = aggregate.GetRequiredProfileState(fileProfileId); + + String proposedName = request.Name?.Trim(); + String proposedListeningDirectory = request.ListeningDirectory?.Trim(); + String proposedRequestType = request.RequestType?.Trim(); + String proposedOperatorName = request.OperatorName?.Trim(); + String proposedLineTerminator = request.LineTerminator; + String proposedFileFormatHandler = request.FileFormatHandler?.Trim(); + + if (request.Name != null && string.IsNullOrWhiteSpace(proposedName)) + { + return Result.Invalid("No file profile name provided"); + } + + if (request.ListeningDirectory != null && string.IsNullOrWhiteSpace(proposedListeningDirectory)) + { + return Result.Invalid("No listening directory provided"); + } + + if (request.RequestType != null && string.IsNullOrWhiteSpace(proposedRequestType)) + { + return Result.Invalid("No request type provided"); + } + + if (request.OperatorName != null && string.IsNullOrWhiteSpace(proposedOperatorName)) + { + return Result.Invalid("No operator name provided"); + } + + if (request.LineTerminator != null && string.IsNullOrEmpty(proposedLineTerminator)) + { + return Result.Invalid("No line terminator provided"); + } + + if (request.FileFormatHandler != null && string.IsNullOrWhiteSpace(proposedFileFormatHandler)) + { + return Result.Invalid("No file format handler provided"); + } + + if (request.Name != null && + Comparer.Equals(currentProfile.Name, proposedName) == false && + aggregate.FileProfiles.Values.Any(profile => profile.FileProfileId != fileProfileId && profile.IsArchived == false && Comparer.Equals(profile.Name, proposedName))) + { + return Result.Invalid($"A file profile with name [{request.Name}] already exists"); + } + + if (request.RequestType != null && + Comparer.Equals(currentProfile.RequestType, proposedRequestType) == false && + aggregate.FileProfiles.Values.Any(profile => profile.FileProfileId != fileProfileId && profile.IsArchived == false && Comparer.Equals(profile.RequestType, proposedRequestType))) + { + return Result.Invalid($"A file profile with request type [{request.RequestType}] already exists"); + } + + return Result.Success(); + } + + private static Boolean IsSameProfile(this FileProfileAggregate aggregate, FileProfileState profile, CreateFileProfileRequest request) + { + return Comparer.Equals(profile.Name, request.Name.Trim()) && + Comparer.Equals(profile.ListeningDirectory, request.ListeningDirectory.Trim()) && + Comparer.Equals(profile.RequestType, request.RequestType.Trim()) && + Comparer.Equals(profile.OperatorName, request.OperatorName.Trim()) && + Comparer.Equals(profile.LineTerminator, request.LineTerminator) && + Comparer.Equals(profile.FileFormatHandler, request.FileFormatHandler.Trim()) && + profile.IsArchived == false; + } + + private static FileProfileState GetRequiredProfileState(this FileProfileAggregate aggregate, Guid fileProfileId) + { + if (aggregate.FileProfiles.TryGetValue(fileProfileId, out FileProfileState fileProfile) == false || fileProfile.IsArchived) + { + throw new InvalidOperationException($"File profile with Id [{fileProfileId}] not found"); + } + + return fileProfile; + } + + private static FileProfileModel ToModel(this FileProfileState fileProfile) + { + return new FileProfileModel(fileProfile.FileProfileId, + fileProfile.Name, + fileProfile.ListeningDirectory, + fileProfile.RequestType, + fileProfile.OperatorName, + fileProfile.LineTerminator, + fileProfile.FileFormatHandler); + } +} + +public record FileProfileAggregate : Aggregate +{ + public static readonly Guid FileProfileCollectionId = Guid.Parse("2E8EAF39-3F51-4C10-9B8B-0D9DE3F80110"); + + internal readonly Dictionary FileProfiles; + + public bool IsCreated { get; internal set; } + + public int AppliedEventCount { get; internal set; } + + [ExcludeFromCodeCoverage] + public FileProfileAggregate() + { + this.FileProfiles = new Dictionary(); + } + + private FileProfileAggregate(Guid aggregateId) + { + this.AggregateId = aggregateId; + this.FileProfiles = new Dictionary(); + } + + public static FileProfileAggregate Create() + { + return new FileProfileAggregate(FileProfileCollectionId); + } + + protected override object GetMetadata() + { + return null; + } + + public override void PlayEvent(IDomainEvent domainEvent) => FileProfileAggregateExtensions.PlayEvent(this, (dynamic)domainEvent); + + public int ActiveProfileCount => this.FileProfiles.Values.Count(profile => profile.IsArchived == false); +} + +internal sealed class FileProfileState +{ + public Guid FileProfileId { get; set; } + + public string Name { get; set; } + + public string ListeningDirectory { get; set; } + + public string RequestType { get; set; } + + public string OperatorName { get; set; } + + public string LineTerminator { get; set; } + + public string FileFormatHandler { get; set; } + + public bool IsArchived { get; set; } +} diff --git a/FileProcessor.IntegrationTesting.Helpers/FileProcessor.IntegrationTesting.Helpers.csproj b/FileProcessor.IntegrationTesting.Helpers/FileProcessor.IntegrationTesting.Helpers.csproj index c7f0870..67db3c2 100644 --- a/FileProcessor.IntegrationTesting.Helpers/FileProcessor.IntegrationTesting.Helpers.csproj +++ b/FileProcessor.IntegrationTesting.Helpers/FileProcessor.IntegrationTesting.Helpers.csproj @@ -13,12 +13,14 @@ + + diff --git a/FileProcessor.IntegrationTesting.Helpers/FileProcessorSteps.cs b/FileProcessor.IntegrationTesting.Helpers/FileProcessorSteps.cs index 6617cfd..4a21409 100644 --- a/FileProcessor.IntegrationTesting.Helpers/FileProcessorSteps.cs +++ b/FileProcessor.IntegrationTesting.Helpers/FileProcessorSteps.cs @@ -4,9 +4,11 @@ namespace FileProcessor.IntegrationTesting.Helpers.FileProcessor.IntegrationTest using Client; using DataTransferObjects; +using DataTransferObjects.Requests; using DataTransferObjects.Responses; using Shared.IntegrationTesting; using Shouldly; +using FileProfileModel = global::FileProcessor.Models.FileProfile; public class FileProcessorSteps { @@ -55,6 +57,31 @@ await Retry.For(async () => return fileId; } + public async Task>> GetFileProfiles(String accessToken, CancellationToken cancellationToken) + { + return await this.FileProcessorClient.GetFileProfiles(accessToken, cancellationToken); + } + + public async Task> GetFileProfile(String accessToken, Guid fileProfileId, CancellationToken cancellationToken) + { + return await this.FileProcessorClient.GetFileProfile(accessToken, fileProfileId, cancellationToken); + } + + public async Task> CreateFileProfile(String accessToken, CreateFileProfileRequest request, CancellationToken cancellationToken) + { + return await this.FileProcessorClient.CreateFileProfile(accessToken, request, cancellationToken); + } + + public async Task> UpdateFileProfile(String accessToken, Guid fileProfileId, UpdateFileProfileRequest request, CancellationToken cancellationToken) + { + return await this.FileProcessorClient.UpdateFileProfile(accessToken, fileProfileId, request, cancellationToken); + } + + public async Task ArchiveFileProfile(String accessToken, Guid fileProfileId, CancellationToken cancellationToken) + { + return await this.FileProcessorClient.ArchiveFileProfile(accessToken, fileProfileId, cancellationToken); + } + public async Task GivenIUploadThisFileForProcessingAnErrorShouldBeReturnedIndicatingTheFileIsADuplicate(String accessToken, String filePath, Byte[] fileData, @@ -198,4 +225,4 @@ await Retry.For(async () => { TimeSpan.FromMinutes(4), TimeSpan.FromSeconds(30)); } -} \ No newline at end of file +} diff --git a/FileProcessor.IntegrationTests/Features/FileProfile.feature b/FileProcessor.IntegrationTests/Features/FileProfile.feature new file mode 100644 index 0000000..ab717b0 --- /dev/null +++ b/FileProcessor.IntegrationTests/Features/FileProfile.feature @@ -0,0 +1,44 @@ +@base @shared @fileprofiles +Feature: FileProfile + +Background: + Given I create the following api scopes + | Name | DisplayName | Description | + | fileProcessor | File Processor REST Scope | A scope for File Processor REST | + + Given the following api resources exist + | Name | DisplayName | Secret | Scopes | UserClaims | + | fileProcessor | File Processor REST | Secret1 | fileProcessor | | + + Given the following clients exist + | ClientId | ClientName | Secret | Scopes | GrantTypes | + | serviceClient | Service Client | Secret1 | fileProcessor | client_credentials | + + Given I have a token to access the estate management and transaction processor resources + | ClientId | + | serviceClient | + +Scenario: Create, update and list file profiles + When I create the following file profiles + | Alias | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | + | Profile A | Profile A | /home/txnproc/bulkfiles/profile-a | ProfileARequest | Operator A | \n | ProfileAHandler | + | Profile B | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | \r\n | ProfileBHandler | + 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 | \r\n | ProfileAHandlerUpdated | + 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 | \r\n | ProfileAHandlerUpdated | + | Profile B | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | \r\n | ProfileBHandler | + +Scenario: Reject duplicate file profile name and request type + When I create the following file profiles + | Alias | Name | ListeningDirectory | RequestType | OperatorName | LineTerminator | FileFormatHandler | + | Profile A | Profile A | /home/txnproc/bulkfiles/profile-a | ProfileARequest | Operator A | \n | ProfileAHandler | + | Profile B | Profile B | /home/txnproc/bulkfiles/profile-b | ProfileBRequest | Operator B | \r\n | ProfileBHandler | + 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 diff --git a/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs b/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs new file mode 100644 index 0000000..9264caa --- /dev/null +++ b/FileProcessor.IntegrationTests/Features/FileProfile.feature.cs @@ -0,0 +1,346 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://reqnroll.net/). +// Reqnroll Version:3.0.0.0 +// Reqnroll Generator Version:3.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace FileProcessor.IntegrationTests.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "3.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::NUnit.Framework.TestFixtureAttribute()] + [global::NUnit.Framework.DescriptionAttribute("FileProfile")] + [global::NUnit.Framework.FixtureLifeCycleAttribute(global::NUnit.Framework.LifeCycle.InstancePerTestCase)] + [global::NUnit.Framework.CategoryAttribute("base")] + [global::NUnit.Framework.CategoryAttribute("shared")] + [global::NUnit.Framework.CategoryAttribute("fileprofiles")] + public partial class FileProfileFeature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = new string[] { + "base", + "shared", + "fileprofiles"}; + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "FileProfile", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags, InitializeCucumberMessages()); + +#line 1 "FileProfile.feature" +#line hidden + + [global::NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [global::NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + await global::Reqnroll.TestRunnerManager.ReleaseFeatureAsync(featureInfo); + } + + [global::NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [global::NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, global::Reqnroll.RuleInfo ruleInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo, ruleInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(global::NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 4 +#line hidden + global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { + "Name", + "DisplayName", + "Description"}); + table1.AddRow(new string[] { + "fileProcessor", + "File Processor REST Scope", + "A scope for File Processor REST"}); +#line 5 + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table1, "Given "); +#line hidden + global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { + "Name", + "DisplayName", + "Secret", + "Scopes", + "UserClaims"}); + table2.AddRow(new string[] { + "fileProcessor", + "File Processor REST", + "Secret1", + "fileProcessor", + ""}); +#line 9 + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table2, "Given "); +#line hidden + global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { + "ClientId", + "ClientName", + "Secret", + "Scopes", + "GrantTypes"}); + table3.AddRow(new string[] { + "serviceClient", + "Service Client", + "Secret1", + "fileProcessor", + "client_credentials"}); +#line 13 + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table3, "Given "); +#line hidden + global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { + "ClientId"}); + table4.AddRow(new string[] { + "serviceClient"}); +#line 17 + await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + + "s", ((string)(null)), table4, "Given "); +#line hidden + } + + private static global::Reqnroll.Formatters.RuntimeSupport.FeatureLevelCucumberMessages InitializeCucumberMessages() + { + return new global::Reqnroll.Formatters.RuntimeSupport.FeatureLevelCucumberMessages("Features/FileProfile.feature.ndjson", 4); + } + + [global::NUnit.Framework.TestAttribute()] + [global::NUnit.Framework.DescriptionAttribute("Create, update and list file profiles")] + public async global::System.Threading.Tasks.Task CreateUpdateAndListFileProfiles() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + string pickleIndex = "0"; + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create, update and list file profiles", null, tagsOfScenario, argumentsOfScenario, featureTags, pickleIndex); + string[] tagsOfRule = ((string[])(null)); + global::Reqnroll.RuleInfo ruleInfo = null; +#line 21 +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 table5 = new global::Reqnroll.Table(new string[] { + "Alias", + "Name", + "ListeningDirectory", + "RequestType", + "OperatorName", + "LineTerminator", + "FileFormatHandler"}); + table5.AddRow(new string[] { + "Profile A", + "Profile A", + "/home/txnproc/bulkfiles/profile-a", + "ProfileARequest", + "Operator A", + "\n", + "ProfileAHandler"}); + table5.AddRow(new string[] { + "Profile B", + "Profile B", + "/home/txnproc/bulkfiles/profile-b", + "ProfileBRequest", + "Operator B", + "\\r\n", + "ProfileBHandler"}); +#line 22 + await testRunner.WhenAsync("I create the following file profiles", ((string)(null)), table5, "When "); +#line hidden + global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { + "Alias", + "Name", + "ListeningDirectory", + "RequestType", + "OperatorName", + "LineTerminator", + "FileFormatHandler"}); + table6.AddRow(new string[] { + "Profile A", + "Profile A Updated", + "/home/txnproc/bulkfiles/profile-a-updated", + "ProfileARequestUpdated", + "Operator A Updated", + "\\r\n", + "ProfileAHandlerUpdated"}); +#line 26 + await testRunner.AndAsync("I update the following file profiles", ((string)(null)), table6, "And "); +#line hidden +#line 29 + await testRunner.ThenAsync("the file profiles list should contain 2 items", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { + "Alias", + "Name", + "ListeningDirectory", + "RequestType", + "OperatorName", + "LineTerminator", + "FileFormatHandler"}); + table7.AddRow(new string[] { + "Profile A", + "Profile A Updated", + "/home/txnproc/bulkfiles/profile-a-updated", + "ProfileARequestUpdated", + "Operator A Updated", + "\\r\n", + "ProfileAHandlerUpdated"}); + table7.AddRow(new string[] { + "Profile B", + "Profile B", + "/home/txnproc/bulkfiles/profile-b", + "ProfileBRequest", + "Operator B", + "\\r\n", + "ProfileBHandler"}); +#line 30 + 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() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + string pickleIndex = "1"; + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Reject duplicate file profile name and request type", null, tagsOfScenario, argumentsOfScenario, featureTags, pickleIndex); + string[] tagsOfRule = ((string[])(null)); + global::Reqnroll.RuleInfo ruleInfo = null; +#line 35 +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 table8 = new global::Reqnroll.Table(new string[] { + "Alias", + "Name", + "ListeningDirectory", + "RequestType", + "OperatorName", + "LineTerminator", + "FileFormatHandler"}); + table8.AddRow(new string[] { + "Profile A", + "Profile A", + "/home/txnproc/bulkfiles/profile-a", + "ProfileARequest", + "Operator A", + "\n", + "ProfileAHandler"}); + table8.AddRow(new string[] { + "Profile B", + "Profile B", + "/home/txnproc/bulkfiles/profile-b", + "ProfileBRequest", + "Operator B", + "\\r\n", + "ProfileBHandler"}); +#line 36 + await testRunner.WhenAsync("I create the following file profiles", ((string)(null)), table8, "When "); +#line hidden + global::Reqnroll.Table table9 = new global::Reqnroll.Table(new string[] { + "DuplicateType", + "BasedOn"}); + table9.AddRow(new string[] { + "Name", + "Profile A"}); + table9.AddRow(new string[] { + "RequestType", + "Profile B"}); +#line 40 + await testRunner.AndAsync("I try to create the following duplicate file profiles", ((string)(null)), table9, "And "); +#line hidden +#line 44 + await testRunner.AndAsync("the file profiles list should contain 2 items", ((string)(null)), ((global::Reqnroll.Table)(null)), "And "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs new file mode 100644 index 0000000..5e1893b --- /dev/null +++ b/FileProcessor.IntegrationTests/Features/FileProfileSteps.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FileProcessor.DataTransferObjects.Requests; +using FileProcessor.IntegrationTests.Common; +using FileProcessor.Models; +using FileProcessorStepsHelper = FileProcessor.IntegrationTesting.Helpers.FileProcessor.IntegrationTests.Steps.FileProcessorSteps; +using Reqnroll; +using Shouldly; +using SimpleResults; + +namespace FileProcessor.IntegrationTests.Features; + +[Binding] +[Scope(Tag = "fileprofiles")] +public class FileProfileSteps +{ + private readonly TestingContext TestingContext; + private readonly FileProcessorStepsHelper FileProcessorSteps; + private readonly Dictionary FileProfileIds = new(StringComparer.OrdinalIgnoreCase); + + public FileProfileSteps(TestingContext testingContext) + { + this.TestingContext = testingContext; + this.FileProcessorSteps = new FileProcessorStepsHelper(testingContext.DockerHelper.FileProcessorClient); + } + + [When(@"I create the following file profiles")] + public async Task WhenICreateTheFollowingFileProfiles(DataTable table) + { + foreach (DataTableRow row in table.Rows) + { + string alias = row["Alias"]; + CreateFileProfileRequest request = this.BuildCreateRequest(row); + Result createResult = await this.FileProcessorSteps.CreateFileProfile(this.TestingContext.AccessToken, request, CancellationToken.None); + + createResult.IsSuccess.ShouldBeTrue(createResult.Message); + createResult.Data.ShouldNotBeNull(); + + this.FileProfileIds[alias] = createResult.Data.FileProfileId; + } + } + + [When(@"I update the following file profiles")] + public async Task WhenIUpdateTheFollowingFileProfiles(DataTable table) + { + foreach (DataTableRow row in table.Rows) + { + string alias = row["Alias"]; + UpdateFileProfileRequest request = this.BuildUpdateRequest(row); + await this.UpdateProfile(alias, request); + } + } + + [When(@"I try to create the following duplicate file profiles")] + public async Task WhenITryToCreateTheFollowingDuplicateFileProfiles(DataTable table) + { + foreach (DataTableRow row in table.Rows) + { + string duplicateType = row["DuplicateType"]; + string basedOn = row["BasedOn"]; + FileProfile sourceProfile = await this.GetProfileByAlias(basedOn); + + CreateFileProfileRequest request = duplicateType.Equals("Name", StringComparison.OrdinalIgnoreCase) + ? new CreateFileProfileRequest + { + FileProfileId = Guid.NewGuid(), + Name = sourceProfile.Name, + ListeningDirectory = $"/tmp/{Guid.NewGuid():N}", + RequestType = $"{sourceProfile.RequestType}-duplicate", + OperatorName = sourceProfile.OperatorName, + LineTerminator = sourceProfile.LineTerminator, + FileFormatHandler = sourceProfile.FileFormatHandler + } + : new CreateFileProfileRequest + { + FileProfileId = Guid.NewGuid(), + Name = $"{sourceProfile.Name}-duplicate", + ListeningDirectory = $"/tmp/{Guid.NewGuid():N}", + RequestType = sourceProfile.RequestType, + OperatorName = sourceProfile.OperatorName, + LineTerminator = sourceProfile.LineTerminator, + FileFormatHandler = sourceProfile.FileFormatHandler + }; + + Result duplicateResult = await this.FileProcessorSteps.CreateFileProfile(this.TestingContext.AccessToken, request, CancellationToken.None); + duplicateResult.IsFailed.ShouldBeTrue(duplicateResult.Message); + } + } + + [Then(@"the file profiles list should contain (.*) items")] + public async Task ThenTheFileProfilesListShouldContainItems(int expectedCount) + { + Result> fileProfilesResult = await this.FileProcessorSteps.GetFileProfiles(this.TestingContext.AccessToken, CancellationToken.None); + + fileProfilesResult.IsSuccess.ShouldBeTrue(fileProfilesResult.Message); + fileProfilesResult.Data.ShouldNotBeNull(); + fileProfilesResult.Data.Count.ShouldBe(expectedCount); + } + + [Then(@"the file profiles should have the following values")] + public async Task ThenTheFileProfilesShouldHaveTheFollowingValues(DataTable table) + { + Result> fileProfilesResult = await this.FileProcessorSteps.GetFileProfiles(this.TestingContext.AccessToken, CancellationToken.None); + fileProfilesResult.IsSuccess.ShouldBeTrue(fileProfilesResult.Message); + fileProfilesResult.Data.ShouldNotBeNull(); + + foreach (DataTableRow row in table.Rows) + { + Guid fileProfileId = this.FileProfileIds[row["Alias"]]; + FileProfile fileProfile = fileProfilesResult.Data.SingleOrDefault(profile => profile.FileProfileId == fileProfileId); + + fileProfile.ShouldNotBeNull(); + fileProfile.Name.ShouldBe(row["Name"]); + fileProfile.ListeningDirectory.ShouldBe(row["ListeningDirectory"]); + fileProfile.RequestType.ShouldBe(row["RequestType"]); + fileProfile.OperatorName.ShouldBe(row["OperatorName"]); + fileProfile.LineTerminator.ShouldBe(row["LineTerminator"]); + fileProfile.FileFormatHandler.ShouldBe(row["FileFormatHandler"]); + } + } + + private CreateFileProfileRequest BuildCreateRequest(DataTableRow row) + { + return new CreateFileProfileRequest + { + FileProfileId = Guid.NewGuid(), + Name = row["Name"], + ListeningDirectory = row["ListeningDirectory"], + RequestType = row["RequestType"], + OperatorName = row["OperatorName"], + LineTerminator = row["LineTerminator"], + FileFormatHandler = row["FileFormatHandler"] + }; + } + + private UpdateFileProfileRequest BuildUpdateRequest(DataTableRow row) + { + return new UpdateFileProfileRequest + { + Name = row["Name"], + ListeningDirectory = row["ListeningDirectory"], + RequestType = row["RequestType"], + OperatorName = row["OperatorName"], + LineTerminator = row["LineTerminator"], + FileFormatHandler = row["FileFormatHandler"] + }; + } + + private async Task UpdateProfile(string alias, UpdateFileProfileRequest request) + { + Guid fileProfileId = this.FileProfileIds[alias]; + Result updateResult = await this.FileProcessorSteps.UpdateFileProfile(this.TestingContext.AccessToken, fileProfileId, request, CancellationToken.None); + + updateResult.IsSuccess.ShouldBeTrue(updateResult.Message); + updateResult.Data.ShouldNotBeNull(); + } + + private async Task GetProfileByAlias(string alias) + { + Guid fileProfileId = this.FileProfileIds[alias]; + Result result = await this.FileProcessorSteps.GetFileProfile(this.TestingContext.AccessToken, fileProfileId, CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(result.Message); + result.Data.ShouldNotBeNull(); + return result.Data; + } +} diff --git a/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs b/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs index 5d57128..f10b6aa 100644 --- a/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs +++ b/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs @@ -113,118 +113,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table1.AddRow(new string[] { + table11.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table1.AddRow(new string[] { + table11.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table1, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table11, "Given "); #line hidden - global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table2.AddRow(new string[] { + table12.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table2.AddRow(new string[] { + table12.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table2, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table12, "Given "); #line hidden - global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table3.AddRow(new string[] { + table13.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table3, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table13, "Given "); #line hidden - global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table4.AddRow(new string[] { + table14.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table4, "Given "); + "s", ((string)(null)), table14, "Given "); #line hidden - global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table5.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table5, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table15, "Given "); #line hidden - global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table6.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table6.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table6, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table16, "Given "); #line hidden - global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table7.AddRow(new string[] { + table17.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table7.AddRow(new string[] { + table17.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table7, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table17, "And "); #line hidden - global::Reqnroll.Table table8 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table8.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table8.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table8, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table18, "Given "); #line hidden - global::Reqnroll.Table table9 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -232,7 +232,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table9.AddRow(new string[] { + table19.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -240,7 +240,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table9.AddRow(new string[] { + table19.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -249,9 +249,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table9, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table19, "When "); #line hidden - global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -259,7 +259,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table10.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -268,9 +268,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table10, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table20, "When "); #line hidden - global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -280,7 +280,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table11.AddRow(new string[] { + table21.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -290,7 +290,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 1", "testcontact1@merchant1.co.uk", "Test Estate 1"}); - table11.AddRow(new string[] { + table21.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -301,99 +301,99 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant2.co.uk", "Test Estate 1"}); #line 51 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table11, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table21, "Given "); #line hidden - global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table12.AddRow(new string[] { + table22.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table12.AddRow(new string[] { + table22.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table12.AddRow(new string[] { + table22.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table12.AddRow(new string[] { + table22.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); #line 56 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table12, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table22, "Given "); #line hidden - global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table13.AddRow(new string[] { + table23.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table13.AddRow(new string[] { + table23.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); #line 63 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table13, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table23, "Given "); #line hidden - global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table14.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table14.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table14.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table14.AddRow(new string[] { + table24.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); #line 68 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table14, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table24, "When "); #line hidden - global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table15.AddRow(new string[] { + table25.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table15.AddRow(new string[] { + table25.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 2", "Test Estate 1"}); #line 75 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table15, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table25, "Given "); #line hidden } @@ -429,144 +429,144 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table16.AddRow(new string[] { + table26.AddRow(new string[] { "H", "20210508", ""}); - table16.AddRow(new string[] { + table26.AddRow(new string[] { "D", "07777777775", "100"}); - table16.AddRow(new string[] { + table26.AddRow(new string[] { "T", "1", ""}); #line 83 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table16, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table26, "Given "); #line hidden - global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table17.AddRow(new string[] { + table27.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 88 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table17, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table27, "And "); #line hidden - global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table18.AddRow(new string[] { + table28.AddRow(new string[] { "H", "20210508", ""}); - table18.AddRow(new string[] { + table28.AddRow(new string[] { "D", "07777777776", "150"}); - table18.AddRow(new string[] { + table28.AddRow(new string[] { "T", "1", ""}); #line 92 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table18, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table28, "Given "); #line hidden - global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table19.AddRow(new string[] { + table29.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 97 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table19, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table29, "And "); #line hidden - global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table20.AddRow(new string[] { + table30.AddRow(new string[] { "H", "20210508", "", ""}); - table20.AddRow(new string[] { + table30.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table20.AddRow(new string[] { + table30.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table20.AddRow(new string[] { + table30.AddRow(new string[] { "T", "1", "", ""}); #line 101 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table20, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table30, "Given "); #line hidden - global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table21.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 107 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table21, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table31, "And "); #line hidden - global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { "ImportLogDate", "FileCount"}); - table22.AddRow(new string[] { + table32.AddRow(new string[] { "Today", "3"}); #line 111 await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Yesterday\' and \'Today\' the followi" + - "ng data is returned", ((string)(null)), table22, "When "); + "ng data is returned", ((string)(null)), table32, "When "); #line hidden - global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { "MerchantName", "OriginalFileName"}); - table23.AddRow(new string[] { + table33.AddRow(new string[] { "Test Merchant 1", "SafarcomTopup1.txt"}); - table23.AddRow(new string[] { + table33.AddRow(new string[] { "Test Merchant 2", "SafarcomTopup2.txt"}); - table23.AddRow(new string[] { + table33.AddRow(new string[] { "Test Merchant 1", "VoucherIssue1.txt"}); #line 115 await testRunner.WhenAsync("I get the \'Test Estate 1\' import log for \'Today\' the following file information i" + - "s returned", ((string)(null)), table23, "When "); + "s returned", ((string)(null)), table33, "When "); #line hidden - global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -574,7 +574,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table24.AddRow(new string[] { + table34.AddRow(new string[] { "True", "3", "3", @@ -584,29 +584,29 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 121 await testRunner.WhenAsync("I get the file \'SafarcomTopup1.txt\' for Estate \'Test Estate 1\' the following file" + - " information is returned", ((string)(null)), table24, "When "); + " information is returned", ((string)(null)), table34, "When "); #line hidden - global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table25.AddRow(new string[] { + table35.AddRow(new string[] { "1", "H,20210508,", "Ignored"}); - table25.AddRow(new string[] { + table35.AddRow(new string[] { "2", "D,07777777775,100", "Successful"}); - table25.AddRow(new string[] { + table35.AddRow(new string[] { "3", "T,1,", "Ignored"}); #line 125 await testRunner.WhenAsync("I get the file \'SafarcomTopup1.txt\' for Estate \'Test Estate 1\' the following file" + - " lines are returned", ((string)(null)), table25, "When "); + " lines are returned", ((string)(null)), table35, "When "); #line hidden - global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -614,7 +614,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table26.AddRow(new string[] { + table36.AddRow(new string[] { "True", "3", "3", @@ -624,29 +624,29 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 131 await testRunner.WhenAsync("I get the file \'SafarcomTopup2.txt\' for Estate \'Test Estate 1\' the following file" + - " information is returned", ((string)(null)), table26, "When "); + " information is returned", ((string)(null)), table36, "When "); #line hidden - global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table27.AddRow(new string[] { + table37.AddRow(new string[] { "1", "H,20210508,", "Ignored"}); - table27.AddRow(new string[] { + table37.AddRow(new string[] { "2", "D,07777777776,150", "Successful"}); - table27.AddRow(new string[] { + table37.AddRow(new string[] { "3", "T,1,", "Ignored"}); #line 135 await testRunner.WhenAsync("I get the file \'SafarcomTopup2.txt\' for Estate \'Test Estate 1\' the following file" + - " lines are returned", ((string)(null)), table27, "When "); + " lines are returned", ((string)(null)), table37, "When "); #line hidden - global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -654,7 +654,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table28.AddRow(new string[] { + table38.AddRow(new string[] { "True", "4", "4", @@ -664,31 +664,31 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 141 await testRunner.WhenAsync("I get the file \'VoucherIssue1.txt\' for Estate \'Test Estate 1\' the following file " + - "information is returned", ((string)(null)), table28, "When "); + "information is returned", ((string)(null)), table38, "When "); #line hidden - global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table29.AddRow(new string[] { + table39.AddRow(new string[] { "1", "H,20210508,,", "Ignored"}); - table29.AddRow(new string[] { + table39.AddRow(new string[] { "2", "D,Hospital 1,07777777775,10", "Successful"}); - table29.AddRow(new string[] { + table39.AddRow(new string[] { "3", "D,Hospital 1,testrecipient1@recipient.com,10", "Successful"}); - table29.AddRow(new string[] { + table39.AddRow(new string[] { "4", "T,1,,", "Ignored"}); #line 145 await testRunner.WhenAsync("I get the file \'VoucherIssue1.txt\' for Estate \'Test Estate 1\' the following file " + - "lines are returned", ((string)(null)), table29, "When "); + "lines are returned", ((string)(null)), table39, "When "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs b/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs index fcecbd5..6c71026 100644 --- a/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs +++ b/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs @@ -111,118 +111,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table40 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table30.AddRow(new string[] { + table40.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table30.AddRow(new string[] { + table40.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table30, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table40, "Given "); #line hidden - global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table41 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table31.AddRow(new string[] { + table41.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table31.AddRow(new string[] { + table41.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table31, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table41, "Given "); #line hidden - global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table42 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table32.AddRow(new string[] { + table42.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table32, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table42, "Given "); #line hidden - global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table43 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table33.AddRow(new string[] { + table43.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table33, "Given "); + "s", ((string)(null)), table43, "Given "); #line hidden - global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table44 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table34.AddRow(new string[] { + table44.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table34, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table44, "Given "); #line hidden - global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table45 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table35.AddRow(new string[] { + table45.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table35.AddRow(new string[] { + table45.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table35, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table45, "Given "); #line hidden - global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table46 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table36.AddRow(new string[] { + table46.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table36.AddRow(new string[] { + table46.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table36, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table46, "And "); #line hidden - global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table47 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table37.AddRow(new string[] { + table47.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table37.AddRow(new string[] { + table47.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table37, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table47, "Given "); #line hidden - global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table48 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -230,7 +230,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table38.AddRow(new string[] { + table48.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -238,7 +238,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table38.AddRow(new string[] { + table48.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -247,9 +247,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table38, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table48, "When "); #line hidden - global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table49 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -257,7 +257,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table39.AddRow(new string[] { + table49.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -266,9 +266,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table39, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table49, "When "); #line hidden - global::Reqnroll.Table table40 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table50 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -278,7 +278,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table40.AddRow(new string[] { + table50.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -289,69 +289,69 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk", "Test Estate 1"}); #line 51 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table40, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table50, "Given "); #line hidden - global::Reqnroll.Table table41 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table51 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table41.AddRow(new string[] { + table51.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table41.AddRow(new string[] { + table51.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); #line 55 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table41, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table51, "Given "); #line hidden - global::Reqnroll.Table table42 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table52 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table42.AddRow(new string[] { + table52.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); #line 60 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table42, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table52, "Given "); #line hidden - global::Reqnroll.Table table43 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table53 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table43.AddRow(new string[] { + table53.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table43.AddRow(new string[] { + table53.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); #line 64 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table43, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table53, "When "); #line hidden - global::Reqnroll.Table table44 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table54 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table44.AddRow(new string[] { + table54.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); #line 69 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table44, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table54, "Given "); #line hidden } @@ -385,37 +385,37 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table45 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table55 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table45.AddRow(new string[] { + table55.AddRow(new string[] { "H", "20210508", ""}); - table45.AddRow(new string[] { + table55.AddRow(new string[] { "D", "07777777775", "100"}); - table45.AddRow(new string[] { + table55.AddRow(new string[] { "T", "1", ""}); #line 75 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table45, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table55, "Given "); #line hidden - global::Reqnroll.Table table46 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table56 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table46.AddRow(new string[] { + table56.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 80 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table46, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table56, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -446,41 +446,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table47 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table57 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table47.AddRow(new string[] { + table57.AddRow(new string[] { "H", "20210508", ""}); - table47.AddRow(new string[] { + table57.AddRow(new string[] { "D", "07777777775", "100"}); - table47.AddRow(new string[] { + table57.AddRow(new string[] { "D", "07777777776", "200"}); - table47.AddRow(new string[] { + table57.AddRow(new string[] { "T", "2", ""}); #line 88 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table47, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table57, "Given "); #line hidden - global::Reqnroll.Table table48 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table58 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table48.AddRow(new string[] { + table58.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 94 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table48, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table58, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -511,73 +511,73 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table49 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table59 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table49.AddRow(new string[] { + table59.AddRow(new string[] { "H", "20210508", ""}); - table49.AddRow(new string[] { + table59.AddRow(new string[] { "D", "07777777775", "100"}); - table49.AddRow(new string[] { + table59.AddRow(new string[] { "T", "1", ""}); #line 102 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table49, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table59, "Given "); #line hidden - global::Reqnroll.Table table50 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table60 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table50.AddRow(new string[] { + table60.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 107 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table50, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table60, "And "); #line hidden - global::Reqnroll.Table table51 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table61 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table51.AddRow(new string[] { + table61.AddRow(new string[] { "H", "20210508", ""}); - table51.AddRow(new string[] { + table61.AddRow(new string[] { "D", "07777777776", "150"}); - table51.AddRow(new string[] { + table61.AddRow(new string[] { "D", "07777777777", "50"}); - table51.AddRow(new string[] { + table61.AddRow(new string[] { "T", "2", ""}); #line 111 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table51, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table61, "Given "); #line hidden - global::Reqnroll.Table table52 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table62 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table52.AddRow(new string[] { + table62.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 117 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table52, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table62, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -610,78 +610,78 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table53 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table63 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table53.AddRow(new string[] { + table63.AddRow(new string[] { "H", "20210508", ""}); - table53.AddRow(new string[] { + table63.AddRow(new string[] { "D", "07777777775", "100"}); - table53.AddRow(new string[] { + table63.AddRow(new string[] { "D", "07777777776", "200"}); - table53.AddRow(new string[] { + table63.AddRow(new string[] { "T", "1", ""}); #line 126 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table53, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table63, "Given "); #line hidden - global::Reqnroll.Table table54 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table64 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table54.AddRow(new string[] { + table64.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 132 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table54, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table64, "And "); #line hidden - global::Reqnroll.Table table55 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table65 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table55.AddRow(new string[] { + table65.AddRow(new string[] { "H", "20210508", ""}); - table55.AddRow(new string[] { + table65.AddRow(new string[] { "D", "07777777775", "100"}); - table55.AddRow(new string[] { + table65.AddRow(new string[] { "D", "07777777776", "200"}); - table55.AddRow(new string[] { + table65.AddRow(new string[] { "T", "1", ""}); #line 138 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table55, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table65, "Given "); #line hidden - global::Reqnroll.Table table56 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table66 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table56.AddRow(new string[] { + table66.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 144 await testRunner.AndAsync("I upload this file for processing an error should be returned indicating the file" + - " is a duplicate", ((string)(null)), table56, "And "); + " is a duplicate", ((string)(null)), table66, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -712,77 +712,77 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table57 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table67 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table57.AddRow(new string[] { + table67.AddRow(new string[] { "H", "20210508", ""}); - table57.AddRow(new string[] { + table67.AddRow(new string[] { "D", "07777777775", "100"}); - table57.AddRow(new string[] { + table67.AddRow(new string[] { "T", "1", ""}); #line 151 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table57, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table67, "Given "); #line hidden - global::Reqnroll.Table table58 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table68 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table58.AddRow(new string[] { + table68.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 156 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table58, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table68, "And "); #line hidden #line 160 await testRunner.WhenAsync("I get the import log for estate \'Test Estate 1\' the date on the import log is \'To" + "day\'", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden - global::Reqnroll.Table table59 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table69 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table59.AddRow(new string[] { + table69.AddRow(new string[] { "H", "20210508", ""}); - table59.AddRow(new string[] { + table69.AddRow(new string[] { "D", "07777777775", "200"}); - table59.AddRow(new string[] { + table69.AddRow(new string[] { "T", "1", ""}); #line 162 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table59, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table69, "Given "); #line hidden - global::Reqnroll.Table table60 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table70 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table60.AddRow(new string[] { + table70.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "01/09/2021"}); #line 167 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table60, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table70, "And "); #line hidden #line 171 await testRunner.WhenAsync("I get the import log for estate \'Test Estate 1\' the date on the import log is \'01" + diff --git a/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs b/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs index 6387541..d860f83 100644 --- a/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs +++ b/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs @@ -113,118 +113,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table61 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table71 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table61.AddRow(new string[] { + table71.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table61.AddRow(new string[] { + table71.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table61, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table71, "Given "); #line hidden - global::Reqnroll.Table table62 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table72 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table62.AddRow(new string[] { + table72.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table62.AddRow(new string[] { + table72.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table62, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table72, "Given "); #line hidden - global::Reqnroll.Table table63 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table73 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table63.AddRow(new string[] { + table73.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table63, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table73, "Given "); #line hidden - global::Reqnroll.Table table64 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table74 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table64.AddRow(new string[] { + table74.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table64, "Given "); + "s", ((string)(null)), table74, "Given "); #line hidden - global::Reqnroll.Table table65 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table75 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table65.AddRow(new string[] { + table75.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table65, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table75, "Given "); #line hidden - global::Reqnroll.Table table66 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table66.AddRow(new string[] { + table76.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table66.AddRow(new string[] { + table76.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table66, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table76, "Given "); #line hidden - global::Reqnroll.Table table67 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table67.AddRow(new string[] { + table77.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table67.AddRow(new string[] { + table77.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table67, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table77, "And "); #line hidden - global::Reqnroll.Table table68 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table68.AddRow(new string[] { + table78.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table68.AddRow(new string[] { + table78.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table68, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table78, "Given "); #line hidden - global::Reqnroll.Table table69 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -232,7 +232,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table69.AddRow(new string[] { + table79.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -240,7 +240,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table69.AddRow(new string[] { + table79.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -249,9 +249,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table69, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table79, "When "); #line hidden - global::Reqnroll.Table table70 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -259,7 +259,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table70.AddRow(new string[] { + table80.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -267,7 +267,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Fixed", "Merchant Commission", "2.50"}); - table70.AddRow(new string[] { + table80.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -276,9 +276,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table70, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table80, "When "); #line hidden - global::Reqnroll.Table table71 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -288,7 +288,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table71.AddRow(new string[] { + table81.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -299,69 +299,69 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk", "Test Estate 1"}); #line 52 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table71, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table81, "Given "); #line hidden - global::Reqnroll.Table table72 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table72.AddRow(new string[] { + table82.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table72.AddRow(new string[] { + table82.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); #line 56 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table72, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table82, "Given "); #line hidden - global::Reqnroll.Table table73 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table73.AddRow(new string[] { + table83.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); #line 61 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table73, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table83, "Given "); #line hidden - global::Reqnroll.Table table74 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table74.AddRow(new string[] { + table84.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table74.AddRow(new string[] { + table84.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); #line 65 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table74, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table84, "When "); #line hidden - global::Reqnroll.Table table75 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table85 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table75.AddRow(new string[] { + table85.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); #line 70 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table75, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table85, "Given "); #line hidden } @@ -395,41 +395,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table86 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table76.AddRow(new string[] { + table86.AddRow(new string[] { "H", "20210508", "", ""}); - table76.AddRow(new string[] { + table86.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table76.AddRow(new string[] { + table86.AddRow(new string[] { "T", "1", "", ""}); #line 76 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table76, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table86, "Given "); #line hidden - global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table87 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table77.AddRow(new string[] { + table87.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 81 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table77, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table87, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -460,41 +460,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table88 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table78.AddRow(new string[] { + table88.AddRow(new string[] { "H", "20210508", "", ""}); - table78.AddRow(new string[] { + table88.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table78.AddRow(new string[] { + table88.AddRow(new string[] { "T", "1", "", ""}); #line 89 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table78, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table88, "Given "); #line hidden - global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table89 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table79.AddRow(new string[] { + table89.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 94 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table79, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table89, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -525,46 +525,46 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table90 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table80.AddRow(new string[] { + table90.AddRow(new string[] { "H", "20210508", "", ""}); - table80.AddRow(new string[] { + table90.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table80.AddRow(new string[] { + table90.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table80.AddRow(new string[] { + table90.AddRow(new string[] { "T", "1", "", ""}); #line 102 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table80, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table90, "Given "); #line hidden - global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table91 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table81.AddRow(new string[] { + table91.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 108 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table81, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table91, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -595,87 +595,87 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table92 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table82.AddRow(new string[] { + table92.AddRow(new string[] { "H", "20210508", "", ""}); - table82.AddRow(new string[] { + table92.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table82.AddRow(new string[] { + table92.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table82.AddRow(new string[] { + table92.AddRow(new string[] { "T", "1", "", ""}); #line 116 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table82, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table92, "Given "); #line hidden - global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table93 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table83.AddRow(new string[] { + table93.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 122 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table83, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table93, "And "); #line hidden - global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table94 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table84.AddRow(new string[] { + table94.AddRow(new string[] { "H", "20210508", "", ""}); - table84.AddRow(new string[] { + table94.AddRow(new string[] { "D", "Hospital 1", "07777777775", "20"}); - table84.AddRow(new string[] { + table94.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table84.AddRow(new string[] { + table94.AddRow(new string[] { "T", "1", "", ""}); #line 126 - await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table84, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table94, "Given "); #line hidden - global::Reqnroll.Table table85 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table95 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table85.AddRow(new string[] { + table95.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 132 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table85, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table95, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -708,88 +708,88 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table86 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table96 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table86.AddRow(new string[] { + table96.AddRow(new string[] { "H", "20210508", "", ""}); - table86.AddRow(new string[] { + table96.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table86.AddRow(new string[] { + table96.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table86.AddRow(new string[] { + table96.AddRow(new string[] { "T", "1", "", ""}); #line 141 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table86, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table96, "Given "); #line hidden - global::Reqnroll.Table table87 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table97 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table87.AddRow(new string[] { + table97.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 147 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table87, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table97, "And "); #line hidden - global::Reqnroll.Table table88 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table98 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table88.AddRow(new string[] { + table98.AddRow(new string[] { "H", "20210508", "", ""}); - table88.AddRow(new string[] { + table98.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table88.AddRow(new string[] { + table98.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table88.AddRow(new string[] { + table98.AddRow(new string[] { "T", "1", "", ""}); #line 153 - await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table88, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table98, "Given "); #line hidden - global::Reqnroll.Table table89 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table99 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table89.AddRow(new string[] { + table99.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 160 await testRunner.AndAsync("I upload this file for processing an error should be returned indicating the file" + - " is a duplicate", ((string)(null)), table89, "And "); + " is a duplicate", ((string)(null)), table99, "And "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/FileProcessor.IntegrationTests/FileProcessor.IntegrationTests.csproj b/FileProcessor.IntegrationTests/FileProcessor.IntegrationTests.csproj index d30f2e0..e6ce79e 100644 --- a/FileProcessor.IntegrationTests/FileProcessor.IntegrationTests.csproj +++ b/FileProcessor.IntegrationTests/FileProcessor.IntegrationTests.csproj @@ -40,6 +40,7 @@ + diff --git a/FileProcessor.sln b/FileProcessor.sln index df5f885..42959a8 100644 --- a/FileProcessor.sln +++ b/FileProcessor.sln @@ -27,10 +27,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileAggregate EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileImportLogAggregate", "FileProcessor.FileImportLogAggregate\FileProcessor.FileImportLogAggregate.csproj", "{407BB702-E9AD-4100-9174-3B3BBE79AFF2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileProfile.DomainEvents", "FileProcessor.FileProfile.DomainEvents\FileProcessor.FileProfile.DomainEvents.csproj", "{A8A00D72-7B5E-4E9A-8F8E-5F0E5A6F17D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileProfileAggregate", "FileProcessor.FileProfileAggregate\FileProcessor.FileProfileAggregate.csproj", "{8BE57A76-E621-4D79-A2CE-77E6F07FD974}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileImportLog.DomainEvents", "FileProcessor.FileImportLog.DomainEvents\FileProcessor.FileImportLog.DomainEvents.csproj", "{0C3CC2E5-7DE5-4DC6-89F8-20A34008510B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileImportLogAggregate.Tests", "FileProcessor.FileImportLogAggregate.Tests\FileProcessor.FileImportLogAggregate.Tests.csproj", "{E5538650-D916-4E30-B26D-3A3C63D9C480}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.FileProfileAggregate.Tests", "FileProcessor.FileProfileAggregate.Tests\FileProcessor.FileProfileAggregate.Tests.csproj", "{8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.IntegrationTests", "FileProcessor.IntegrationTests\FileProcessor.IntegrationTests.csproj", "{DCD88706-0BE9-40F3-B1C7-2C852AA6E15C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileProcessor.Tests", "FileProcessor.Tests\FileProcessor.Tests.csproj", "{9CD81E46-326B-45F7-890E-310F1315758C}" @@ -91,6 +97,14 @@ Global {407BB702-E9AD-4100-9174-3B3BBE79AFF2}.Debug|Any CPU.Build.0 = Debug|Any CPU {407BB702-E9AD-4100-9174-3B3BBE79AFF2}.Release|Any CPU.ActiveCfg = Release|Any CPU {407BB702-E9AD-4100-9174-3B3BBE79AFF2}.Release|Any CPU.Build.0 = Release|Any CPU + {A8A00D72-7B5E-4E9A-8F8E-5F0E5A6F17D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8A00D72-7B5E-4E9A-8F8E-5F0E5A6F17D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8A00D72-7B5E-4E9A-8F8E-5F0E5A6F17D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8A00D72-7B5E-4E9A-8F8E-5F0E5A6F17D9}.Release|Any CPU.Build.0 = Release|Any CPU + {8BE57A76-E621-4D79-A2CE-77E6F07FD974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8BE57A76-E621-4D79-A2CE-77E6F07FD974}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8BE57A76-E621-4D79-A2CE-77E6F07FD974}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8BE57A76-E621-4D79-A2CE-77E6F07FD974}.Release|Any CPU.Build.0 = Release|Any CPU {0C3CC2E5-7DE5-4DC6-89F8-20A34008510B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C3CC2E5-7DE5-4DC6-89F8-20A34008510B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C3CC2E5-7DE5-4DC6-89F8-20A34008510B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -99,6 +113,10 @@ Global {E5538650-D916-4E30-B26D-3A3C63D9C480}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5538650-D916-4E30-B26D-3A3C63D9C480}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5538650-D916-4E30-B26D-3A3C63D9C480}.Release|Any CPU.Build.0 = Release|Any CPU + {8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8}.Release|Any CPU.Build.0 = Release|Any CPU {DCD88706-0BE9-40F3-B1C7-2C852AA6E15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DCD88706-0BE9-40F3-B1C7-2C852AA6E15C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCD88706-0BE9-40F3-B1C7-2C852AA6E15C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -132,6 +150,7 @@ Global {407BB702-E9AD-4100-9174-3B3BBE79AFF2} = {DB9E2720-74A1-4B32-B5BF-6C7C75385A40} {0C3CC2E5-7DE5-4DC6-89F8-20A34008510B} = {DB9E2720-74A1-4B32-B5BF-6C7C75385A40} {E5538650-D916-4E30-B26D-3A3C63D9C480} = {90754177-874F-4E7F-ACC4-BE1B879332E6} + {8FBAF7D7-0959-4F72-9E3E-9A0E8D6A45E8} = {90754177-874F-4E7F-ACC4-BE1B879332E6} {DCD88706-0BE9-40F3-B1C7-2C852AA6E15C} = {90754177-874F-4E7F-ACC4-BE1B879332E6} {9CD81E46-326B-45F7-890E-310F1315758C} = {90754177-874F-4E7F-ACC4-BE1B879332E6} {958AD704-4785-49B6-A90E-FEA2F63D92AC} = {DB9E2720-74A1-4B32-B5BF-6C7C75385A40} diff --git a/FileProcessor/Bootstrapper/FileRegistry.cs b/FileProcessor/Bootstrapper/FileRegistry.cs index 85e01ee..ce9c1e1 100644 --- a/FileProcessor/Bootstrapper/FileRegistry.cs +++ b/FileProcessor/Bootstrapper/FileRegistry.cs @@ -6,10 +6,10 @@ using System.IO.Abstractions; using System.Linq; using BusinessLogic.FileFormatHandlers; -using FileProcessor.Models; using Lamar; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using FileProfileModel = global::FileProcessor.Models.FileProfile; [ExcludeFromCodeCoverage] public class FileRegistry : ServiceRegistry @@ -21,7 +21,7 @@ public class FileRegistry : ServiceRegistry /// public FileRegistry() { - IEnumerable fileProfiles = Startup.Configuration.GetSection("AppSettings:FileProfiles").GetChildren().ToList().Select(x => new + IEnumerable fileProfiles = Startup.Configuration.GetSection("AppSettings:FileProfiles").GetChildren().ToList().Select(x => new { Name = x.GetValue("Name"), FileProfileId = x.GetValue("Id"), @@ -32,7 +32,7 @@ public FileRegistry() FileFormatHandler = x.GetValue("FileFormatHandler") }).Select(f => { - return new FileProfile(f.FileProfileId, + return new FileProfileModel(f.FileProfileId, f.Name, f.ListeningDirectory, f.RequestType, @@ -54,4 +54,4 @@ public FileRegistry() } #endregion -} \ No newline at end of file +} diff --git a/FileProcessor/Bootstrapper/MiddlewareRegistry.cs b/FileProcessor/Bootstrapper/MiddlewareRegistry.cs index 7ca9258..39a43b6 100644 --- a/FileProcessor/Bootstrapper/MiddlewareRegistry.cs +++ b/FileProcessor/Bootstrapper/MiddlewareRegistry.cs @@ -146,6 +146,7 @@ private HttpClientHandler ApiEndpointHttpHandler(IServiceProvider serviceProvide #endregion } + [ExcludeFromCodeCoverage] public static class JsonSerializerConfiguration { public static void ConfigureMinimalApi(JsonSerializerOptions serializerOptions) diff --git a/FileProcessor/Bootstrapper/RepositoryRegistry.cs b/FileProcessor/Bootstrapper/RepositoryRegistry.cs index 16341c2..44a1054 100644 --- a/FileProcessor/Bootstrapper/RepositoryRegistry.cs +++ b/FileProcessor/Bootstrapper/RepositoryRegistry.cs @@ -19,6 +19,7 @@ namespace FileProcessor.Bootstrapper; using Shared.General; using System; using System.Diagnostics.CodeAnalysis; +using FileProfileAggregateRoot = FileProcessor.FileProfileAggregate.FileProfileAggregate; [ExcludeFromCodeCoverage] public class RepositoryRegistry : ServiceRegistry @@ -53,11 +54,14 @@ public RepositoryRegistry() this.AddSingleton, AggregateRepository>(); this.AddSingleton, AggregateRepository>(); + this.AddSingleton, + AggregateRepository>(); + this.AddSingleton(); this.AddSingleton(); this.AddSingleton>(cont => SubscriptionRepository.Create); } #endregion -} \ No newline at end of file +} diff --git a/FileProcessor/Common/Extensions.cs b/FileProcessor/Common/Extensions.cs index 0705392..08f14f1 100644 --- a/FileProcessor/Common/Extensions.cs +++ b/FileProcessor/Common/Extensions.cs @@ -4,7 +4,6 @@ namespace FileProcessor.Common; using EventStore.Client; using FileProcessor.BusinessLogic.Managers; -using FileProcessor.Models; using KurrentDB.Client; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; @@ -23,6 +22,7 @@ namespace FileProcessor.Common; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using FileProfileModel = global::FileProcessor.Models.FileProfile; [ExcludeFromCodeCoverage] public static class Extensions @@ -74,15 +74,15 @@ public static void PreWarm(this IApplicationBuilder applicationBuilder){ fileSystem.Directory.CreateDirectory(temporaryFileLocation); Logger.LogInformation($"Created TemporaryFileLocation at [{temporaryFileLocation}]"); - Result> fileProfilesResult = fileProcessorManager.GetAllFileProfiles(CancellationToken.None).Result; + Result> fileProfilesResult = fileProcessorManager.GetAllFileProfiles(CancellationToken.None).Result; if (fileProfilesResult.IsFailed) { Logger.LogWarning($"Error getting file profiles {fileProfilesResult.Message}"); throw new ApplicationStartupException(fileProfilesResult.Message); } - List fileProfiles = fileProfilesResult.Data; - foreach (FileProfile fileProfile in fileProfiles){ + List fileProfiles = fileProfilesResult.Data; + foreach (FileProfileModel fileProfile in fileProfiles){ fileSystem.Directory.CreateDirectory($"{fileProfile.ListeningDirectory}//inprogress"); Logger.LogInformation($"Created in progress at [{fileProfile.ListeningDirectory}//inprogress"); fileSystem.Directory.CreateDirectory(fileProfile.ProcessedDirectory); @@ -121,4 +121,4 @@ public ApplicationStartupException(string message) { } } -} \ No newline at end of file +} diff --git a/FileProcessor/Endpoints/FileProfileEndpoints.cs b/FileProcessor/Endpoints/FileProfileEndpoints.cs new file mode 100644 index 0000000..b314f1d --- /dev/null +++ b/FileProcessor/Endpoints/FileProfileEndpoints.cs @@ -0,0 +1,34 @@ +using System; +using FileProcessor.Handlers; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace FileProcessor.Endpoints; + +public static class FileProfileEndpoints +{ + public static readonly String BaseRoute = "api/file-profiles"; + + public static void MapFileProfileEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("File Profiles") + .RequireAuthorization(); + + group.MapGet("/", FileProfileHandlers.GetFileProfiles) + .WithName("GetFileProfiles"); + + group.MapGet("/{fileProfileId:guid}", FileProfileHandlers.GetFileProfile) + .WithName("GetFileProfile"); + + group.MapPost("/", FileProfileHandlers.CreateFileProfile) + .WithName("CreateFileProfile"); + + group.MapPatch("/{fileProfileId:guid}", FileProfileHandlers.UpdateFileProfile) + .WithName("UpdateFileProfile"); + + group.MapDelete("/{fileProfileId:guid}", FileProfileHandlers.ArchiveFileProfile) + .WithName("ArchiveFileProfile"); + } +} diff --git a/FileProcessor/GlobalUsings.cs b/FileProcessor/GlobalUsings.cs new file mode 100644 index 0000000..51067b2 --- /dev/null +++ b/FileProcessor/GlobalUsings.cs @@ -0,0 +1 @@ +global using FileProfile = global::FileProcessor.Models.FileProfile; diff --git a/FileProcessor/Handlers/FileProfileHandlers.cs b/FileProcessor/Handlers/FileProfileHandlers.cs new file mode 100644 index 0000000..a6b72c9 --- /dev/null +++ b/FileProcessor/Handlers/FileProfileHandlers.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FileProcessor.BusinessLogic.Requests; +using FileProcessor.DataTransferObjects.Requests; +using MediatR; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; +using FileProfileModel = global::FileProcessor.Models.FileProfile; + +namespace FileProcessor.Handlers; + +public static class FileProfileHandlers +{ + public static async Task GetFileProfiles(IMediator mediator, CancellationToken cancellationToken) + { + FileProfileQueries.GetFileProfilesQuery query = new(); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, x => x); + } + + public static async Task GetFileProfile(IMediator mediator, + [FromRoute] Guid fileProfileId, + CancellationToken cancellationToken) + { + FileProfileQueries.GetFileProfileQuery query = new(fileProfileId); + Result result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, x => x); + } + + public static async Task CreateFileProfile(IMediator mediator, + [FromBody] CreateFileProfileRequest request, + CancellationToken cancellationToken) + { + FileProfileCommands.CreateFileProfileCommand command = new(request); + Result result = await mediator.Send(command, cancellationToken); + + return ResponseFactory.FromResult(result, x => x); + } + + public static async Task UpdateFileProfile(IMediator mediator, + [FromRoute] Guid fileProfileId, + [FromBody] UpdateFileProfileRequest request, + CancellationToken cancellationToken) + { + FileProfileCommands.UpdateFileProfileCommand command = new(fileProfileId, request); + Result result = await mediator.Send(command, cancellationToken); + + return ResponseFactory.FromResult(result, x => x); + } + + public static async Task ArchiveFileProfile(IMediator mediator, + [FromRoute] Guid fileProfileId, + CancellationToken cancellationToken) + { + FileProfileCommands.ArchiveFileProfileCommand command = new(fileProfileId); + Result result = await mediator.Send(command, cancellationToken); + + if (result.IsFailed) + { + return ResponseFactory.FromResult(result); + } + + return Results.NoContent(); + } +} diff --git a/FileProcessor/Startup.cs b/FileProcessor/Startup.cs index 6f6816d..7acce98 100644 --- a/FileProcessor/Startup.cs +++ b/FileProcessor/Startup.cs @@ -127,6 +127,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF endpoints.MapDomainEventEndpoint(); endpoints.MapFileEndpoints(); endpoints.MapFileImportLogEndpoints(); + endpoints.MapFileProfileEndpoints(); endpoints.MapHealthChecks("health", new HealthCheckOptions() { From 602bfbf8914b741340119ffa3be2d7410256709d Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sat, 11 Jul 2026 06:35:35 +0100 Subject: [PATCH 2/3] fixes from review --- .../DummyFileProcessorDomainService.cs | 4 + .../Managers/FileProcessorManager.cs | 5 + .../Managers/FileProfileManager.cs | 15 +- .../Managers/IFileProcessorManager.cs | 2 + .../Managers/IFileProfileManager.cs | 2 + .../FileProfileAggregate.cs | 198 +++++++------- .../Features/GetFileImportDetails.feature.cs | 236 ++++++++--------- .../Features/ProcessTopupCSV.feature.cs | 242 +++++++++--------- .../Features/ProcessVoucherCSV.feature.cs | 230 ++++++++--------- FileProcessor/Common/Extensions.cs | 6 + 10 files changed, 489 insertions(+), 451 deletions(-) diff --git a/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs index 6be3735..aded260 100644 --- a/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs +++ b/FileProcessor.BusinessLogic.Tests/DummyFileProcessorDomainService.cs @@ -27,6 +27,10 @@ public async Task ProcessTransactionForFileLine(FileCommands.ProcessTran } public class DummyFileProcessorManager : IFileProcessorManager { + public async Task EnsureSeededFileProfiles(CancellationToken cancellationToken) { + return Result.Success(); + } + public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) { return Result.Success(); } diff --git a/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs b/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs index b33db6d..901d5b2 100644 --- a/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs +++ b/FileProcessor.BusinessLogic/Managers/FileProcessorManager.cs @@ -76,6 +76,11 @@ public async Task> GetFileProfile(Guid fileProfileId, return await this.FileProfileManager.GetFileProfile(fileProfileId, cancellationToken); } + public async Task EnsureSeededFileProfiles(CancellationToken cancellationToken) + { + return await this.FileProfileManager.EnsureSeededFileProfiles(cancellationToken); + } + private async Task GetContext(Guid estateId) { ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); diff --git a/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs b/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs index c7d875a..89bc246 100644 --- a/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs +++ b/FileProcessor.BusinessLogic/Managers/FileProfileManager.cs @@ -28,7 +28,7 @@ public FileProfileManager(List seedProfiles, this.FileProfileAggregateRepository = fileProfileAggregateRepository; } - public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) + public async Task EnsureSeededFileProfiles(CancellationToken cancellationToken) { Result aggregateResult = await this.LoadAggregate(cancellationToken, true); if (aggregateResult.IsFailed) @@ -36,12 +36,23 @@ public async Task>> GetAllFileProfiles(Cancellatio return ResultHelpers.CreateFailure(aggregateResult); } + return Result.Success(); + } + + public async Task>> GetAllFileProfiles(CancellationToken cancellationToken) + { + Result aggregateResult = await this.LoadAggregate(cancellationToken, false); + if (aggregateResult.IsFailed) + { + return ResultHelpers.CreateFailure(aggregateResult); + } + return Result.Success(aggregateResult.Data.GetAllProfiles()); } public async Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken) { - Result aggregateResult = await this.LoadAggregate(cancellationToken, true); + Result aggregateResult = await this.LoadAggregate(cancellationToken, false); if (aggregateResult.IsFailed) { return ResultHelpers.CreateFailure(aggregateResult); diff --git a/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs b/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs index dad8041..4a6b3c6 100644 --- a/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs +++ b/FileProcessor.BusinessLogic/Managers/IFileProcessorManager.cs @@ -20,6 +20,8 @@ public interface IFileProcessorManager Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken); + Task EnsureSeededFileProfiles(CancellationToken cancellationToken); + Task>> GetFileImportLogs(Guid estateId, DateTime startDateTime, DateTime endDateTime, Guid? merchantId, CancellationToken cancellationToken); Task> GetFileImportLog(Guid fileImportLogId, Guid estateId, Guid? merchantId, CancellationToken cancellationToken); diff --git a/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs b/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs index a5ff4ce..b817411 100644 --- a/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs +++ b/FileProcessor.BusinessLogic/Managers/IFileProfileManager.cs @@ -10,6 +10,8 @@ namespace FileProcessor.BusinessLogic.Managers; public interface IFileProfileManager { + Task EnsureSeededFileProfiles(CancellationToken cancellationToken); + Task>> GetAllFileProfiles(CancellationToken cancellationToken); Task> GetFileProfile(Guid fileProfileId, CancellationToken cancellationToken); diff --git a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs index e946762..6cc52e8 100644 --- a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs +++ b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs @@ -127,35 +127,24 @@ public static Result UpdateProfile(this FileProfileAggregate aggregate, Guid fil return validationResult; } - if (request.Name != null && Comparer.Equals(profile.Name, request.Name.Trim()) == false) - { - aggregate.ApplyAndAppend(new FileProfileNameUpdatedEvent(aggregate.AggregateId, fileProfileId, request.Name.Trim())); - } - - if (request.ListeningDirectory != null && Comparer.Equals(profile.ListeningDirectory, request.ListeningDirectory.Trim()) == false) - { - aggregate.ApplyAndAppend(new FileProfileListeningDirectoryUpdatedEvent(aggregate.AggregateId, fileProfileId, request.ListeningDirectory.Trim())); - } - - if (request.RequestType != null && Comparer.Equals(profile.RequestType, request.RequestType.Trim()) == false) - { - aggregate.ApplyAndAppend(new FileProfileRequestTypeUpdatedEvent(aggregate.AggregateId, fileProfileId, request.RequestType.Trim())); - } - - if (request.OperatorName != null && Comparer.Equals(profile.OperatorName, request.OperatorName.Trim()) == false) - { - aggregate.ApplyAndAppend(new FileProfileOperatorNameUpdatedEvent(aggregate.AggregateId, fileProfileId, request.OperatorName.Trim())); - } - - if (request.LineTerminator != null && Comparer.Equals(profile.LineTerminator, request.LineTerminator) == false) - { - aggregate.ApplyAndAppend(new FileProfileLineTerminatorUpdatedEvent(aggregate.AggregateId, fileProfileId, request.LineTerminator)); - } - - if (request.FileFormatHandler != null && Comparer.Equals(profile.FileFormatHandler, request.FileFormatHandler.Trim()) == false) - { - aggregate.ApplyAndAppend(new FileProfileFileFormatHandlerUpdatedEvent(aggregate.AggregateId, fileProfileId, request.FileFormatHandler.Trim())); - } + aggregate.ApplyStringUpdateIfChanged(profile.Name, + request.Name, + value => new FileProfileNameUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); + aggregate.ApplyStringUpdateIfChanged(profile.ListeningDirectory, + request.ListeningDirectory, + value => new FileProfileListeningDirectoryUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); + aggregate.ApplyStringUpdateIfChanged(profile.RequestType, + request.RequestType, + value => new FileProfileRequestTypeUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); + aggregate.ApplyStringUpdateIfChanged(profile.OperatorName, + request.OperatorName, + value => new FileProfileOperatorNameUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); + aggregate.ApplyStringUpdateIfChanged(profile.LineTerminator, + request.LineTerminator, + value => new FileProfileLineTerminatorUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); + aggregate.ApplyStringUpdateIfChanged(profile.FileFormatHandler, + request.FileFormatHandler, + value => new FileProfileFileFormatHandlerUpdatedEvent(aggregate.AggregateId, fileProfileId, value)); return Result.Success(); } @@ -212,42 +201,13 @@ private static Result ValidateCreateRequest(this FileProfileAggregate aggregate, return Result.Invalid("No file profile name provided"); } - if (string.IsNullOrWhiteSpace(request.ListeningDirectory)) - { - return Result.Invalid("No listening directory provided"); - } - - if (string.IsNullOrWhiteSpace(request.RequestType)) - { - return Result.Invalid("No request type provided"); - } - - if (string.IsNullOrWhiteSpace(request.OperatorName)) - { - return Result.Invalid("No operator name provided"); - } - - if (string.IsNullOrEmpty(request.LineTerminator)) - { - return Result.Invalid("No line terminator provided"); - } - - if (string.IsNullOrWhiteSpace(request.FileFormatHandler)) + Result requiredFieldsResult = ValidateRequiredCreateFields(request); + if (requiredFieldsResult.IsFailed) { - return Result.Invalid("No file format handler provided"); + return requiredFieldsResult; } - if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.Name, request.Name.Trim()))) - { - return Result.Invalid($"A file profile with name [{request.Name}] already exists"); - } - - if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.RequestType, request.RequestType.Trim()))) - { - return Result.Invalid($"A file profile with request type [{request.RequestType}] already exists"); - } - - return Result.Success(); + return aggregate.ValidateCreateUniqueness(request); } private static Result ValidateUpdateRequest(this FileProfileAggregate aggregate, Guid fileProfileId, UpdateFileProfileRequest request) @@ -259,43 +219,68 @@ private static Result ValidateUpdateRequest(this FileProfileAggregate aggregate, FileProfileState currentProfile = aggregate.GetRequiredProfileState(fileProfileId); - String proposedName = request.Name?.Trim(); - String proposedListeningDirectory = request.ListeningDirectory?.Trim(); - String proposedRequestType = request.RequestType?.Trim(); - String proposedOperatorName = request.OperatorName?.Trim(); - String proposedLineTerminator = request.LineTerminator; - String proposedFileFormatHandler = request.FileFormatHandler?.Trim(); - - if (request.Name != null && string.IsNullOrWhiteSpace(proposedName)) + Result proposedValuesResult = ValidateProposedUpdateValues(request); + if (proposedValuesResult.IsFailed) { - return Result.Invalid("No file profile name provided"); + return proposedValuesResult; } - if (request.ListeningDirectory != null && string.IsNullOrWhiteSpace(proposedListeningDirectory)) - { - return Result.Invalid("No listening directory provided"); - } + return aggregate.ValidateUpdateUniqueness(fileProfileId, currentProfile, request); + } - if (request.RequestType != null && string.IsNullOrWhiteSpace(proposedRequestType)) - { - return Result.Invalid("No request type provided"); - } + private static Boolean IsSameProfile(this FileProfileAggregate aggregate, FileProfileState profile, CreateFileProfileRequest request) + { + return Comparer.Equals(profile.Name, request.Name.Trim()) && + Comparer.Equals(profile.ListeningDirectory, request.ListeningDirectory.Trim()) && + Comparer.Equals(profile.RequestType, request.RequestType.Trim()) && + Comparer.Equals(profile.OperatorName, request.OperatorName.Trim()) && + Comparer.Equals(profile.LineTerminator, request.LineTerminator) && + Comparer.Equals(profile.FileFormatHandler, request.FileFormatHandler.Trim()) && + profile.IsArchived == false; + } - if (request.OperatorName != null && string.IsNullOrWhiteSpace(proposedOperatorName)) - { - return Result.Invalid("No operator name provided"); - } + private static Result ValidateRequiredCreateFields(CreateFileProfileRequest request) + { + return ValidateStringField(request.ListeningDirectory, "No listening directory provided") ?? + ValidateStringField(request.RequestType, "No request type provided") ?? + ValidateStringField(request.OperatorName, "No operator name provided") ?? + ValidateStringField(request.LineTerminator, "No line terminator provided") ?? + ValidateStringField(request.FileFormatHandler, "No file format handler provided"); + } - if (request.LineTerminator != null && string.IsNullOrEmpty(proposedLineTerminator)) + private static Result ValidateProposedUpdateValues(UpdateFileProfileRequest request) + { + return ValidateOptionalStringField(request.Name, "No file profile name provided") ?? + ValidateOptionalStringField(request.ListeningDirectory, "No listening directory provided") ?? + ValidateOptionalStringField(request.RequestType, "No request type provided") ?? + ValidateOptionalStringField(request.OperatorName, "No operator name provided") ?? + ValidateOptionalStringField(request.LineTerminator, "No line terminator provided") ?? + ValidateOptionalStringField(request.FileFormatHandler, "No file format handler provided"); + } + + private static Result ValidateCreateUniqueness(this FileProfileAggregate aggregate, CreateFileProfileRequest request) + { + String proposedName = request.Name.Trim(); + String proposedRequestType = request.RequestType.Trim(); + + if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.Name, proposedName))) { - return Result.Invalid("No line terminator provided"); + return Result.Invalid($"A file profile with name [{request.Name}] already exists"); } - if (request.FileFormatHandler != null && string.IsNullOrWhiteSpace(proposedFileFormatHandler)) + if (aggregate.FileProfiles.Values.Any(profile => profile.IsArchived == false && Comparer.Equals(profile.RequestType, proposedRequestType))) { - return Result.Invalid("No file format handler provided"); + return Result.Invalid($"A file profile with request type [{request.RequestType}] already exists"); } + return Result.Success(); + } + + private static Result ValidateUpdateUniqueness(this FileProfileAggregate aggregate, Guid fileProfileId, FileProfileState currentProfile, UpdateFileProfileRequest request) + { + String proposedName = request.Name?.Trim(); + String proposedRequestType = request.RequestType?.Trim(); + if (request.Name != null && Comparer.Equals(currentProfile.Name, proposedName) == false && aggregate.FileProfiles.Values.Any(profile => profile.FileProfileId != fileProfileId && profile.IsArchived == false && Comparer.Equals(profile.Name, proposedName))) @@ -313,15 +298,38 @@ private static Result ValidateUpdateRequest(this FileProfileAggregate aggregate, return Result.Success(); } - private static Boolean IsSameProfile(this FileProfileAggregate aggregate, FileProfileState profile, CreateFileProfileRequest request) + private static Result ValidateStringField(String value, String errorMessage) { - return Comparer.Equals(profile.Name, request.Name.Trim()) && - Comparer.Equals(profile.ListeningDirectory, request.ListeningDirectory.Trim()) && - Comparer.Equals(profile.RequestType, request.RequestType.Trim()) && - Comparer.Equals(profile.OperatorName, request.OperatorName.Trim()) && - Comparer.Equals(profile.LineTerminator, request.LineTerminator) && - Comparer.Equals(profile.FileFormatHandler, request.FileFormatHandler.Trim()) && - profile.IsArchived == false; + return string.IsNullOrWhiteSpace(value) ? Result.Invalid(errorMessage) : Result.Success(); + } + + private static Result ValidateOptionalStringField(String value, String errorMessage) + { + if (value == null) + { + return Result.Success(); + } + + return ValidateStringField(value, errorMessage); + } + + private static void ApplyStringUpdateIfChanged(this FileProfileAggregate aggregate, + String currentValue, + String proposedValue, + Func eventFactory) + { + if (proposedValue == null) + { + return; + } + + String trimmedValue = proposedValue.Trim(); + if (Comparer.Equals(currentValue, trimmedValue)) + { + return; + } + + aggregate.ApplyAndAppend(eventFactory(trimmedValue)); } private static FileProfileState GetRequiredProfileState(this FileProfileAggregate aggregate, Guid fileProfileId) diff --git a/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs b/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs index f10b6aa..b6e2bc6 100644 --- a/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs +++ b/FileProcessor.IntegrationTests/Features/GetFileImportDetails.feature.cs @@ -113,118 +113,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table11.AddRow(new string[] { + table10.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table11.AddRow(new string[] { + table10.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table11, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table10, "Given "); #line hidden - global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table12.AddRow(new string[] { + table11.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table12.AddRow(new string[] { + table11.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table12, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table11, "Given "); #line hidden - global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table13.AddRow(new string[] { + table12.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table13, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table12, "Given "); #line hidden - global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table14.AddRow(new string[] { + table13.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table14, "Given "); + "s", ((string)(null)), table13, "Given "); #line hidden - global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table15.AddRow(new string[] { + table14.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table15, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table14, "Given "); #line hidden - global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table16.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table16.AddRow(new string[] { + table15.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table16, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table15, "Given "); #line hidden - global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table17.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table17.AddRow(new string[] { + table16.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table17, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table16, "And "); #line hidden - global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table18.AddRow(new string[] { + table17.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table18.AddRow(new string[] { + table17.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table18, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table17, "Given "); #line hidden - global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -232,7 +232,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table19.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -240,7 +240,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table19.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -249,9 +249,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table19, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table18, "When "); #line hidden - global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -259,7 +259,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table20.AddRow(new string[] { + table19.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -268,9 +268,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table20, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table19, "When "); #line hidden - global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -280,7 +280,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table21.AddRow(new string[] { + table20.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -290,7 +290,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 1", "testcontact1@merchant1.co.uk", "Test Estate 1"}); - table21.AddRow(new string[] { + table20.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -301,99 +301,99 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant2.co.uk", "Test Estate 1"}); #line 51 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table21, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table20, "Given "); #line hidden - global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table22.AddRow(new string[] { + table21.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table22.AddRow(new string[] { + table21.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table22.AddRow(new string[] { + table21.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table22.AddRow(new string[] { + table21.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); #line 56 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table22, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table21, "Given "); #line hidden - global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table23.AddRow(new string[] { + table22.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table23.AddRow(new string[] { + table22.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); #line 63 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table23, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table22, "Given "); #line hidden - global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table24.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table24.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table24.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table24.AddRow(new string[] { + table23.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); #line 68 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table24, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table23, "When "); #line hidden - global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table25.AddRow(new string[] { + table24.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table25.AddRow(new string[] { + table24.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 2", "Test Estate 1"}); #line 75 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table25, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table24, "Given "); #line hidden } @@ -429,144 +429,144 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table26.AddRow(new string[] { + table25.AddRow(new string[] { "H", "20210508", ""}); - table26.AddRow(new string[] { + table25.AddRow(new string[] { "D", "07777777775", "100"}); - table26.AddRow(new string[] { + table25.AddRow(new string[] { "T", "1", ""}); #line 83 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table26, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table25, "Given "); #line hidden - global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table27.AddRow(new string[] { + table26.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 88 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table27, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table26, "And "); #line hidden - global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table28.AddRow(new string[] { + table27.AddRow(new string[] { "H", "20210508", ""}); - table28.AddRow(new string[] { + table27.AddRow(new string[] { "D", "07777777776", "150"}); - table28.AddRow(new string[] { + table27.AddRow(new string[] { "T", "1", ""}); #line 92 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table28, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table27, "Given "); #line hidden - global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table29.AddRow(new string[] { + table28.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 97 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table29, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table28, "And "); #line hidden - global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table30.AddRow(new string[] { + table29.AddRow(new string[] { "H", "20210508", "", ""}); - table30.AddRow(new string[] { + table29.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table30.AddRow(new string[] { + table29.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table30.AddRow(new string[] { + table29.AddRow(new string[] { "T", "1", "", ""}); #line 101 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table30, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table29, "Given "); #line hidden - global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table31.AddRow(new string[] { + table30.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 107 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table31, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table30, "And "); #line hidden - global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { "ImportLogDate", "FileCount"}); - table32.AddRow(new string[] { + table31.AddRow(new string[] { "Today", "3"}); #line 111 await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Yesterday\' and \'Today\' the followi" + - "ng data is returned", ((string)(null)), table32, "When "); + "ng data is returned", ((string)(null)), table31, "When "); #line hidden - global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { "MerchantName", "OriginalFileName"}); - table33.AddRow(new string[] { + table32.AddRow(new string[] { "Test Merchant 1", "SafarcomTopup1.txt"}); - table33.AddRow(new string[] { + table32.AddRow(new string[] { "Test Merchant 2", "SafarcomTopup2.txt"}); - table33.AddRow(new string[] { + table32.AddRow(new string[] { "Test Merchant 1", "VoucherIssue1.txt"}); #line 115 await testRunner.WhenAsync("I get the \'Test Estate 1\' import log for \'Today\' the following file information i" + - "s returned", ((string)(null)), table33, "When "); + "s returned", ((string)(null)), table32, "When "); #line hidden - global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -574,7 +574,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table34.AddRow(new string[] { + table33.AddRow(new string[] { "True", "3", "3", @@ -584,29 +584,29 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 121 await testRunner.WhenAsync("I get the file \'SafarcomTopup1.txt\' for Estate \'Test Estate 1\' the following file" + - " information is returned", ((string)(null)), table34, "When "); + " information is returned", ((string)(null)), table33, "When "); #line hidden - global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table35.AddRow(new string[] { + table34.AddRow(new string[] { "1", "H,20210508,", "Ignored"}); - table35.AddRow(new string[] { + table34.AddRow(new string[] { "2", "D,07777777775,100", "Successful"}); - table35.AddRow(new string[] { + table34.AddRow(new string[] { "3", "T,1,", "Ignored"}); #line 125 await testRunner.WhenAsync("I get the file \'SafarcomTopup1.txt\' for Estate \'Test Estate 1\' the following file" + - " lines are returned", ((string)(null)), table35, "When "); + " lines are returned", ((string)(null)), table34, "When "); #line hidden - global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -614,7 +614,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table36.AddRow(new string[] { + table35.AddRow(new string[] { "True", "3", "3", @@ -624,29 +624,29 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 131 await testRunner.WhenAsync("I get the file \'SafarcomTopup2.txt\' for Estate \'Test Estate 1\' the following file" + - " information is returned", ((string)(null)), table36, "When "); + " information is returned", ((string)(null)), table35, "When "); #line hidden - global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table37.AddRow(new string[] { + table36.AddRow(new string[] { "1", "H,20210508,", "Ignored"}); - table37.AddRow(new string[] { + table36.AddRow(new string[] { "2", "D,07777777776,150", "Successful"}); - table37.AddRow(new string[] { + table36.AddRow(new string[] { "3", "T,1,", "Ignored"}); #line 135 await testRunner.WhenAsync("I get the file \'SafarcomTopup2.txt\' for Estate \'Test Estate 1\' the following file" + - " lines are returned", ((string)(null)), table37, "When "); + " lines are returned", ((string)(null)), table36, "When "); #line hidden - global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { "ProcessingCompleted", "NumberOfLines", "TotaLines", @@ -654,7 +654,7 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "IgnoredLines", "FailedLines", "NotProcessedLines"}); - table38.AddRow(new string[] { + table37.AddRow(new string[] { "True", "4", "4", @@ -664,31 +664,31 @@ await testRunner.WhenAsync("I get the \'Test Estate 1\' import logs between \'Ye "0"}); #line 141 await testRunner.WhenAsync("I get the file \'VoucherIssue1.txt\' for Estate \'Test Estate 1\' the following file " + - "information is returned", ((string)(null)), table38, "When "); + "information is returned", ((string)(null)), table37, "When "); #line hidden - global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { "LineNumber", "Data", "Result"}); - table39.AddRow(new string[] { + table38.AddRow(new string[] { "1", "H,20210508,,", "Ignored"}); - table39.AddRow(new string[] { + table38.AddRow(new string[] { "2", "D,Hospital 1,07777777775,10", "Successful"}); - table39.AddRow(new string[] { + table38.AddRow(new string[] { "3", "D,Hospital 1,testrecipient1@recipient.com,10", "Successful"}); - table39.AddRow(new string[] { + table38.AddRow(new string[] { "4", "T,1,,", "Ignored"}); #line 145 await testRunner.WhenAsync("I get the file \'VoucherIssue1.txt\' for Estate \'Test Estate 1\' the following file " + - "lines are returned", ((string)(null)), table39, "When "); + "lines are returned", ((string)(null)), table38, "When "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs b/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs index 6c71026..0a0b0da 100644 --- a/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs +++ b/FileProcessor.IntegrationTests/Features/ProcessTopupCSV.feature.cs @@ -111,118 +111,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table40 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table40.AddRow(new string[] { + table39.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table40.AddRow(new string[] { + table39.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table40, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table39, "Given "); #line hidden - global::Reqnroll.Table table41 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table40 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table41.AddRow(new string[] { + table40.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table41.AddRow(new string[] { + table40.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table41, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table40, "Given "); #line hidden - global::Reqnroll.Table table42 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table41 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table42.AddRow(new string[] { + table41.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table42, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table41, "Given "); #line hidden - global::Reqnroll.Table table43 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table42 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table43.AddRow(new string[] { + table42.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table43, "Given "); + "s", ((string)(null)), table42, "Given "); #line hidden - global::Reqnroll.Table table44 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table43 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table44.AddRow(new string[] { + table43.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table44, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table43, "Given "); #line hidden - global::Reqnroll.Table table45 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table44 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table45.AddRow(new string[] { + table44.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table45.AddRow(new string[] { + table44.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table45, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table44, "Given "); #line hidden - global::Reqnroll.Table table46 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table45 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table46.AddRow(new string[] { + table45.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table46.AddRow(new string[] { + table45.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table46, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table45, "And "); #line hidden - global::Reqnroll.Table table47 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table46 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table47.AddRow(new string[] { + table46.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table47.AddRow(new string[] { + table46.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table47, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table46, "Given "); #line hidden - global::Reqnroll.Table table48 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table47 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -230,7 +230,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table48.AddRow(new string[] { + table47.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -238,7 +238,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table48.AddRow(new string[] { + table47.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -247,9 +247,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table48, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table47, "When "); #line hidden - global::Reqnroll.Table table49 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table48 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -257,7 +257,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table49.AddRow(new string[] { + table48.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -266,9 +266,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table49, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table48, "When "); #line hidden - global::Reqnroll.Table table50 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table49 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -278,7 +278,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table50.AddRow(new string[] { + table49.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -289,69 +289,69 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk", "Test Estate 1"}); #line 51 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table50, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table49, "Given "); #line hidden - global::Reqnroll.Table table51 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table50 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table51.AddRow(new string[] { + table50.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table51.AddRow(new string[] { + table50.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); #line 55 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table51, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table50, "Given "); #line hidden - global::Reqnroll.Table table52 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table51 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table52.AddRow(new string[] { + table51.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); #line 60 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table52, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table51, "Given "); #line hidden - global::Reqnroll.Table table53 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table52 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table53.AddRow(new string[] { + table52.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table53.AddRow(new string[] { + table52.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); #line 64 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table53, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table52, "When "); #line hidden - global::Reqnroll.Table table54 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table53 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table54.AddRow(new string[] { + table53.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); #line 69 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table54, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table53, "Given "); #line hidden } @@ -385,37 +385,37 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table55 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table54 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table55.AddRow(new string[] { + table54.AddRow(new string[] { "H", "20210508", ""}); - table55.AddRow(new string[] { + table54.AddRow(new string[] { "D", "07777777775", "100"}); - table55.AddRow(new string[] { + table54.AddRow(new string[] { "T", "1", ""}); #line 75 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table55, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table54, "Given "); #line hidden - global::Reqnroll.Table table56 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table55 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table56.AddRow(new string[] { + table55.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 80 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table56, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table55, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -446,41 +446,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table57 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table56 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table57.AddRow(new string[] { + table56.AddRow(new string[] { "H", "20210508", ""}); - table57.AddRow(new string[] { + table56.AddRow(new string[] { "D", "07777777775", "100"}); - table57.AddRow(new string[] { + table56.AddRow(new string[] { "D", "07777777776", "200"}); - table57.AddRow(new string[] { + table56.AddRow(new string[] { "T", "2", ""}); #line 88 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table57, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table56, "Given "); #line hidden - global::Reqnroll.Table table58 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table57 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table58.AddRow(new string[] { + table57.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 94 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table58, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table57, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -511,73 +511,73 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table59 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table58 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table59.AddRow(new string[] { + table58.AddRow(new string[] { "H", "20210508", ""}); - table59.AddRow(new string[] { + table58.AddRow(new string[] { "D", "07777777775", "100"}); - table59.AddRow(new string[] { + table58.AddRow(new string[] { "T", "1", ""}); #line 102 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table59, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table58, "Given "); #line hidden - global::Reqnroll.Table table60 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table59 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table60.AddRow(new string[] { + table59.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 107 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table60, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table59, "And "); #line hidden - global::Reqnroll.Table table61 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table60 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table61.AddRow(new string[] { + table60.AddRow(new string[] { "H", "20210508", ""}); - table61.AddRow(new string[] { + table60.AddRow(new string[] { "D", "07777777776", "150"}); - table61.AddRow(new string[] { + table60.AddRow(new string[] { "D", "07777777777", "50"}); - table61.AddRow(new string[] { + table60.AddRow(new string[] { "T", "2", ""}); #line 111 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table61, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table60, "Given "); #line hidden - global::Reqnroll.Table table62 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table61 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table62.AddRow(new string[] { + table61.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 117 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table62, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table61, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -610,78 +610,78 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table63 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table62 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table63.AddRow(new string[] { + table62.AddRow(new string[] { "H", "20210508", ""}); - table63.AddRow(new string[] { + table62.AddRow(new string[] { "D", "07777777775", "100"}); - table63.AddRow(new string[] { + table62.AddRow(new string[] { "D", "07777777776", "200"}); - table63.AddRow(new string[] { + table62.AddRow(new string[] { "T", "1", ""}); #line 126 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table63, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table62, "Given "); #line hidden - global::Reqnroll.Table table64 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table63 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table64.AddRow(new string[] { + table63.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 132 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table64, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table63, "And "); #line hidden - global::Reqnroll.Table table65 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table64 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table65.AddRow(new string[] { + table64.AddRow(new string[] { "H", "20210508", ""}); - table65.AddRow(new string[] { + table64.AddRow(new string[] { "D", "07777777775", "100"}); - table65.AddRow(new string[] { + table64.AddRow(new string[] { "D", "07777777776", "200"}); - table65.AddRow(new string[] { + table64.AddRow(new string[] { "T", "1", ""}); #line 138 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table65, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup2.txt\' with the following contents", ((string)(null)), table64, "Given "); #line hidden - global::Reqnroll.Table table66 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table65 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table66.AddRow(new string[] { + table65.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 144 await testRunner.AndAsync("I upload this file for processing an error should be returned indicating the file" + - " is a duplicate", ((string)(null)), table66, "And "); + " is a duplicate", ((string)(null)), table65, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -712,77 +712,77 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table67 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table66 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table67.AddRow(new string[] { + table66.AddRow(new string[] { "H", "20210508", ""}); - table67.AddRow(new string[] { + table66.AddRow(new string[] { "D", "07777777775", "100"}); - table67.AddRow(new string[] { + table66.AddRow(new string[] { "T", "1", ""}); #line 151 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table67, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup.txt\' with the following contents", ((string)(null)), table66, "Given "); #line hidden - global::Reqnroll.Table table68 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table67 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table68.AddRow(new string[] { + table67.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "Today"}); #line 156 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table68, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table67, "And "); #line hidden #line 160 await testRunner.WhenAsync("I get the import log for estate \'Test Estate 1\' the date on the import log is \'To" + "day\'", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden - global::Reqnroll.Table table69 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table68 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3"}); - table69.AddRow(new string[] { + table68.AddRow(new string[] { "H", "20210508", ""}); - table69.AddRow(new string[] { + table68.AddRow(new string[] { "D", "07777777775", "200"}); - table69.AddRow(new string[] { + table68.AddRow(new string[] { "T", "1", ""}); #line 162 - await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table69, "Given "); + await testRunner.GivenAsync("I have a file named \'SafarcomTopup1.txt\' with the following contents", ((string)(null)), table68, "Given "); #line hidden - global::Reqnroll.Table table70 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table69 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId", "UploadDateTime"}); - table70.AddRow(new string[] { + table69.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "B2A59ABF-293D-4A6B-B81B-7007503C3476", "ABA59ABF-293D-4A6B-B81B-7007503C3476", "01/09/2021"}); #line 167 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table70, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table69, "And "); #line hidden #line 171 await testRunner.WhenAsync("I get the import log for estate \'Test Estate 1\' the date on the import log is \'01" + diff --git a/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs b/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs index d860f83..015ac0b 100644 --- a/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs +++ b/FileProcessor.IntegrationTests/Features/ProcessVoucherCSV.feature.cs @@ -113,118 +113,118 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table71 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table70 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table71.AddRow(new string[] { + table70.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table71.AddRow(new string[] { + table70.AddRow(new string[] { "fileProcessor", "File Processor REST Scope", "A scope for File Processor REST"}); #line 5 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table71, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table70, "Given "); #line hidden - global::Reqnroll.Table table72 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table71 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table72.AddRow(new string[] { + table71.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST", "Secret1", "transactionProcessor", "MerchantId, EstateId, role"}); - table72.AddRow(new string[] { + table71.AddRow(new string[] { "fileProcessor", "File Processor REST", "Secret1", "fileProcessor", ""}); #line 10 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table72, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table71, "Given "); #line hidden - global::Reqnroll.Table table73 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table72 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table73.AddRow(new string[] { + table72.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", "transactionProcessor,fileProcessor", "client_credentials"}); #line 15 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table73, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table72, "Given "); #line hidden - global::Reqnroll.Table table74 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table73 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table74.AddRow(new string[] { + table73.AddRow(new string[] { "serviceClient"}); #line 19 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table74, "Given "); + "s", ((string)(null)), table73, "Given "); #line hidden - global::Reqnroll.Table table75 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table74 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table75.AddRow(new string[] { + table74.AddRow(new string[] { "Test Estate 1"}); #line 23 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table75, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table74, "Given "); #line hidden - global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table75 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table76.AddRow(new string[] { + table75.AddRow(new string[] { "Test Estate 1", "Safaricom", "True", "True"}); - table76.AddRow(new string[] { + table75.AddRow(new string[] { "Test Estate 1", "Voucher", "True", "True"}); #line 27 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table76, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table75, "Given "); #line hidden - global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table77.AddRow(new string[] { + table76.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table77.AddRow(new string[] { + table76.AddRow(new string[] { "Test Estate 1", "Voucher"}); #line 32 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table77, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table76, "And "); #line hidden - global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table78.AddRow(new string[] { + table77.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table78.AddRow(new string[] { + table77.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); #line 37 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table78, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table77, "Given "); #line hidden - global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -232,7 +232,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table79.AddRow(new string[] { + table78.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -240,7 +240,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table79.AddRow(new string[] { + table78.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -249,9 +249,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "Voucher"}); #line 42 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table79, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table78, "When "); #line hidden - global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -259,7 +259,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table80.AddRow(new string[] { + table79.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -267,7 +267,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Fixed", "Merchant Commission", "2.50"}); - table80.AddRow(new string[] { + table79.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -276,9 +276,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "2.50"}); #line 47 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table80, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table79, "When "); #line hidden - global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -288,7 +288,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table81.AddRow(new string[] { + table80.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -299,69 +299,69 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact1@merchant1.co.uk", "Test Estate 1"}); #line 52 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table81, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table80, "Given "); #line hidden - global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table82.AddRow(new string[] { + table81.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table82.AddRow(new string[] { + table81.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); #line 56 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table82, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table81, "Given "); #line hidden - global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table83.AddRow(new string[] { + table82.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); #line 61 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table83, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table82, "Given "); #line hidden - global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table84.AddRow(new string[] { + table83.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table84.AddRow(new string[] { + table83.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); #line 65 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table84, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table83, "When "); #line hidden - global::Reqnroll.Table table85 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table85.AddRow(new string[] { + table84.AddRow(new string[] { "Deposit1", "300.00", "Today", "Test Merchant 1", "Test Estate 1"}); #line 70 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table85, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table84, "Given "); #line hidden } @@ -395,41 +395,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table86 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table85 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table86.AddRow(new string[] { + table85.AddRow(new string[] { "H", "20210508", "", ""}); - table86.AddRow(new string[] { + table85.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table86.AddRow(new string[] { + table85.AddRow(new string[] { "T", "1", "", ""}); #line 76 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table86, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table85, "Given "); #line hidden - global::Reqnroll.Table table87 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table86 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table87.AddRow(new string[] { + table86.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 81 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table87, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table86, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -460,41 +460,41 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table88 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table87 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table88.AddRow(new string[] { + table87.AddRow(new string[] { "H", "20210508", "", ""}); - table88.AddRow(new string[] { + table87.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table88.AddRow(new string[] { + table87.AddRow(new string[] { "T", "1", "", ""}); #line 89 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table88, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table87, "Given "); #line hidden - global::Reqnroll.Table table89 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table88 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table89.AddRow(new string[] { + table88.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 94 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table89, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table88, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -525,46 +525,46 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table90 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table89 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table90.AddRow(new string[] { + table89.AddRow(new string[] { "H", "20210508", "", ""}); - table90.AddRow(new string[] { + table89.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table90.AddRow(new string[] { + table89.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table90.AddRow(new string[] { + table89.AddRow(new string[] { "T", "1", "", ""}); #line 102 - await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table90, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue.txt\' with the following contents", ((string)(null)), table89, "Given "); #line hidden - global::Reqnroll.Table table91 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table90 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table91.AddRow(new string[] { + table90.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 108 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table91, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table90, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -595,87 +595,87 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table92 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table91 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table92.AddRow(new string[] { + table91.AddRow(new string[] { "H", "20210508", "", ""}); - table92.AddRow(new string[] { + table91.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table92.AddRow(new string[] { + table91.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "10"}); - table92.AddRow(new string[] { + table91.AddRow(new string[] { "T", "1", "", ""}); #line 116 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table92, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table91, "Given "); #line hidden - global::Reqnroll.Table table93 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table92 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table93.AddRow(new string[] { + table92.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 122 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table93, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table92, "And "); #line hidden - global::Reqnroll.Table table94 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table93 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table94.AddRow(new string[] { + table93.AddRow(new string[] { "H", "20210508", "", ""}); - table94.AddRow(new string[] { + table93.AddRow(new string[] { "D", "Hospital 1", "07777777775", "20"}); - table94.AddRow(new string[] { + table93.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table94.AddRow(new string[] { + table93.AddRow(new string[] { "T", "1", "", ""}); #line 126 - await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table94, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table93, "Given "); #line hidden - global::Reqnroll.Table table95 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table94 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table95.AddRow(new string[] { + table94.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 132 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table95, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table94, "And "); #line hidden } await this.ScenarioCleanupAsync(); @@ -708,88 +708,88 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table96 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table95 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table96.AddRow(new string[] { + table95.AddRow(new string[] { "H", "20210508", "", ""}); - table96.AddRow(new string[] { + table95.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table96.AddRow(new string[] { + table95.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table96.AddRow(new string[] { + table95.AddRow(new string[] { "T", "1", "", ""}); #line 141 - await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table96, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue1.txt\' with the following contents", ((string)(null)), table95, "Given "); #line hidden - global::Reqnroll.Table table97 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table96 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table97.AddRow(new string[] { + table96.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 147 - await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table97, "And "); + await testRunner.AndAsync("I upload this file for processing", ((string)(null)), table96, "And "); #line hidden - global::Reqnroll.Table table98 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table97 = new global::Reqnroll.Table(new string[] { "Column1", "Column2", "Column3", "Column4"}); - table98.AddRow(new string[] { + table97.AddRow(new string[] { "H", "20210508", "", ""}); - table98.AddRow(new string[] { + table97.AddRow(new string[] { "D", "Hospital 1", "07777777775", "10"}); - table98.AddRow(new string[] { + table97.AddRow(new string[] { "D", "Hospital 1", "testrecipient1@recipient.com", "20"}); - table98.AddRow(new string[] { + table97.AddRow(new string[] { "T", "1", "", ""}); #line 153 - await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table98, "Given "); + await testRunner.GivenAsync("I have a file named \'VoucherIssue2.txt\' with the following contents", ((string)(null)), table97, "Given "); #line hidden - global::Reqnroll.Table table99 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table98 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "FileProfileId", "UserId"}); - table99.AddRow(new string[] { + table98.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8806EDBC-3ED6-406B-9E5F-A9078356BE99", "ABA59ABF-293D-4A6B-B81B-7007503C3476"}); #line 160 await testRunner.AndAsync("I upload this file for processing an error should be returned indicating the file" + - " is a duplicate", ((string)(null)), table99, "And "); + " is a duplicate", ((string)(null)), table98, "And "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/FileProcessor/Common/Extensions.cs b/FileProcessor/Common/Extensions.cs index 08f14f1..8d125a0 100644 --- a/FileProcessor/Common/Extensions.cs +++ b/FileProcessor/Common/Extensions.cs @@ -74,6 +74,12 @@ public static void PreWarm(this IApplicationBuilder applicationBuilder){ fileSystem.Directory.CreateDirectory(temporaryFileLocation); Logger.LogInformation($"Created TemporaryFileLocation at [{temporaryFileLocation}]"); + Result seedResult = fileProcessorManager.EnsureSeededFileProfiles(CancellationToken.None).Result; + if (seedResult.IsFailed) { + Logger.LogWarning($"Error seeding file profiles {seedResult.Message}"); + throw new ApplicationStartupException(seedResult.Message); + } + Result> fileProfilesResult = fileProcessorManager.GetAllFileProfiles(CancellationToken.None).Result; if (fileProfilesResult.IsFailed) { From ce2fcb8e07c0ba820d0042f9fb7822f947c67c75 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sat, 11 Jul 2026 06:44:52 +0100 Subject: [PATCH 3/3] Refactor file profile uniqueness validation --- .../FileProfileAggregate.cs | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs index 6cc52e8..2c1c47c 100644 --- a/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs +++ b/FileProcessor.FileProfileAggregate/FileProfileAggregate.cs @@ -281,18 +281,42 @@ private static Result ValidateUpdateUniqueness(this FileProfileAggregate aggrega String proposedName = request.Name?.Trim(); String proposedRequestType = request.RequestType?.Trim(); - if (request.Name != null && + Result nameValidationResult = aggregate.ValidateUpdatedNameIsUnique(fileProfileId, currentProfile, request.Name, proposedName); + if (nameValidationResult.IsFailed) + { + return nameValidationResult; + } + + return aggregate.ValidateUpdatedRequestTypeIsUnique(fileProfileId, currentProfile, request.RequestType, proposedRequestType); + } + + private static Result ValidateUpdatedNameIsUnique(this FileProfileAggregate aggregate, + Guid fileProfileId, + FileProfileState currentProfile, + String rawName, + String proposedName) + { + if (rawName != null && Comparer.Equals(currentProfile.Name, proposedName) == false && aggregate.FileProfiles.Values.Any(profile => profile.FileProfileId != fileProfileId && profile.IsArchived == false && Comparer.Equals(profile.Name, proposedName))) { - return Result.Invalid($"A file profile with name [{request.Name}] already exists"); + return Result.Invalid($"A file profile with name [{rawName}] already exists"); } - if (request.RequestType != null && + return Result.Success(); + } + + private static Result ValidateUpdatedRequestTypeIsUnique(this FileProfileAggregate aggregate, + Guid fileProfileId, + FileProfileState currentProfile, + String rawRequestType, + String proposedRequestType) + { + if (rawRequestType != null && Comparer.Equals(currentProfile.RequestType, proposedRequestType) == false && aggregate.FileProfiles.Values.Any(profile => profile.FileProfileId != fileProfileId && profile.IsArchived == false && Comparer.Equals(profile.RequestType, proposedRequestType))) { - return Result.Invalid($"A file profile with request type [{request.RequestType}] already exists"); + return Result.Invalid($"A file profile with request type [{rawRequestType}] already exists"); } return Result.Success();