Skip to content

Commit 0f49e7e

Browse files
Merge pull request #221 from TransactionProcessing/task/#214_complete_todo
validate reference data
2 parents 329cd56 + 5d29eb2 commit 0f49e7e

14 files changed

Lines changed: 706 additions & 25 deletions

File tree

CallbackHander.Testing/TestData.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System;
21
using CallbackHandlers.Models;
2+
using System;
3+
using System.Collections.Generic;
34
using Xunit;
45

56
namespace CallbackHander.Testing;
67

78
using CallbackHandler.BusinessLogic.Requests;
89
using CallbackHandler.CallbackMessageAggregate;
10+
using SecurityService.DataTransferObjects.Responses;
911

1012
public class TestData
1113
{
@@ -22,7 +24,27 @@ public class TestData
2224
public static String Reference = "640E863C23E244BDB9717C92733FFD4C-9D20A3961CF645EDAA7BDD436318BA29";
2325
public static Guid EstateReference = Guid.Parse("640E863C-23E2-44BD-B971-7C92733FFD4C");
2426
public static Guid MerchantReference = Guid.Parse("9D20A396-1CF6-45ED-AA7B-DD436318BA29");
27+
public static TokenResponse TokenResponse()
28+
{
29+
return SecurityService.DataTransferObjects.Responses.TokenResponse.Create("AccessToken", string.Empty, 100);
30+
}
2531

32+
public static IReadOnlyDictionary<String, String> DefaultAppSettings =>
33+
new Dictionary<String, String>
34+
{
35+
["AppSettings:ClientId"] = "clientId",
36+
["AppSettings:ClientSecret"] = "clientSecret",
37+
["AppSettings:UseConnectionStringConfig"] = "false",
38+
["EventStoreSettings:ConnectionString"] = "esdb://127.0.0.1:2113",
39+
["SecurityConfiguration:Authority"] = "https://127.0.0.1",
40+
["AppSettings:EstateManagementApi"] = "http://127.0.0.1",
41+
["AppSettings:SecurityService"] = "http://127.0.0.1",
42+
["AppSettings:ContractProductFeeCacheExpiryInHours"] = "",
43+
["AppSettings:ContractProductFeeCacheEnabled"] = "",
44+
["ConnectionStrings:HealthCheck"] = "HealthCheck",
45+
["ConnectionStrings:EstateReportingReadModel"] = "",
46+
["ConnectionStrings:TransactionProcessorReadModel"] = ""
47+
};
2648
public static CallbackCommands.RecordCallbackCommand RecordCallbackCommand =>
2749
new (TestData.CallbackId,
2850
TestData.CallbackMessage,
@@ -47,6 +69,22 @@ public class TestData
4769
TestData.TypeString,
4870
"reference");
4971

72+
public static CallbackCommands.RecordCallbackCommand RecordCallbackCommandInvalidEstateIdInReference =>
73+
new(TestData.CallbackId,
74+
TestData.CallbackMessage,
75+
TestData.Destinations,
76+
(MessageFormat)TestData.MessageFormat,
77+
TestData.TypeString,
78+
"reference-71AA10137C9341C793EDDDE89C549455");
79+
80+
public static CallbackCommands.RecordCallbackCommand RecordCallbackCommandInvalidMerchantIdInReference =>
81+
new(TestData.CallbackId,
82+
TestData.CallbackMessage,
83+
TestData.Destinations,
84+
(MessageFormat)TestData.MessageFormat,
85+
TestData.TypeString,
86+
"71AA10137C9341C793EDDDE89C549455-reference");
87+
5088
public static CallbackQueries.GetCallbackQuery GetCallbackQuery =>
5189
new CallbackQueries.GetCallbackQuery(TestData.CallbackId);
5290

CallbackHandler.BusinessLogic.Tests/Mediator/MediatorTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace CallbackHandler.BusinessLogic.Tests.Mediator;
1717
using BusinessLogic.Services;
1818
using Lamar;
1919
using Microsoft.Extensions.DependencyInjection;
20+
using Shared.General;
2021

2122
public class MediatorTests
2223
{
@@ -38,7 +39,10 @@ public async Task Mediator_Send_RequestHandled()
3839

3940
ServiceRegistry services = new();
4041
Startup s = new(hostingEnvironment.Object);
41-
Startup.Configuration = this.SetupMemoryConfiguration();
42+
IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build();
43+
//ConfigurationReader.Initialise(configurationRoot);
44+
45+
Startup.Configuration = configurationRoot;
4246

4347
this.AddTestRegistrations(services, hostingEnvironment.Object);
4448
s.ConfigureContainer(services);

CallbackHandler.BusinessLogic.Tests/Services/CallbackDomainServiceTests.cs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,56 @@
1+
using SecurityService.Client;
2+
using SecurityService.DataTransferObjects.Responses;
3+
using Shared.General;
4+
using SimpleResults;
15
using System;
26
using System.Collections.Generic;
37
using System.Linq;
48
using System.Text;
59
using System.Threading.Tasks;
6-
using SimpleResults;
10+
using Shared.Logger;
11+
using TransactionProcessor.Client;
712

813
namespace CallbackHandler.BusinessLogic.Tests.Services;
914

10-
using System.Threading;
1115
using BusinessLogic.Services;
1216
using CallbackHander.Testing;
1317
using CallbackHandlers.Models;
1418
using CallbackMessageAggregate;
19+
using Microsoft.Extensions.Configuration;
1520
using Moq;
1621
using Shared.DomainDrivenDesign.EventSourcing;
1722
using Shared.EventStore.Aggregate;
1823
using Shouldly;
24+
using System.Threading;
1925
using Xunit;
2026

2127
public class CallbackDomainServiceTests
2228
{
2329
private readonly ICallbackDomainService DomainService;
2430

2531
private readonly Mock<IAggregateRepository<CallbackMessageAggregate, DomainEvent>> AggregateRepository;
32+
private readonly Mock<ISecurityServiceClient> SecurityServiceClient;
33+
private readonly Mock<ITransactionProcessorClient> TransactionProcessorClient;
2634
public CallbackDomainServiceTests() {
2735
this.AggregateRepository = new Mock<IAggregateRepository<CallbackMessageAggregate, DomainEvent>>();
28-
this.DomainService = new CallbackDomainService(this.AggregateRepository.Object);
36+
this.SecurityServiceClient = new Mock<ISecurityServiceClient>();
37+
this.TransactionProcessorClient = new Mock<ITransactionProcessorClient>();
38+
this.DomainService = new CallbackDomainService(this.AggregateRepository.Object, this.SecurityServiceClient.Object,
39+
this.TransactionProcessorClient.Object);
2940
this.AggregateRepository.Setup(a => a.GetLatestVersion(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
3041
.ReturnsAsync(TestData.EmptyCallbackMessageAggregate());
3142
this.AggregateRepository.Setup(a => a.SaveChanges(It.IsAny<CallbackMessageAggregate>(), It.IsAny<CancellationToken>()))
3243
.ReturnsAsync(Result.Success);
44+
this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Success(TestData.TokenResponse()));
45+
this.TransactionProcessorClient.Setup(t => t.GetMerchant(It.IsAny<String>(), It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Success());
46+
47+
IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build();
48+
ConfigurationReader.Initialise(configurationRoot);
49+
50+
Logger.Initialise(NullLogger.Instance);
3351
}
3452

53+
3554
[Fact]
3655
public async Task CallbackDomainService_RecordCallback_CallbackRecorded() {
3756
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommand, CancellationToken.None);
@@ -49,4 +68,45 @@ public async Task CallbackDomainService_RecordCallback_InvalidReference_ResultFa
4968
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommandInvalidReference, CancellationToken.None);
5069
result.IsFailed.ShouldBeTrue();
5170
}
71+
72+
[Fact]
73+
public async Task CallbackDomainService_RecordCallback_EstateIdNotValidGuid_ResultFailed()
74+
{
75+
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommandInvalidEstateIdInReference, CancellationToken.None);
76+
result.IsFailed.ShouldBeTrue();
77+
}
78+
79+
[Fact]
80+
public async Task CallbackDomainService_RecordCallback_MerchantIdNotValidGuid_ResultFailed()
81+
{
82+
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommandInvalidMerchantIdInReference, CancellationToken.None);
83+
result.IsFailed.ShouldBeTrue();
84+
}
85+
86+
[Fact]
87+
public async Task CallbackDomainService_RecordCallback_GetTokenFailed_ResultFailed()
88+
{
89+
this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<CancellationToken>()))
90+
.ReturnsAsync(Result.Failure());
91+
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommand, CancellationToken.None);
92+
result.IsFailed.ShouldBeTrue();
93+
}
94+
95+
[Fact]
96+
public async Task CallbackDomainService_RecordCallback_GetMerchantFailed_ResultFailed()
97+
{
98+
this.TransactionProcessorClient.Setup(t => t.GetMerchant(It.IsAny<String>(), It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>())).ReturnsAsync(Result.Failure());
99+
100+
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommand, CancellationToken.None);
101+
result.IsFailed.ShouldBeTrue();
102+
}
103+
104+
[Fact]
105+
public async Task CallbackDomainService_RecordCallback_SaveFailed_ResultFailed()
106+
{
107+
this.AggregateRepository.Setup(a => a.SaveChanges(It.IsAny<CallbackMessageAggregate>(), It.IsAny<CancellationToken>()))
108+
.ReturnsAsync(Result.Failure);
109+
Result result = await this.DomainService.RecordCallback(TestData.RecordCallbackCommand, CancellationToken.None);
110+
result.IsFailed.ShouldBeTrue();
111+
}
52112
}

