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:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user