From 818a1831d9b018ad70db41658095cb3d22e40e72 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 26 Aug 2025 16:04:02 +0100 Subject: [PATCH 1/4] handle import log created duplicates --- .../ContractEventTests.cs | 48 ++++++++++++------- ...TransactionProcessorReadModelRepository.cs | 2 +- TransactionProcessor.Testing/TestData.cs | 14 ++++++ .../TransactionProcessor.Testing.csproj | 1 + TransactionProcessor/appsettings.json | 8 ++-- 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/TransactionProcessor.DatabaseTests/ContractEventTests.cs b/TransactionProcessor.DatabaseTests/ContractEventTests.cs index 41826357..7675bb9e 100644 --- a/TransactionProcessor.DatabaseTests/ContractEventTests.cs +++ b/TransactionProcessor.DatabaseTests/ContractEventTests.cs @@ -11,10 +11,8 @@ using TransactionProcessor.Repository; using TransactionProcessor.Testing; -namespace TransactionProcessor.DatabaseTests -{ - public class ContractEventTests : BaseTest - { +namespace TransactionProcessor.DatabaseTests { + public class ContractEventTests : BaseTest { [Fact] public async Task AddContract_ContractIsAdded() { Result result = await this.Repository.AddContract(TestData.DomainEvents.ContractCreatedEvent, CancellationToken.None); @@ -34,46 +32,60 @@ public async Task AddContract_ContractIsAdded_EventReplayHandled() { } [Fact] - public async Task AddContractProduct_ContractProductIsAdded() - { + public async Task AddContractProduct_ContractProductIsAdded() { Result result = await this.Repository.AddContractProduct(TestData.DomainEvents.FixedValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); EstateManagementContext context = this.GetContext(); - ContractProduct? fixedContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ContractId && - c.ContractProductId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ProductId); + ContractProduct? fixedContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ContractId && c.ContractProductId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ProductId); fixedContractProduct.ShouldNotBeNull(); result = await this.Repository.AddContractProduct(TestData.DomainEvents.VariableValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); context = this.GetContext(); - ContractProduct? variableContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ContractId && - c.ContractProductId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ProductId); + ContractProduct? variableContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ContractId && c.ContractProductId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ProductId); variableContractProduct.ShouldNotBeNull(); } [Fact] - public async Task AddContractProduct_ContractProductIsAdded_EventReplayHandled() - { + public async Task AddContractProduct_ContractProductIsAdded_EventReplayHandled() { Result result = await this.Repository.AddContractProduct(TestData.DomainEvents.FixedValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); EstateManagementContext context = this.GetContext(); - ContractProduct? fixedContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ContractId && - c.ContractProductId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ProductId); + ContractProduct? fixedContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ContractId && c.ContractProductId == TestData.DomainEvents.FixedValueProductAddedToContractEvent.ProductId); fixedContractProduct.ShouldNotBeNull(); result = await this.Repository.AddContractProduct(TestData.DomainEvents.VariableValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); context = this.GetContext(); - ContractProduct? variableContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ContractId && - c.ContractProductId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ProductId); + ContractProduct? variableContractProduct = await context.ContractProducts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ContractId && c.ContractProductId == TestData.DomainEvents.VariableValueProductAddedToContractEvent.ProductId); variableContractProduct.ShouldNotBeNull(); result = await this.Repository.AddContractProduct(TestData.DomainEvents.FixedValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - + result = await this.Repository.AddContractProduct(TestData.DomainEvents.VariableValueProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - + + } + } + + public class FileEventTests : BaseTest { + [Fact] + public async Task AddFileImportLog_FileImportLogIsAdded() { + Result result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + var fileImportLog = await context.FileImportLogs.SingleOrDefaultAsync(f => f.FileImportLogId == TestData.DomainEvents.ImportLogCreatedEvent.FileImportLogId); + fileImportLog.ShouldNotBeNull(); + } + + [Fact] + public async Task AddFileImportLog_FileImportLogIsAdded_EventReplayHandled() { + Result result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + + result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); } } } diff --git a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs index 521442f6..bda3b78c 100644 --- a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs +++ b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs @@ -1837,7 +1837,7 @@ public async Task AddFileImportLog(ImportLogCreatedEvent domainEvent, await context.FileImportLogs.AddAsync(fileImportLog, cancellationToken); - return await context.SaveChangesAsync(cancellationToken); + return await context.SaveChangesWithDuplicateHandling(cancellationToken); } public async Task AddFileLineToFile(FileLineAddedEvent domainEvent, diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index c3ed4864..7e2dfb54 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -1,4 +1,5 @@ using System.Text; +using FileProcessor.FileImportLog.DomainEvents; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using Shared.EventStore.ProjectionEngine; @@ -451,6 +452,14 @@ public class TestData public static Decimal TransactionAmount = 1000.00m; + public static Guid FileImportLogId = Guid.Parse("2250C12E-B617-4012-8145-33ADD6E8E848"); + public static Guid FileId = Guid.Parse("3F2A50DE-FD72-4D50-87B0-8F97D1688EFD"); + public static Guid FileProfileId = Guid.Parse("E1FF239B-908B-497D-AB5B-6BA3148CE80D"); + public static String FileName = "TestFile1.csv"; + public static String FilePath = "/uploads/TestFile1.csv"; + public static DateTime FileUploadedDateTime = new DateTime(2023, 1, 1, 10, 0, 0); + public static DateTime ImportLogDateTime = new DateTime(2023, 1, 1, 10, 5, 0); + public static OperatorResponse OperatorResponse => new OperatorResponse { @@ -2764,6 +2773,11 @@ public static class DomainEvents { public static OperatorDomainEvents.OperatorNameUpdatedEvent OperatorNameUpdatedEvent => new(TestData.OperatorId, TestData.EstateId, TestData.OperatorName2); public static OperatorDomainEvents.OperatorRequireCustomMerchantNumberChangedEvent OperatorRequireCustomMerchantNumberChangedEvent => new(TestData.OperatorId, TestData.EstateId, TestData.RequireCustomMerchantNumberFalse); public static OperatorDomainEvents.OperatorRequireCustomTerminalNumberChangedEvent OperatorRequireCustomTerminalNumberChangedEvent => new(TestData.OperatorId, TestData.EstateId, TestData.RequireCustomTerminalNumberFalse); + + public static ImportLogCreatedEvent ImportLogCreatedEvent => new ImportLogCreatedEvent(FileImportLogId, EstateId, ImportLogDateTime); + public static FileAddedToImportLogEvent FileAddedToImportLogEvent => new FileAddedToImportLogEvent(FileImportLogId, FileId, EstateId, MerchantId, EstateSecurityUserId, + FileProfileId, FileName, FilePath, FileUploadedDateTime ); + } } diff --git a/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj b/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj index 3eb0c81a..7840f717 100644 --- a/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj +++ b/TransactionProcessor.Testing/TransactionProcessor.Testing.csproj @@ -7,6 +7,7 @@ + diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index 49f2424d..6f023a18 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -186,7 +186,7 @@ } }, "ConnectionStrings": { - "TransactionProcessorReadModel": "server=192.168.1.167;user id=sa;password=Sc0tland;database=TransactionProcessorReadModel;Encrypt=false" + "TransactionProcessorReadModel": "server=192.168.1.163;user id=sa;password=Sc0tland;database=TransactionProcessorReadModel;Encrypt=false" }, "SecurityConfiguration": { "ApiName": "transactionProcessor" @@ -194,7 +194,7 @@ }, "OperatorConfiguration": { "Safaricom": { - "Url": "http://192.168.1.167:9000/api/safaricom", + "Url": "http://192.168.1.163:9000/api/safaricom", "LoginId": "D-S136", "MSISDN": "700945625", "Pin": "0322", @@ -202,13 +202,13 @@ "ExtCode": "SA" }, "PataPawaPostPay": { - "Url": "http://192.168.1.167:9000/PataPawaPostPayService/basichttp", + "Url": "http://192.168.1.163:9000/PataPawaPostPayService/basichttp", "Username": "testuser1", "Password": "password1", "ApiLogonRequired": true }, "PataPawaPrePay": { - "Url": "http://192.168.1.167:9000/api/patapawaprepay", + "Url": "http://192.168.1.163:9000/api/patapawaprepay", "Username": "operatora", "Password": "1234567898", "ApiLogonRequired": true From 428c40fbb7ab18f6754709c496ae056c0316d541 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 26 Aug 2025 17:43:47 +0100 Subject: [PATCH 2/4] :| --- .../ContractEventTests.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/TransactionProcessor.DatabaseTests/ContractEventTests.cs b/TransactionProcessor.DatabaseTests/ContractEventTests.cs index c359a27c..20b3b1aa 100644 --- a/TransactionProcessor.DatabaseTests/ContractEventTests.cs +++ b/TransactionProcessor.DatabaseTests/ContractEventTests.cs @@ -110,5 +110,25 @@ public async Task AddContractProductTransactionFee_ContractIsAdded_EventReplayHa result = await this.Repository.AddContractProductTransactionFee(TestData.DomainEvents.TransactionFeeForProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); } + + [Fact] + public async Task AddFileImportLog_FileImportLogIsAdded() + { + Result result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + var fileImportLog = await context.FileImportLogs.SingleOrDefaultAsync(f => f.FileImportLogId == TestData.DomainEvents.ImportLogCreatedEvent.FileImportLogId); + fileImportLog.ShouldNotBeNull(); + } + + [Fact] + public async Task AddFileImportLog_FileImportLogIsAdded_EventReplayHandled() + { + Result result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + + result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } } } From 236bd43e5f798066d361910fd3ba9fcad4108c86 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 26 Aug 2025 17:45:00 +0100 Subject: [PATCH 3/4] :| --- TransactionProcessor.DatabaseTests/ContractEventTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TransactionProcessor.DatabaseTests/ContractEventTests.cs b/TransactionProcessor.DatabaseTests/ContractEventTests.cs index f17b673c..6f30ea6d 100644 --- a/TransactionProcessor.DatabaseTests/ContractEventTests.cs +++ b/TransactionProcessor.DatabaseTests/ContractEventTests.cs @@ -110,9 +110,7 @@ public async Task AddContractProductTransactionFee_ContractIsAdded_EventReplayHa result = await this.Repository.AddContractProductTransactionFee(TestData.DomainEvents.TransactionFeeForProductAddedToContractEvent, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); } - } - - public class FileEventTests : BaseTest { + [Fact] public async Task AddFileImportLog_FileImportLogIsAdded() { From 50e6c273a7f28cc11a91ce7bd5e19ac720dc63cb Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 26 Aug 2025 18:01:37 +0100 Subject: [PATCH 4/4] :| merge issue --- TransactionProcessor.DatabaseTests/ContractEventTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TransactionProcessor.DatabaseTests/ContractEventTests.cs b/TransactionProcessor.DatabaseTests/ContractEventTests.cs index 6f30ea6d..2d642d00 100644 --- a/TransactionProcessor.DatabaseTests/ContractEventTests.cs +++ b/TransactionProcessor.DatabaseTests/ContractEventTests.cs @@ -128,6 +128,7 @@ public async Task AddFileImportLog_FileImportLogIsAdded_EventReplayHandled() result.IsSuccess.ShouldBeTrue(); result = await this.Repository.AddFileImportLog(TestData.DomainEvents.ImportLogCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); } } }