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:
226
tests/StellaOps.Router.Gateway.Tests/ConnectionManagerTests.cs
Normal file
226
tests/StellaOps.Router.Gateway.Tests/ConnectionManagerTests.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Router.Common.Enums;
|
||||
using StellaOps.Router.Common.Models;
|
||||
using StellaOps.Router.Gateway.Services;
|
||||
using StellaOps.Router.Gateway.State;
|
||||
using StellaOps.Router.Transport.InMemory;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Router.Gateway.Tests;
|
||||
|
||||
public sealed class ConnectionManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task StartAsync_WhenHelloInvalid_RejectsAndClosesChannel()
|
||||
{
|
||||
var (manager, server, registry, routingState) = Create();
|
||||
var client = CreateClient(registry, server);
|
||||
|
||||
try
|
||||
{
|
||||
await manager.StartAsync(CancellationToken.None);
|
||||
|
||||
var invalid = new InstanceDescriptor
|
||||
{
|
||||
InstanceId = "inv-1",
|
||||
ServiceName = "",
|
||||
Version = "1.0.0",
|
||||
Region = "eu1"
|
||||
};
|
||||
|
||||
await client.ConnectAsync(
|
||||
invalid,
|
||||
endpoints:
|
||||
[
|
||||
new EndpointDescriptor
|
||||
{
|
||||
ServiceName = "inventory",
|
||||
Version = "1.0.0",
|
||||
Method = "GET",
|
||||
Path = "/items"
|
||||
}
|
||||
],
|
||||
CancellationToken.None);
|
||||
|
||||
await EventuallyAsync(
|
||||
() => registry.Count == 0,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
|
||||
routingState.GetAllConnections().Should().BeEmpty();
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.Dispose();
|
||||
await manager.StopAsync(CancellationToken.None);
|
||||
server.Dispose();
|
||||
registry.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WhenClientDisconnects_RemovesFromRoutingState()
|
||||
{
|
||||
var (manager, server, registry, routingState) = Create();
|
||||
var client = CreateClient(registry, server);
|
||||
|
||||
try
|
||||
{
|
||||
await manager.StartAsync(CancellationToken.None);
|
||||
|
||||
await client.ConnectAsync(
|
||||
CreateInstance("inventory", "1.0.0", "inv-1"),
|
||||
endpoints:
|
||||
[
|
||||
new EndpointDescriptor
|
||||
{
|
||||
ServiceName = "inventory",
|
||||
Version = "1.0.0",
|
||||
Method = "GET",
|
||||
Path = "/items"
|
||||
}
|
||||
],
|
||||
CancellationToken.None);
|
||||
|
||||
await EventuallyAsync(
|
||||
() => routingState.GetAllConnections().Count == 1,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
|
||||
client.Dispose();
|
||||
|
||||
await EventuallyAsync(
|
||||
() => routingState.GetAllConnections().Count == 0,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.Dispose();
|
||||
await manager.StopAsync(CancellationToken.None);
|
||||
server.Dispose();
|
||||
registry.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WhenMultipleClientsConnect_TracksAndCleansIndependently()
|
||||
{
|
||||
var (manager, server, registry, routingState) = Create();
|
||||
var client1 = CreateClient(registry, server);
|
||||
var client2 = CreateClient(registry, server);
|
||||
|
||||
try
|
||||
{
|
||||
await manager.StartAsync(CancellationToken.None);
|
||||
|
||||
var endpoints =
|
||||
new[]
|
||||
{
|
||||
new EndpointDescriptor
|
||||
{
|
||||
ServiceName = "inventory",
|
||||
Version = "1.0.0",
|
||||
Method = "GET",
|
||||
Path = "/items"
|
||||
}
|
||||
};
|
||||
|
||||
await client1.ConnectAsync(
|
||||
CreateInstance("inventory", "1.0.0", "inv-1"),
|
||||
endpoints,
|
||||
CancellationToken.None);
|
||||
|
||||
await client2.ConnectAsync(
|
||||
CreateInstance("inventory", "1.0.0", "inv-2"),
|
||||
endpoints,
|
||||
CancellationToken.None);
|
||||
|
||||
await EventuallyAsync(
|
||||
() => routingState.GetConnectionsFor("inventory", "1.0.0", "GET", "/items").Count == 2,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
|
||||
var before = routingState.GetConnectionsFor("inventory", "1.0.0", "GET", "/items")
|
||||
.Select(c => c.Instance.InstanceId)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
before.Should().BeEquivalentTo(new[] { "inv-1", "inv-2" });
|
||||
|
||||
client1.Dispose();
|
||||
|
||||
await EventuallyAsync(
|
||||
() => routingState.GetConnectionsFor("inventory", "1.0.0", "GET", "/items").Count == 1,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
|
||||
var after = routingState.GetConnectionsFor("inventory", "1.0.0", "GET", "/items")
|
||||
.Single()
|
||||
.Instance.InstanceId;
|
||||
|
||||
after.Should().Be("inv-2");
|
||||
}
|
||||
finally
|
||||
{
|
||||
client1.Dispose();
|
||||
client2.Dispose();
|
||||
await manager.StopAsync(CancellationToken.None);
|
||||
server.Dispose();
|
||||
registry.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static (ConnectionManager Manager, InMemoryTransportServer Server, InMemoryConnectionRegistry Registry, InMemoryRoutingState RoutingState) Create()
|
||||
{
|
||||
var registry = new InMemoryConnectionRegistry();
|
||||
var server = new InMemoryTransportServer(
|
||||
registry,
|
||||
Options.Create(new InMemoryTransportOptions()),
|
||||
NullLogger<InMemoryTransportServer>.Instance);
|
||||
|
||||
var routingState = new InMemoryRoutingState();
|
||||
var manager = new ConnectionManager(
|
||||
server,
|
||||
registry,
|
||||
routingState,
|
||||
NullLogger<ConnectionManager>.Instance);
|
||||
|
||||
return (manager, server, registry, routingState);
|
||||
}
|
||||
|
||||
private static InMemoryTransportClient CreateClient(InMemoryConnectionRegistry registry, InMemoryTransportServer server)
|
||||
{
|
||||
return new InMemoryTransportClient(
|
||||
registry,
|
||||
Options.Create(new InMemoryTransportOptions()),
|
||||
NullLogger<InMemoryTransportClient>.Instance,
|
||||
server);
|
||||
}
|
||||
|
||||
private static InstanceDescriptor CreateInstance(string serviceName, string version, string instanceId)
|
||||
{
|
||||
return new InstanceDescriptor
|
||||
{
|
||||
InstanceId = instanceId,
|
||||
ServiceName = serviceName,
|
||||
Version = version,
|
||||
Region = "eu1"
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task EventuallyAsync(Func<bool> predicate, TimeSpan timeout, TimeSpan? pollInterval = null)
|
||||
{
|
||||
pollInterval ??= TimeSpan.FromMilliseconds(25);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user