Refactor and enhance tests for call graph extractors and connection management

- Updated JavaScriptCallGraphExtractorTests to improve naming conventions and test cases for Azure Functions, CLI commands, and socket handling.
- Modified NodeCallGraphExtractorTests to correctly assert exceptions for null inputs.
- Enhanced WitnessModalComponent tests in Angular to use Jasmine spies and improved assertions for path visualization and signature verification.
- Added ConnectionState property for tracking connection establishment time in Router.Common.
- Implemented validation for HelloPayload in ConnectionManager to ensure required fields are present.
- Introduced RabbitMqContainerFixture method for restarting RabbitMQ container during tests.
- Added integration tests for RabbitMq to verify connection recovery after broker restarts.
- Created new BinaryCallGraphExtractorTests, GoCallGraphExtractorTests, and PythonCallGraphExtractorTests for comprehensive coverage of binary, Go, and Python call graph extraction functionalities.
- Developed ConnectionManagerTests to validate connection handling, including rejection of invalid hello messages and proper cleanup on client disconnects.
This commit is contained in:
master
2025-12-19 18:49:36 +02:00
parent 8779e9226f
commit 91f3610b9d
18 changed files with 1119 additions and 69 deletions

View File

@@ -80,6 +80,17 @@ public sealed class RabbitMqContainerFixture : RouterCollectionFixture, IAsyncDi
};
}
public async Task RestartAsync()
{
if (_container is null)
{
throw new InvalidOperationException("RabbitMQ container is not running.");
}
await _container.StopAsync();
await _container.StartAsync();
}
/// <inheritdoc />
public override async Task InitializeAsync()
{

View File

@@ -50,9 +50,11 @@ public sealed class RabbitMqIntegrationTests : IAsyncLifetime
_fixture.GetLogger<RabbitMqTransportServer>());
}
private RabbitMqTransportClient CreateClient(string? instanceId = null)
private RabbitMqTransportClient CreateClient(string? instanceId = null, string? nodeId = null)
{
var options = _fixture.CreateOptions(instanceId: instanceId ?? $"svc-{Guid.NewGuid():N}"[..12]);
var options = _fixture.CreateOptions(
instanceId: instanceId ?? $"svc-{Guid.NewGuid():N}"[..12],
nodeId: nodeId);
return new RabbitMqTransportClient(
Options.Create(options),
_fixture.GetLogger<RabbitMqTransportClient>());
@@ -137,8 +139,9 @@ public sealed class RabbitMqIntegrationTests : IAsyncLifetime
public async Task ClientConnectAsync_SendsHelloFrame_ServerReceives()
{
// Arrange
_server = CreateServer("gw-hello-test");
_client = CreateClient("svc-hello-test");
const string nodeId = "gw-hello-test";
_server = CreateServer(nodeId);
_client = CreateClient("svc-hello-test", nodeId: nodeId);
Frame? receivedFrame = null;
string? receivedConnectionId = null;
@@ -214,8 +217,9 @@ public sealed class RabbitMqIntegrationTests : IAsyncLifetime
public async Task ServerReceivesHeartbeat_UpdatesLastHeartbeatUtc()
{
// Arrange
_server = CreateServer("gw-heartbeat-test");
_client = CreateClient("svc-heartbeat-test");
const string nodeId = "gw-heartbeat-test";
_server = CreateServer(nodeId);
_client = CreateClient("svc-heartbeat-test", nodeId: nodeId);
var heartbeatReceived = new TaskCompletionSource<bool>();
_server.OnFrame += (connectionId, frame) =>
@@ -270,6 +274,119 @@ public sealed class RabbitMqIntegrationTests : IAsyncLifetime
#endregion
#region Connection Recovery Tests
[RabbitMqIntegrationFact]
public async Task ConnectionRecovery_BrokerRestart_AllowsPublishingAndConsumingAgain()
{
// Arrange
const string nodeId = "gw-recovery-test";
const string instanceId = "svc-recovery-test";
_server = CreateServer(nodeId);
_client = CreateClient(instanceId, nodeId: nodeId);
await _server.StartAsync(CancellationToken.None);
var instance = new InstanceDescriptor
{
InstanceId = instanceId,
ServiceName = "test-service",
Version = "1.0.0",
Region = "us-east-1"
};
await _client.ConnectAsync(instance, [], CancellationToken.None);
await EventuallyAsync(
() => _server.ConnectionCount > 0,
timeout: TimeSpan.FromSeconds(15));
// Act: force broker restart and wait for client/server recovery.
await _fixture.RestartAsync();
var heartbeat = new HeartbeatPayload
{
InstanceId = instanceId,
Status = InstanceHealthStatus.Healthy,
InFlightRequestCount = 0,
ErrorRate = 0,
TimestampUtc = DateTime.UtcNow
};
var heartbeatReceived = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_server.OnFrame += (_, frame) =>
{
if (frame.Type == FrameType.Heartbeat)
{
heartbeatReceived.TrySetResult(true);
}
};
await EventuallyAsync(
async () =>
{
await _client.SendHeartbeatAsync(heartbeat, CancellationToken.None);
return true;
},
timeout: TimeSpan.FromSeconds(30),
swallowExceptions: true);
await heartbeatReceived.Task.WaitAsync(TimeSpan.FromSeconds(30));
}
#endregion
private static async Task EventuallyAsync(
Func<bool> predicate,
TimeSpan timeout,
TimeSpan? pollInterval = null)
{
pollInterval ??= TimeSpan.FromMilliseconds(250);
var deadline = DateTime.UtcNow + timeout;
while (DateTime.UtcNow < deadline)
{
if (predicate())
{
return;
}
await Task.Delay(pollInterval.Value);
}
predicate().Should().BeTrue("condition should become true within {0}", timeout);
}
private static async Task EventuallyAsync(
Func<Task<bool>> predicate,
TimeSpan timeout,
bool swallowExceptions,
TimeSpan? pollInterval = null)
{
pollInterval ??= TimeSpan.FromMilliseconds(500);
var deadline = DateTime.UtcNow + timeout;
while (DateTime.UtcNow < deadline)
{
try
{
if (await predicate())
{
return;
}
}
catch when (swallowExceptions)
{
// Retry
}
await Task.Delay(pollInterval.Value);
}
(await predicate()).Should().BeTrue("condition should become true within {0}", timeout);
}
#region Queue Declaration Tests
[RabbitMqIntegrationFact]