CallbackHandler.BusinessLogic/CallbackHandler.BusinessLogic.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
</PropertyGroup>
66
<ItemGroup>
77
<PackageReference Include="MediatR" Version="14.0.0" />
8+
<PackageReference Include="SecurityService.Client" Version="2025.12.2" />
89
<PackageReference Include="Shared" Version="2025.12.1" />
910
<PackageReference Include="Shared.DomainDrivenDesign" Version="2025.12.1" />
1011
<PackageReference Include="Shared.EventStore" Version="2025.12.1" />
12+
<PackageReference Include="TransactionProcessor.Client" Version="2025.12.1" />
1113
</ItemGroup>
1214
<ItemGroup>
1315
<ProjectReference Include="..\CallbackHandler.CallbackMessageAggregate\CallbackHandler.CallbackMessageAggregate.csproj" />

CallbackHandler.BusinessLogic/Services/CallbackDomainService.cs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
using Shared.EventStore.Helpers;
1+
using SecurityService.Client;
2+
using Shared.EventStore.Helpers;
3+
using Shared.Logger;
24
using SimpleResults;
5+
using TransactionProcessor.Client;
6+
using TransactionProcessor.DataTransferObjects.Responses.Merchant;
37

48
namespace CallbackHandler.BusinessLogic.Services;
59

