using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Auth; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class RouteHandlerTests { [Fact] // T:2796 public void RouteConfig_ShouldSucceed() { var options = new ServerOptions(); var errors = new List(); var warnings = new List(); var parseError = ServerOptions.ParseCluster( Map( ("name", "abc"), ("listen", "127.0.0.1:4244"), ("authorization", Map(("user", "route_user"), ("password", "top_secret"), ("timeout", 1L))), ("no_advertise", true), ("connect_retries", 2L), ("connect_backoff", true), ("routes", Arr("nats-route://foo:bar@127.0.0.1:4245", "nats-route://foo:bar@127.0.0.1:4246"))), options, errors, warnings); parseError.ShouldBeNull(); errors.ShouldBeEmpty(); options.Cluster.Name.ShouldBe("abc"); options.Cluster.Host.ShouldBe("127.0.0.1"); options.Cluster.Port.ShouldBe(4244); options.Cluster.Username.ShouldBe("route_user"); options.Cluster.Password.ShouldBe("top_secret"); options.Cluster.NoAdvertise.ShouldBeTrue(); options.Cluster.ConnectRetries.ShouldBe(2); options.Cluster.ConnectBackoff.ShouldBeTrue(); options.Routes.Count.ShouldBe(2); } [Fact] // T:2797 public void ClusterAdvertise_ShouldSucceed() { var options = new ServerOptions(); var errors = new List(); var warnings = new List(); var parseError = ServerOptions.ParseCluster( Map(("listen", "127.0.0.1:6222"), ("advertise", "127.0.0.1:7222")), options, errors, warnings); parseError.ShouldBeNull(); errors.ShouldBeEmpty(); options.Cluster.Advertise.ShouldBe("127.0.0.1:7222"); } [Fact] // T:2799 public void ClientAdvertise_ShouldSucceed() { var options = new ServerOptions(); var error = options.ProcessConfigString(""" { "client_advertise": "me:1" } """); error.ShouldBeNull(); options.ClientAdvertise.ShouldBe("me:1"); } [Fact] // T:2800 public void ServerRoutesWithClients_ShouldSucceed() { var merged = ServerOptions.MergeOptions( new ServerOptions { Routes = [new Uri("nats://127.0.0.1:4245")] }, new ServerOptions { RoutesStr = "nats://127.0.0.1:4245, nats://127.0.0.1:4246" }); merged.RoutesStr.ShouldBe("nats://127.0.0.1:4245, nats://127.0.0.1:4246"); merged.Routes.Count.ShouldBe(2); } [Fact] // T:2801 public void ServerRoutesWithAuthAndBCrypt_ShouldSucceed() { var (auth, error) = ServerOptions.ParseAuthorization( Map(("users", Arr( Map(("user", "derek"), ("password", "$2a$11$abcdefghijklmnopqrstuv")), Map(("user", "alice"), ("password", "$2a$11$zyxwvutsrqponmlkjihgfe")))))); error.ShouldBeNull(); auth.ShouldNotBeNull(); auth.Users.Count.ShouldBe(2); auth.Users[0].Password.ShouldStartWith("$2a$11$"); } [Fact] // T:2802 public void SeedSolicitWorks_ShouldSucceed() { var routes = ServerOptions.RoutesFromStr("nats://127.0.0.1:7244"); routes.Count.ShouldBe(1); routes[0].Host.ShouldBe("127.0.0.1"); routes[0].Port.ShouldBe(7244); } [Fact] // T:2803 public void TLSSeedSolicitWorks_ShouldSucceed() { var (tlsOptions, parseError) = ServerOptions.ParseTLS( Map(("verify", true), ("timeout", 2L)), isClientCtx: false); parseError.ShouldBeNull(); tlsOptions.ShouldNotBeNull(); tlsOptions.Verify.ShouldBeTrue(); tlsOptions.Timeout.ShouldBe(2d); } [Fact] // T:2804 public void ChainedSolicitWorks_ShouldSucceed() { var routes = ServerOptions.RoutesFromStr( "nats://127.0.0.1:7244, nats://127.0.0.1:7245, nats://127.0.0.1:7246"); routes.Count.ShouldBe(3); } [Fact] // T:2805 public void TLSChainedSolicitWorks_ShouldSucceed() { var (tlsOptions, parseError) = ServerOptions.ParseTLS( Map(("verify", true), ("timeout", "3s")), isClientCtx: false); parseError.ShouldBeNull(); tlsOptions.ShouldNotBeNull(); tlsOptions.Timeout.ShouldBe(3d); } [Fact] // T:2806 public void RouteTLSHandshakeError_ShouldSucceed() { var (tlsOptions, parseError) = ServerOptions.ParseTLS( Map(("cipher_suites", Arr("TLS_RSA_WITH_RC4_128_SHA"))), isClientCtx: false); tlsOptions.ShouldBeNull(); parseError.ShouldNotBeNull(); parseError.Message.ShouldContain("insecure cipher suites configured"); } [Fact] // T:2813 public void RoutePermsAppliedOnInboundAndOutboundRoute_ShouldSucceed() { var cluster = new ClusterOpts(); var permissions = new Permissions { Publish = new SubjectPermission { Allow = ["imp.foo"], Deny = ["imp.bar"] }, Subscribe = new SubjectPermission { Allow = ["exp.foo"], Deny = ["exp.bar"] }, }; ServerOptions.SetClusterPermissions(cluster, permissions); cluster.Permissions.ShouldNotBeNull(); cluster.Permissions.Import.ShouldNotBeNull(); cluster.Permissions.Export.ShouldNotBeNull(); cluster.Permissions.Import.Allow.ShouldContain("imp.foo"); cluster.Permissions.Import.Deny.ShouldContain("imp.bar"); cluster.Permissions.Export.Allow.ShouldContain("exp.foo"); cluster.Permissions.Export.Deny.ShouldContain("exp.bar"); } private static Dictionary Map(params (string Key, object? Value)[] entries) { var map = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var (key, value) in entries) map[key] = value; return map; } private static List Arr(params object?[] entries) => [.. entries]; }