Implements ProcessImplicitRoute and ForwardNewRouteInfoToKnownServers on RouteManager, and ProcessImplicitGateway on GatewayManager, mirroring Go server/route.go and server/gateway.go INFO gossip-based peer discovery. Adds ConnectUrls to ServerInfo and introduces GatewayInfo model. 12 new unit tests in ImplicitDiscoveryTests.
237 lines
7.2 KiB
C#
237 lines
7.2 KiB
C#
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NATS.Server.Configuration;
|
|
using NATS.Server;
|
|
using NATS.Server.Gateways;
|
|
using NATS.Server.Protocol;
|
|
using NATS.Server.Routes;
|
|
|
|
namespace NATS.Server.Tests;
|
|
|
|
// Go reference: server/route.go processImplicitRoute, server/gateway.go processImplicitGateway
|
|
|
|
public class ImplicitDiscoveryTests
|
|
{
|
|
[Fact]
|
|
public void ProcessImplicitRoute_discovers_new_peer()
|
|
{
|
|
// Go reference: server/route.go processImplicitRoute
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
var serverInfo = new ServerInfo
|
|
{
|
|
ServerId = "server-2",
|
|
ServerName = "server-2",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = ["nats://10.0.0.2:6222", "nats://10.0.0.3:6222"],
|
|
};
|
|
|
|
mgr.ProcessImplicitRoute(serverInfo);
|
|
|
|
mgr.DiscoveredRoutes.ShouldContain("nats://10.0.0.2:6222");
|
|
mgr.DiscoveredRoutes.ShouldContain("nats://10.0.0.3:6222");
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitRoute_skips_known_peers()
|
|
{
|
|
// Go reference: server/route.go processImplicitRoute — skip already-known
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
mgr.AddKnownRoute("nats://10.0.0.2:6222");
|
|
|
|
var serverInfo = new ServerInfo
|
|
{
|
|
ServerId = "server-2",
|
|
ServerName = "server-2",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = ["nats://10.0.0.2:6222", "nats://10.0.0.3:6222"],
|
|
};
|
|
|
|
mgr.ProcessImplicitRoute(serverInfo);
|
|
|
|
mgr.DiscoveredRoutes.Count.ShouldBe(1); // only 10.0.0.3 is new
|
|
mgr.DiscoveredRoutes.ShouldContain("nats://10.0.0.3:6222");
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitRoute_with_null_urls_is_noop()
|
|
{
|
|
// Go reference: server/route.go processImplicitRoute — nil ConnectUrls guard
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
var serverInfo = new ServerInfo
|
|
{
|
|
ServerId = "server-2",
|
|
ServerName = "server-2",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = null,
|
|
};
|
|
|
|
mgr.ProcessImplicitRoute(serverInfo);
|
|
|
|
mgr.DiscoveredRoutes.Count.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitRoute_with_empty_urls_is_noop()
|
|
{
|
|
// Go reference: server/route.go processImplicitRoute — empty ConnectUrls guard
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
var serverInfo = new ServerInfo
|
|
{
|
|
ServerId = "server-2",
|
|
ServerName = "server-2",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = [],
|
|
};
|
|
|
|
mgr.ProcessImplicitRoute(serverInfo);
|
|
|
|
mgr.DiscoveredRoutes.Count.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitGateway_discovers_new_gateway()
|
|
{
|
|
// Go reference: server/gateway.go processImplicitGateway
|
|
var mgr = GatewayManagerTestHelper.Create();
|
|
var gwInfo = new GatewayInfo
|
|
{
|
|
Name = "cluster-B",
|
|
Urls = ["nats://10.0.1.1:7222"],
|
|
};
|
|
|
|
mgr.ProcessImplicitGateway(gwInfo);
|
|
|
|
mgr.DiscoveredGateways.ShouldContain("cluster-B");
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitGateway_with_null_throws()
|
|
{
|
|
// Go reference: server/gateway.go processImplicitGateway — null guard
|
|
var mgr = GatewayManagerTestHelper.Create();
|
|
|
|
Should.Throw<ArgumentNullException>(() => mgr.ProcessImplicitGateway(null!));
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessImplicitGateway_deduplicates_same_cluster()
|
|
{
|
|
// Go reference: server/gateway.go processImplicitGateway — idempotent discovery
|
|
var mgr = GatewayManagerTestHelper.Create();
|
|
var gwInfo = new GatewayInfo { Name = "cluster-B", Urls = ["nats://10.0.1.1:7222"] };
|
|
|
|
mgr.ProcessImplicitGateway(gwInfo);
|
|
mgr.ProcessImplicitGateway(gwInfo);
|
|
|
|
mgr.DiscoveredGateways.Count.ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void ForwardNewRouteInfo_invokes_event()
|
|
{
|
|
// Go reference: server/route.go forwardNewRouteInfoToKnownServers
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
var forwarded = new List<string>();
|
|
mgr.OnForwardInfo += urls => forwarded.AddRange(urls);
|
|
|
|
mgr.ForwardNewRouteInfoToKnownServers("nats://10.0.0.5:6222");
|
|
|
|
forwarded.ShouldContain("nats://10.0.0.5:6222");
|
|
}
|
|
|
|
[Fact]
|
|
public void ForwardNewRouteInfo_with_no_handler_does_not_throw()
|
|
{
|
|
// Go reference: server/route.go forwardNewRouteInfoToKnownServers — no subscribers
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
|
|
Should.NotThrow(() => mgr.ForwardNewRouteInfoToKnownServers("nats://10.0.0.5:6222"));
|
|
}
|
|
|
|
[Fact]
|
|
public void AddKnownRoute_prevents_later_discovery()
|
|
{
|
|
// Go reference: server/route.go processImplicitRoute — pre-seeded known routes
|
|
var mgr = RouteManagerTestHelper.Create();
|
|
mgr.AddKnownRoute("nats://10.0.0.9:6222");
|
|
|
|
var serverInfo = new ServerInfo
|
|
{
|
|
ServerId = "server-3",
|
|
ServerName = "server-3",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = ["nats://10.0.0.9:6222"],
|
|
};
|
|
|
|
mgr.ProcessImplicitRoute(serverInfo);
|
|
|
|
mgr.DiscoveredRoutes.Count.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnectUrls_is_serialized_when_set()
|
|
{
|
|
// Go reference: server/route.go INFO message includes connect_urls
|
|
var info = new ServerInfo
|
|
{
|
|
ServerId = "s1",
|
|
ServerName = "s1",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = ["nats://10.0.0.1:4222"],
|
|
};
|
|
|
|
var json = System.Text.Json.JsonSerializer.Serialize(info);
|
|
json.ShouldContain("connect_urls");
|
|
json.ShouldContain("nats://10.0.0.1:4222");
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnectUrls_is_omitted_when_null()
|
|
{
|
|
// Go reference: server/route.go INFO omits connect_urls when empty
|
|
var info = new ServerInfo
|
|
{
|
|
ServerId = "s1",
|
|
ServerName = "s1",
|
|
Version = "0.1.0",
|
|
Host = "0.0.0.0",
|
|
Port = 4222,
|
|
ConnectUrls = null,
|
|
};
|
|
|
|
var json = System.Text.Json.JsonSerializer.Serialize(info);
|
|
json.ShouldNotContain("connect_urls");
|
|
}
|
|
}
|
|
|
|
public static class RouteManagerTestHelper
|
|
{
|
|
public static RouteManager Create()
|
|
{
|
|
var options = new ClusterOptions { Name = "test-cluster", Host = "127.0.0.1", Port = 0 };
|
|
var stats = new ServerStats();
|
|
return new RouteManager(options, stats, "server-1", _ => { }, _ => { }, NullLogger<RouteManager>.Instance);
|
|
}
|
|
}
|
|
|
|
public static class GatewayManagerTestHelper
|
|
{
|
|
public static GatewayManager Create()
|
|
{
|
|
var options = new GatewayOptions { Name = "cluster-A", Host = "127.0.0.1", Port = 0 };
|
|
var stats = new ServerStats();
|
|
return new GatewayManager(options, stats, "server-1", _ => { }, _ => { }, NullLogger<GatewayManager>.Instance);
|
|
}
|
|
}
|