6-
using Requests;
710
using CallbackMessageAggregate;
11+
using Requests;
12+
using SecurityService.DataTransferObjects.Responses;
813
using Shared.DomainDrivenDesign.EventSourcing;
914
using Shared.EventStore.Aggregate;
15+
using Shared.General;
16+
using Shared.Results;
1017
using System;
1118
using System.Threading;
1219
using System.Threading.Tasks;
@@ -20,9 +27,15 @@ Task<Result> RecordCallback(CallbackCommands.RecordCallbackCommand command,
2027
public class CallbackDomainService : ICallbackDomainService
2128
{
2229
private readonly IAggregateRepository<CallbackMessageAggregate, DomainEvent> AggregateRepository;
30+
private readonly ISecurityServiceClient SecurityServiceClient;
31+
private readonly ITransactionProcessorClient TransactionProcessorClient;
2332

24-
public CallbackDomainService(IAggregateRepository<CallbackMessageAggregate, DomainEvent> aggregateRepository) {
33+
public CallbackDomainService(IAggregateRepository<CallbackMessageAggregate, DomainEvent> aggregateRepository,
34+
ISecurityServiceClient securityServiceClient,
35+
ITransactionProcessorClient transactionProcessorClient) {
2536
this.AggregateRepository = aggregateRepository;
37+
this.SecurityServiceClient = securityServiceClient;
38+
this.TransactionProcessorClient = transactionProcessorClient;
2639
}
2740

2841
public async Task<Result> RecordCallback(CallbackCommands.RecordCallbackCommand command,
@@ -43,8 +56,21 @@ public async Task<Result> RecordCallback(CallbackCommands.RecordCallbackCommand
4356
String estateReference = referenceData[0];
4457
String merchantReference = referenceData[1];
4558

46-
// TODO: Validate the reference data
47-
59+
// Validate the reference data
60+
// Validate the estate and merchant references are valid GUIDs
61+
if (!Guid.TryParse(estateReference, out Guid estateId) || !Guid.TryParse(merchantReference, out Guid merchantId)) {
62+
return Result.Invalid("Estate or Merchant reference is not a valid GUID.");
63+
}
64+
65+
Result<TokenResponse> getTokenResult = await Helpers.GetToken(this.TokenResponse, this.SecurityServiceClient, cancellationToken);
66+
if (getTokenResult.IsFailed)
67+
return ResultHelpers.CreateFailure(getTokenResult);
68+
this.TokenResponse = getTokenResult.Data;
69+
70+
Result<MerchantResponse> result = await this.TransactionProcessorClient.GetMerchant(this.TokenResponse.AccessToken, estateId, merchantId, cancellationToken);
71+
if (result.IsFailed)
72+
return ResultHelpers.CreateFailure(result);
73+
4874
Result<CallbackMessageAggregate> getResult = await this.AggregateRepository.GetLatestVersion(command.CallbackId, cancellationToken);
4975
Result<CallbackMessageAggregate> callbackMessageAggregateResult =
5076
DomainServiceHelper.HandleGetAggregateResult(getResult, command.CallbackId, false);
@@ -56,4 +82,40 @@ public async Task<Result> RecordCallback(CallbackCommands.RecordCallbackCommand
5682
return stateResult;
5783
return await this.AggregateRepository.SaveChanges(aggregate, cancellationToken);
5884
}
85+
86+
private TokenResponse TokenResponse;
87+
}
88+
89+
public static class Helpers {
90+
public static async Task<Result<TokenResponse>> GetToken(TokenResponse currentToken, ISecurityServiceClient securityServiceClient, CancellationToken cancellationToken)
91+
{
92+
// Get a token to talk to the estate service
93+
String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId");
94+
String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret");
95+
Logger.LogDebug($"Client Id is {clientId}");
96+
Logger.LogDebug($"Client Secret is {clientSecret}");
97+
98+
if (currentToken == null)
99+
{
100+
Result<TokenResponse> tokenResult = await securityServiceClient.GetToken(clientId, clientSecret, cancellationToken);
101+
if (tokenResult.IsFailed)
102+
return ResultHelpers.CreateFailure(tokenResult);
103+
TokenResponse token = tokenResult.Data;
104+
Logger.LogDebug($"Token is {token.AccessToken}");
105+
return Result.Success(token);
106+
}
107+
108+
if (currentToken.Expires.UtcDateTime.Subtract(DateTime.UtcNow) < TimeSpan.FromMinutes(2))
109+
{
110+
Logger.LogDebug($"Token is about to expire at {currentToken.Expires.DateTime:O}");
111+
Result<TokenResponse> tokenResult = await securityServiceClient.GetToken(clientId, clientSecret, cancellationToken);
112+
if (tokenResult.IsFailed)
113+
return ResultHelpers.CreateFailure(tokenResult);
114+
TokenResponse token = tokenResult.Data;
115+
Logger.LogDebug($"Token is {token.AccessToken}");
116+
return Result.Success(token);
117+
}
118+
119+
return Result.Success(currentToken);
120+
}
59121
}

CallbackHandler.IntegrationTests/CallbackHandler.IntegrationTests.csproj

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12+
<PackageReference Include="EventStoreProjections" Version="2023.12.3" />
1213
<PackageReference Include="Reqnroll.Tools.MsBuild.Generation" Version="3.2.1" />
1314
<PackageReference Include="Reqnroll" Version="3.2.1" />
1415
<PackageReference Include="Reqnroll.NUnit" Version="3.2.1" />
1516
<PackageReference Include="NUnit" Version="4.4.0" />
1617
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
1718
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
19+
<PackageReference Include="SecurityService.Client" Version="2025.12.2" />
20+
<PackageReference Include="SecurityService.IntegrationTesting.Helpers" Version="2025.12.2" />
1821
<PackageReference Include="Shared" Version="2025.12.1" />
1922
<PackageReference Include="Shared.IntegrationTesting" Version="2025.12.1" />
23+
<PackageReference Include="TransactionProcessor.Client" Version="2025.12.1" />
24+
<PackageReference Include="TransactionProcessor.IntegrationTesting.Helpers" Version="2025.12.1" />
2025
</ItemGroup>
2126

2227
<ItemGroup>
@@ -27,4 +32,31 @@
2732
<ProjectReference Include="..\CallbackHandler.DataTransferObjects\CallbackHandler.DataTransferObjects.csproj" />
2833
</ItemGroup>
2934

35+
<ItemGroup>
36+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\CallbackHandlerEnricher.js">
37+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
38+
</Content>
39+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\EstateAggregator.js">
40+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
41+
</Content>
42+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\EstateManagementSubscriptionStreamBuilder.js">
43+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
44+
</Content>
45+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\FileProcessorSubscriptionStreamBuilder.js">
46+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
47+
</Content>
48+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\MerchantAggregator.js">
49+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
50+
</Content>
51+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\MerchantBalanceAggregator.js">
52+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
53+
</Content>
54+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\MerchantBalanceProjection.js">
55+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
56+
</Content>
57+
<Content Update="C:\Users\stuar\.nuget\packages\eventstoreprojections\2023.12.3\contentFiles\any\net6.0\projections\continuous\TransactionProcessorSubscriptionStreamBuilder.js">
58+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
59+
</Content>
60+
</ItemGroup>
61+
3062
</Project>

0 commit comments

Comments
 (0)