Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit 3acca93

Browse files
feat: MeshHandshake function and tests
1 parent 12bb95d commit 3acca93

6 files changed

Lines changed: 182 additions & 3 deletions

File tree

src/ServiceLayer.API/ServiceLayer.API.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
1515
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
1616
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
17+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.0" />
1718
</ItemGroup>
1819
<ItemGroup>
1920
<None Update="host.json">

src/ServiceLayer.Mesh/Configuration/AppConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ public class AppConfiguration :
44
IFileDiscoveryFunctionConfiguration,
55
IFileExtractFunctionConfiguration,
66
IFileExtractQueueClientConfiguration,
7-
IFileTransformQueueClientConfiguration
7+
IFileTransformQueueClientConfiguration,
8+
IMeshHandshakeFunctionConfiguration
89
{
910
public string NbssMeshMailboxId => GetRequired("NbssMailboxId");
1011

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
6+
namespace ServiceLayer.Mesh.Configuration
7+
{
8+
public interface IMeshHandshakeFunctionConfiguration
9+
{
10+
string NbssMeshMailboxId { get; }
11+
}
12+
}
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,44 @@
1-
namespace ServiceLayer.Mesh.Functions;
1+
using Azure;
2+
using Microsoft.Azure.Functions.Worker;
3+
using Microsoft.Extensions.Logging;
4+
using NHS.MESH.Client.Contracts.Services;
5+
using ServiceLayer.Mesh.Configuration;
26

3-
public class MeshHandshakeFunction
7+
namespace ServiceLayer.Mesh.Functions
48
{
9+
public class MeshHandshakeFunction(
10+
ILogger<MeshHandshakeFunction> logger,
11+
IMeshOperationService meshOperationService,
12+
IMeshHandshakeFunctionConfiguration configuration)
13+
{
14+
[Function("MeshHandshakeFunction")]
15+
public async Task Run([TimerTrigger("%MeshHandshakeTimerExpression%")] TimerInfo myTimer)
16+
{
17+
logger.LogInformation("{FunctionName} started at: {Time}", nameof(MeshHandshakeFunction), DateTime.UtcNow);
518

19+
try
20+
{
21+
var response = await meshOperationService.MeshHandshakeAsync(configuration.NbssMeshMailboxId);
22+
23+
if (response.IsSuccessful)
24+
{
25+
logger.LogInformation("Mesh handshake completed successfully for mailbox {MailboxId} at {Time}. Status: {Status}",
26+
response.Response.MailboxId, DateTime.UtcNow, response.IsSuccessful);
27+
}
28+
else
29+
{
30+
logger.LogWarning("Mesh handshake failed for mailbox {MailboxId} at {Time}. Error: {Error}",
31+
configuration.NbssMeshMailboxId, DateTime.UtcNow, response.Error);
32+
}
33+
}
34+
catch (Exception ex)
35+
{
36+
logger.LogError(ex, "An error occurred during mesh handshake for mailbox {MailboxId} at {Time}", configuration.NbssMeshMailboxId, DateTime.UtcNow);
37+
// throw;
38+
return;
39+
}
40+
41+
logger.LogInformation("{FunctionName} completed at: {Time}", nameof(MeshHandshakeFunction), DateTime.UtcNow);
42+
}
43+
}
644
}

src/ServiceLayer.Mesh/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using Azure.Storage.Blobs;
1010
using ServiceLayer.Mesh.Configuration;
1111
using ServiceLayer.Mesh.Messaging;
12+
using NHS.MESH.Client.Contracts.Services;
13+
using NHS.MESH.Client.Services;
1214

1315
var host = new HostBuilder()
1416
.ConfigureFunctionsWebApplication()
@@ -51,6 +53,7 @@
5153

5254
services.AddSingleton<IFileExtractQueueClient, FileExtractQueueClient>();
5355
services.AddSingleton<IFileTransformQueueClient, FileTransformQueueClient>();
56+
services.AddSingleton<IMeshOperationService, MeshOperationService>();
5457

5558
services.AddSingleton(provider =>
5659
{
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using Microsoft.Azure.Functions.Worker;
2+
using Microsoft.Extensions.Logging;
3+
using Moq;
4+
using NHS.MESH.Client.Contracts.Services;
5+
using NHS.MESH.Client.Models;
6+
using ServiceLayer.Mesh.Configuration;
7+
using ServiceLayer.Mesh.Functions;
8+
9+
namespace ServiceLayer.Mesh.Tests.Functions;
10+
11+
public class MeshHandshakeFunctionTests
12+
{
13+
private readonly Mock<ILogger<MeshHandshakeFunction>> _loggerMock;
14+
private readonly Mock<IMeshOperationService> _meshOperationServiceMock;
15+
private readonly Mock<IMeshHandshakeFunctionConfiguration> _configurationMock;
16+
private readonly MeshHandshakeFunction _function;
17+
private readonly TimerInfo _timerInfo;
18+
private const string TestMailboxId = "test-mailbox-123";
19+
20+
public MeshHandshakeFunctionTests()
21+
{
22+
_loggerMock = new Mock<ILogger<MeshHandshakeFunction>>();
23+
_meshOperationServiceMock = new Mock<IMeshOperationService>();
24+
_configurationMock = new Mock<IMeshHandshakeFunctionConfiguration>();
25+
_timerInfo = new TimerInfo();
26+
27+
_configurationMock.Setup(c => c.NbssMeshMailboxId).Returns(TestMailboxId);
28+
_function = new MeshHandshakeFunction(
29+
_loggerMock.Object,
30+
_meshOperationServiceMock.Object,
31+
_configurationMock.Object
32+
);
33+
}
34+
35+
[Fact]
36+
public async Task Run_SuccessfulHandshake_LogsSuccessAndCompletion()
37+
{
38+
// Arrange
39+
var successfulResponse = new MeshResponse<HandshakeResponse>
40+
{
41+
IsSuccessful = true,
42+
Response = new HandshakeResponse { MailboxId = TestMailboxId }
43+
};
44+
_meshOperationServiceMock
45+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
46+
.ReturnsAsync(successfulResponse);
47+
48+
// Act
49+
await _function.Run(_timerInfo);
50+
51+
// Assert
52+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
53+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started at");
54+
VerifyLogMessage(LogLevel.Information, "Mesh handshake completed successfully");
55+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction completed at");
56+
}
57+
58+
[Fact]
59+
public async Task Run_FailedHandshake_LogsWarningAndCompletion()
60+
{
61+
// Arrange
62+
var failedResponse = new MeshResponse<HandshakeResponse>
63+
{
64+
IsSuccessful = false,
65+
Error = new APIErrorResponse
66+
{
67+
ErrorDescription = "Authentication failed"
68+
}
69+
};
70+
_meshOperationServiceMock
71+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
72+
.ReturnsAsync(failedResponse);
73+
74+
// Act
75+
await _function.Run(_timerInfo);
76+
77+
// Assert
78+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
79+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started at");
80+
VerifyLogMessage(LogLevel.Warning, "Mesh handshake failed");
81+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction completed at");
82+
}
83+
84+
[Fact]
85+
public async Task Run_ExceptionThrown_LogsErrorAndCompletion()
86+
{
87+
// Arrange
88+
_meshOperationServiceMock.Reset();
89+
_loggerMock.Reset();
90+
91+
var expectedException = new InvalidOperationException("Connection failed");
92+
_meshOperationServiceMock
93+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
94+
.ThrowsAsync(expectedException);
95+
96+
// Act
97+
await _function.Run(_timerInfo);
98+
99+
// Assert
100+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
101+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started at");
102+
VerifyLogMessage(LogLevel.Error, "An error occurred during mesh handshake");
103+
_loggerMock.Verify(
104+
x => x.Log(
105+
LogLevel.Error,
106+
It.IsAny<EventId>(),
107+
It.IsAny<It.IsAnyType>(),
108+
expectedException,
109+
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
110+
Times.Once);
111+
}
112+
113+
private void VerifyLogMessage(LogLevel level, string expectedMessage)
114+
{
115+
_loggerMock.Verify(
116+
x => x.Log(
117+
level,
118+
It.IsAny<EventId>(),
119+
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains(expectedMessage)),
120+
It.IsAny<Exception>(),
121+
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
122+
Times.Once);
123+
}
124+
}

0 commit comments

Comments
 (0)