Files
natsnet/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ServerOptionsTests.cs
2026-02-28 11:18:06 -05:00

912 lines
29 KiB
C#

using Shouldly;
using ZB.MOM.NatsNet.Server;
using ZB.MOM.NatsNet.Server.Auth;
using System.Linq;
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
public sealed class ServerOptionsTests
{
private static Dictionary<string, object?> Map(params (string Key, object? Value)[] entries)
{
var map = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
foreach (var (key, value) in entries)
map[key] = value;
return map;
}
private static List<object?> Arr(params object?[] entries) => [.. entries];
private static string CreateJsonConfig(string json)
{
var path = Path.GetTempFileName();
File.WriteAllText(path, json);
return path;
}
private static T ReadProperty<T>(object target, string propertyName)
{
var property = target.GetType().GetProperty(propertyName);
property.ShouldNotBeNull();
var value = property.GetValue(target);
value.ShouldNotBeNull();
return (T)value;
}
[Fact]
public void DeepCopyURLs_WithEntries_ReturnsIndependentCopy()
{
var source = new List<Uri>
{
new("nats://127.0.0.1:4222"),
new("nats://127.0.0.1:4223"),
};
var copy = ServerOptions.DeepCopyURLs(source);
copy.ShouldNotBeNull();
copy.Count.ShouldBe(2);
ReferenceEquals(copy, source).ShouldBeFalse();
copy[0].ToString().ShouldBe(source[0].ToString());
copy[1].ToString().ShouldBe(source[1].ToString());
}
[Fact]
public void ProcessConfigFile_WithValidFile_ReturnsParsedOptions()
{
var tempFile = Path.GetTempFileName();
try
{
File.WriteAllText(tempFile, """
{
"host": "127.0.0.1",
"port": 4444,
"system_account": "$SYS"
}
""");
var options = ServerOptions.ProcessConfigFile(tempFile);
options.Host.ShouldBe("127.0.0.1");
options.Port.ShouldBe(4444);
options.SystemAccount.ShouldBe("$SYS");
}
finally
{
File.Delete(tempFile);
}
}
[Fact]
public void ConfigureSystemAccount_WithSystemAccountString_SetsValue()
{
var options = new ServerOptions();
var config = new Dictionary<string, object?>
{
["system_account"] = "$SYSX",
};
var error = ServerOptions.ConfigureSystemAccount(options, config);
error.ShouldBeNull();
options.SystemAccount.ShouldBe("$SYSX");
}
[Fact]
public void ConfigureSystemAccount_WithNonString_ReturnsError()
{
var options = new ServerOptions();
var config = new Dictionary<string, object?>
{
["system"] = 123L,
};
var error = ServerOptions.ConfigureSystemAccount(options, config);
error.ShouldNotBeNull();
error.Message.ShouldContain("must be a string");
}
[Fact]
public void SetupUsersAndNKeysDuplicateCheckMap_WithUsersAndNkeys_IncludesAllIdentities()
{
var options = new ServerOptions
{
Users =
[
new User { Username = "alice" },
new User { Username = "bob" },
],
Nkeys =
[
new NkeyUser { Nkey = "UAA" },
new NkeyUser { Nkey = "UBB" },
],
};
var map = ServerOptions.SetupUsersAndNKeysDuplicateCheckMap(options);
map.Count.ShouldBe(4);
map.ShouldContain("alice");
map.ShouldContain("bob");
map.ShouldContain("UAA");
map.ShouldContain("UBB");
}
[Fact]
public void ParseDuration_WithDurationString_ReturnsExpectedDuration()
{
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parsed = ServerOptions.ParseDuration("write_deadline", "5s", errors, warnings);
parsed.ShouldBe(TimeSpan.FromSeconds(5));
errors.ShouldBeEmpty();
warnings.ShouldBeEmpty();
}
[Fact]
public void ParseDuration_WithLegacySeconds_AddsWarning()
{
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parsed = ServerOptions.ParseDuration("auth_timeout", 3L, errors, warnings);
parsed.ShouldBe(TimeSpan.FromSeconds(3));
errors.ShouldBeEmpty();
warnings.Count.ShouldBe(1);
}
[Fact]
public void ParseWriteDeadlinePolicy_WithInvalidValue_ReturnsDefaultAndError()
{
var errors = new List<Exception>();
var policy = ServerOptions.ParseWriteDeadlinePolicy("invalid", errors);
policy.ShouldBe(WriteTimeoutPolicy.Default);
errors.Count.ShouldBe(1);
errors[0].Message.ShouldContain("write_timeout");
}
[Theory]
[InlineData(8222L, "", 8222)]
[InlineData("127.0.0.1:6222", "127.0.0.1", 6222)]
public void ParseListen_WithValidInput_ReturnsHostPort(object input, string expectedHost, int expectedPort)
{
var (host, port) = ServerOptions.ParseListen(input);
host.ShouldBe(expectedHost);
port.ShouldBe(expectedPort);
}
[Fact]
public void ParseCompression_WithMapValue_ParsesModeAndThresholds()
{
var compression = new CompressionOpts();
var error = ServerOptions.ParseCompression(
compression,
CompressionModes.S2Fast,
"compression",
new Dictionary<string, object?>
{
["mode"] = CompressionModes.S2Best,
["rtt_thresholds"] = new List<object?> { "10ms", "25ms" },
});
error.ShouldBeNull();
compression.Mode.ShouldBe(CompressionModes.S2Best);
compression.RttThresholds.Count.ShouldBe(2);
compression.RttThresholds[0].ShouldBe(TimeSpan.FromMilliseconds(10));
compression.RttThresholds[1].ShouldBe(TimeSpan.FromMilliseconds(25));
}
[Fact]
public void ParseURLs_WithDuplicateEntries_DeduplicatesAndWarns()
{
var warnings = new List<Exception>();
var errors = new List<Exception>();
var urls = ServerOptions.ParseURLs(
["nats://127.0.0.1:4222", "nats://127.0.0.1:4222", "nats://127.0.0.1:4223"],
"route",
warnings,
errors);
errors.ShouldBeEmpty();
warnings.Count.ShouldBe(1);
urls.Count.ShouldBe(2);
}
[Fact]
public void ParseCluster_WithBasicConfig_PopulatesClusterAndRoutes()
{
var options = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseCluster(
new Dictionary<string, object?>
{
["name"] = "core",
["listen"] = "127.0.0.1:6222",
["connect_retries"] = 8L,
["connect_backoff"] = true,
["no_advertise"] = true,
["compression"] = "s2_fast",
["routes"] = new List<object?> { "nats://127.0.0.1:6223" },
},
options,
errors,
warnings);
parseError.ShouldBeNull();
errors.ShouldBeEmpty();
options.Cluster.Name.ShouldBe("core");
options.Cluster.Host.ShouldBe("127.0.0.1");
options.Cluster.Port.ShouldBe(6222);
options.Cluster.ConnectRetries.ShouldBe(8);
options.Cluster.ConnectBackoff.ShouldBeTrue();
options.Cluster.NoAdvertise.ShouldBeTrue();
options.Cluster.Compression.Mode.ShouldBe("s2_fast");
options.Routes.Count.ShouldBe(1);
options.Routes[0].ToString().ShouldBe("nats://127.0.0.1:6223/");
}
[Fact]
public void ParseGateway_WithBasicConfig_PopulatesGateway()
{
var options = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseGateway(
new Dictionary<string, object?>
{
["name"] = "edge",
["listen"] = "127.0.0.1:7222",
["connect_retries"] = 4L,
["connect_backoff"] = true,
["advertise"] = "gw.local:7222",
["reject_unknown"] = true,
["authorization"] = new Dictionary<string, object?>
{
["user"] = "gwu",
["password"] = "gwp",
["auth_timeout"] = 3L,
},
},
options,
errors,
warnings);
parseError.ShouldBeNull();
errors.ShouldBeEmpty();
options.Gateway.Name.ShouldBe("edge");
options.Gateway.Host.ShouldBe("127.0.0.1");
options.Gateway.Port.ShouldBe(7222);
options.Gateway.ConnectRetries.ShouldBe(4);
options.Gateway.ConnectBackoff.ShouldBeTrue();
options.Gateway.Advertise.ShouldBe("gw.local:7222");
options.Gateway.RejectUnknown.ShouldBeTrue();
options.Gateway.Username.ShouldBe("gwu");
options.Gateway.Password.ShouldBe("gwp");
options.Gateway.AuthTimeout.ShouldBe(3);
}
[Fact] // T:2514
public void ConfigFile_ShouldSucceed()
{
var path = CreateJsonConfig("""
{
"Host": "127.0.0.1",
"Port": 4242,
"SystemAccount": "$SYS"
}
""");
try
{
var opts = ServerOptions.ProcessConfigFile(path);
opts.Host.ShouldBe("127.0.0.1");
opts.Port.ShouldBe(4242);
opts.SystemAccount.ShouldBe("$SYS");
}
finally
{
File.Delete(path);
}
}
[Fact] // T:2517
public void RouteFlagOverride_ShouldSucceed()
{
var merged = ServerOptions.MergeOptions(
new ServerOptions(),
new ServerOptions { RoutesStr = "nats-route://ruser:top_secret@127.0.0.1:8246" });
merged.RoutesStr.ShouldBe("nats-route://ruser:top_secret@127.0.0.1:8246");
merged.Routes.Count.ShouldBe(1);
merged.Routes[0].ToString().ShouldBe("nats-route://ruser:top_secret@127.0.0.1:8246/");
}
[Fact] // T:2519
public void RouteFlagOverrideWithMultiple_ShouldSucceed()
{
var routes = "nats-route://ruser:top_secret@127.0.0.1:8246, nats-route://ruser:top_secret@127.0.0.1:8266";
var merged = ServerOptions.MergeOptions(new ServerOptions(), new ServerOptions { RoutesStr = routes });
merged.RoutesStr.ShouldBe(routes);
merged.Routes.Count.ShouldBe(2);
}
[Fact] // T:2520
public void DynamicPortOnListen_ShouldSucceed()
{
var (host, port) = ServerOptions.ParseListen("127.0.0.1:-1");
host.ShouldBe("127.0.0.1");
port.ShouldBe(-1);
}
[Fact] // T:2521
public void ListenConfig_ShouldSucceed()
{
var opts = new ServerOptions();
var error = opts.ProcessConfigString("""
{
"listen": "10.0.1.22:4422",
"cluster": {
"listen": "127.0.0.1:4244"
}
}
""");
error.ShouldBeNull();
opts.SetBaselineOptions();
opts.Host.ShouldBe("10.0.1.22");
opts.Port.ShouldBe(4422);
opts.Cluster.Host.ShouldBe("127.0.0.1");
opts.Cluster.Port.ShouldBe(4244);
}
[Fact] // T:2522
public void ListenPortOnlyConfig_ShouldSucceed()
{
var opts = new ServerOptions();
var error = opts.ProcessConfigString("""
{
"listen": 8922
}
""");
error.ShouldBeNull();
opts.SetBaselineOptions();
opts.Host.ShouldBe(ServerConstants.DefaultHost);
opts.Port.ShouldBe(8922);
}
[Fact] // T:2523
public void ListenPortWithColonConfig_ShouldSucceed()
{
var (host, port) = ServerOptions.ParseListen("127.0.0.1:8922");
host.ShouldBe("127.0.0.1");
port.ShouldBe(8922);
}
[Fact] // T:2525
public void MultipleUsersConfig_ShouldSucceed()
{
var (nkeys, users, error) = ServerOptions.ParseUsers(
Arr(
Map(("user", "alice"), ("password", "foo")),
Map(("user", "bob"), ("password", "bar"))));
error.ShouldBeNull();
users.Count.ShouldBe(2);
nkeys.ShouldBeEmpty();
}
[Fact] // T:2526
public void AuthorizationConfig_ShouldSucceed()
{
var (auth, error) = ServerOptions.ParseAuthorization(
Map(("users", Arr(
Map(
("user", "alice"),
("password", "pwd"),
("permissions", Map(("publish", "*"), ("subscribe", ">")))),
Map(("user", "bob"), ("password", "pwd"))))));
error.ShouldBeNull();
auth.ShouldNotBeNull();
auth.Users.Count.ShouldBe(2);
var alice = auth.Users.Single(u => u.Username == "alice");
alice.Permissions.ShouldNotBeNull();
alice.Permissions.Publish.ShouldNotBeNull();
alice.Permissions.Publish.Allow.ShouldContain("*");
alice.Permissions.Subscribe.ShouldNotBeNull();
alice.Permissions.Subscribe.Allow.ShouldContain(">");
}
[Fact] // T:2527
public void NewStyleAuthorizationConfig_ShouldSucceed()
{
var (auth, error) = ServerOptions.ParseAuthorization(
Map(("users", Arr(
Map(
("user", "alice"),
("password", "pwd"),
("permissions", Map(
("publish", Map(("allow", Arr("foo", "bar", "baz")))),
("subscribe", Map(("deny", Arr("$SYS.>")))))))))));
error.ShouldBeNull();
auth.ShouldNotBeNull();
var alice = auth.Users.Single();
alice.Permissions.ShouldNotBeNull();
alice.Permissions.Publish.Allow.Count.ShouldBe(3);
alice.Permissions.Subscribe.Deny.ShouldContain("$SYS.>");
}
[Fact] // T:2528
public void NkeyUsersConfig_ShouldSucceed()
{
var (nkeys, users, error) = ServerOptions.ParseUsers(
Arr(
Map(("nkey", "UDKTV7HZVYJFJN64LLMYQBUR6MTNNYCDC3LAZH4VHURW3GZLL3FULBXV")),
Map(("nkey", "UA3C5TBZYK5GJQJRWPMU6NFY5JNAEVQB2V2TUZFZDHFJFUYVKTTUOFKZ"))));
error.ShouldBeNull();
nkeys.Count.ShouldBe(2);
users.ShouldBeEmpty();
}
[Fact] // T:2529
public void TlsPinnedCertificates_ShouldSucceed()
{
var (tlsOptions, error) = ServerOptions.ParseTLS(
Map(("pinned_certs", Arr(
"7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069",
"a8f407340dcc719864214b85ed96f98d16cbffa8f509d9fa4ca237b7bb3f9c32"))),
isClientCtx: false);
error.ShouldBeNull();
tlsOptions.ShouldNotBeNull();
tlsOptions.PinnedCerts.ShouldNotBeNull();
tlsOptions.PinnedCerts.Count.ShouldBe(2);
}
[Fact] // T:2530
public void NkeyUsersDefaultPermissionsConfig_ShouldSucceed()
{
var (auth, error) = ServerOptions.ParseAuthorization(
Map(
("default_permissions", Map(("publish", "foo"))),
("users", Arr(
Map(("user", "user"), ("password", "pwd")),
Map(("user", "other"), ("password", "pwd"), ("permissions", Map(("subscribe", "bar")))),
Map(("nkey", "UDKTV7HZVYJFJN64LLMYQBUR6MTNNYCDC3LAZH4VHURW3GZLL3FULBXV")),
Map(("nkey", "UA3C5TBZYK5GJQJRWPMU6NFY5JNAEVQB2V2TUZFZDHFJFUYVKTTUOFKZ"), ("permissions", Map(("subscribe", "bar"))))))));
error.ShouldBeNull();
auth.ShouldNotBeNull();
var defaultUser = auth.Users.Single(u => u.Username == "user");
defaultUser.Permissions.ShouldNotBeNull();
defaultUser.Permissions.Publish.ShouldNotBeNull();
defaultUser.Permissions.Publish.Allow.ShouldContain("foo");
var defaultNkey = auth.Nkeys.Single(n => n.Nkey.StartsWith("UDK", StringComparison.Ordinal));
defaultNkey.Permissions.ShouldNotBeNull();
defaultNkey.Permissions.Publish.ShouldNotBeNull();
defaultNkey.Permissions.Publish.Allow.ShouldContain("foo");
}
[Fact] // T:2531
public void NkeyUsersWithPermsConfig_ShouldSucceed()
{
var (nkeys, users, error) = ServerOptions.ParseUsers(
Arr(Map(
("nkey", "UDKTV7HZVYJFJN64LLMYQBUR6MTNNYCDC3LAZH4VHURW3GZLL3FULBXV"),
("permissions", Map(
("publish", "$SYS.>"),
("subscribe", Map(("deny", Arr("foo", "bar", "baz")))))))));
error.ShouldBeNull();
users.ShouldBeEmpty();
nkeys.Count.ShouldBe(1);
nkeys[0].Permissions.ShouldNotBeNull();
nkeys[0].Permissions.Publish.Allow.ShouldContain("$SYS.>");
nkeys[0].Permissions.Subscribe.Deny.Count.ShouldBe(3);
}
[Fact] // T:2532
public void BadNkeyConfig_ShouldSucceed()
{
var (_, _, error) = ServerOptions.ParseUsers(Arr(Map(("nkey", "Ufoo"))));
error.ShouldNotBeNull();
error.Message.ShouldContain("Not a valid public nkey");
}
[Fact] // T:2533
public void NkeyWithPassConfig_ShouldSucceed()
{
var (_, _, error) = ServerOptions.ParseUsers(
Arr(Map(("nkey", "UDKTV7HZVYJFJN64LLMYQBUR6MTNNYCDC3LAZH4VHURW3GZLL3FULBXV"), ("pass", "foo"))));
error.ShouldNotBeNull();
error.Message.ShouldContain("Nkey users do not take usernames or passwords");
}
[Fact] // T:2540
public void EmptyConfig_ShouldSucceed()
{
var opts = new ServerOptions();
var error = opts.ProcessConfigFileOverload2510(string.Empty);
error.ShouldBeNull();
opts.ConfigFile.ShouldBe(string.Empty);
}
[Fact] // T:2541
public void MalformedListenAddress_ShouldSucceed()
{
Should.Throw<InvalidOperationException>(() => ServerOptions.ParseListen("bad::address"));
}
[Fact] // T:2542
public void MalformedClusterAddress_ShouldSucceed()
{
var opts = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseCluster(Map(("listen", "bad::address")), opts, errors, warnings);
parseError.ShouldBeNull();
errors.Count.ShouldBeGreaterThan(0);
}
[Fact] // T:2545
public void PingIntervalOld_ShouldSucceed()
{
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parsed = ServerOptions.ParseDuration("ping_interval", 5L, errors, warnings);
parsed.ShouldBe(TimeSpan.FromSeconds(5));
errors.ShouldBeEmpty();
warnings.Count.ShouldBe(1);
}
[Fact] // T:2546
public void PingIntervalNew_ShouldSucceed()
{
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parsed = ServerOptions.ParseDuration("ping_interval", "5m", errors, warnings);
parsed.ShouldBe(TimeSpan.FromMinutes(5));
errors.ShouldBeEmpty();
warnings.ShouldBeEmpty();
}
[Fact] // T:2547
public void OptionsProcessConfigFile_ShouldSucceed()
{
var path = CreateJsonConfig("""
{
"debug": false,
"trace": true
}
""");
try
{
var opts = new ServerOptions
{
Debug = true,
Trace = false,
LogFile = "test.log",
};
var error = opts.ProcessConfigFileOverload2510(path);
error.ShouldBeNull();
opts.ConfigFile.ShouldBe(path);
opts.Debug.ShouldBeFalse();
opts.Trace.ShouldBeTrue();
opts.LogFile.ShouldBe("test.log");
}
finally
{
File.Delete(path);
}
}
[Fact] // T:2549
public void ClusterPermissionsConfig_ShouldSucceed()
{
var cluster = new ClusterOpts();
var permissions = new Permissions
{
Publish = new SubjectPermission { Allow = ["foo"] },
Subscribe = new SubjectPermission { Allow = ["bar"] },
};
ServerOptions.SetClusterPermissions(cluster, permissions);
cluster.Permissions.ShouldNotBeNull();
cluster.Permissions.Import.ShouldNotBeNull();
cluster.Permissions.Import.Allow.ShouldContain("foo");
cluster.Permissions.Export.ShouldNotBeNull();
cluster.Permissions.Export.Allow.ShouldContain("bar");
}
[Fact] // T:2550
public void ParseServiceLatency_ShouldSucceed()
{
var (latency, error) = ServerOptions.ParseServiceLatency(
"latency",
Map(("sampling", "33%"), ("subject", "latency.tracking.add")));
error.ShouldBeNull();
latency.ShouldNotBeNull();
ReadProperty<int>(latency, "Sampling").ShouldBe(33);
ReadProperty<string>(latency, "Subject").ShouldBe("latency.tracking.add");
}
[Fact] // T:2553
public void ParsingGateways_ShouldSucceed()
{
var opts = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseGateway(
Map(
("name", "A"),
("listen", "127.0.0.1:4444"),
("authorization", Map(("user", "ivan"), ("password", "pwd"), ("timeout", 2L))),
("advertise", "me:1"),
("connect_retries", 10L),
("connect_backoff", true),
("reject_unknown_cluster", true)),
opts,
errors,
warnings);
parseError.ShouldBeNull();
errors.ShouldBeEmpty();
opts.Gateway.Name.ShouldBe("A");
opts.Gateway.Host.ShouldBe("127.0.0.1");
opts.Gateway.Port.ShouldBe(4444);
opts.Gateway.Username.ShouldBe("ivan");
opts.Gateway.Password.ShouldBe("pwd");
opts.Gateway.AuthTimeout.ShouldBe(2);
opts.Gateway.ConnectRetries.ShouldBe(10);
opts.Gateway.ConnectBackoff.ShouldBeTrue();
opts.Gateway.RejectUnknown.ShouldBeTrue();
}
[Fact] // T:2555
public void ParsingLeafNodesListener_ShouldSucceed()
{
var opts = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseLeafNodes(
Map(
("listen", "127.0.0.1:3333"),
("authorization", Map(("user", "derek"), ("password", "s3cr3t!"), ("timeout", 2.2))),
("advertise", "me:22")),
opts,
errors,
warnings);
parseError.ShouldBeNull();
errors.ShouldBeEmpty();
opts.LeafNode.Host.ShouldBe("127.0.0.1");
opts.LeafNode.Port.ShouldBe(3333);
opts.LeafNode.Username.ShouldBe("derek");
opts.LeafNode.Password.ShouldBe("s3cr3t!");
opts.LeafNode.AuthTimeout.ShouldBe(2.2);
opts.LeafNode.Advertise.ShouldBe("me:22");
}
[Fact] // T:2556
public void ParsingLeafNodeRemotes_ShouldSucceed()
{
var remotes = ServerOptions.ParseRemoteLeafNodes(
Arr(
Map(
("url", "nats-leaf://127.0.0.1:2222"),
("account", "foobar"),
("credentials", "./my.creds"))));
remotes.Count.ShouldBe(1);
remotes[0].Urls.Count.ShouldBe(1);
remotes[0].Urls[0].ToString().ShouldBe("nats-leaf://127.0.0.1:2222/");
remotes[0].LocalAccount.ShouldBe("foobar");
remotes[0].Credentials.ShouldContain("my.creds");
}
[Fact] // T:2560
public void SublistNoCacheConfig_ShouldSucceed()
{
var opts = new ServerOptions();
var error = opts.ProcessConfigString("""
{
"disable_sublist_cache": true
}
""");
error.ShouldBeNull();
opts.NoSublistCache.ShouldBeTrue();
}
[Fact] // T:2583
public void OptionsProxyRequired_ShouldSucceed()
{
var (authSingle, singleError) = ServerOptions.ParseAuthorization(
Map(
("user", "user"),
("password", "pwd"),
("proxy_required", true)));
singleError.ShouldBeNull();
authSingle.ShouldNotBeNull();
authSingle.ProxyRequired.ShouldBeTrue();
var (authUsers, usersError) = ServerOptions.ParseAuthorization(
Map(
("users", Arr(
Map(("user", "user1"), ("password", "pwd1")),
Map(("user", "user2"), ("password", "pwd2"), ("proxy_required", true)),
Map(("nkey", "UCARKS2E3KVB7YORL2DG34XLT7PUCOL2SVM7YXV6ETHLW6Z46UUJ2VZ3"), ("proxy_required", true)),
Map(("nkey", "UD6AYQSOIN2IN5OGC6VQZCR4H3UFMIOXSW6NNS6N53CLJA4PB56CEJJI"), ("proxy_required", false))))));
usersError.ShouldBeNull();
authUsers.ShouldNotBeNull();
authUsers.Users.Single(u => u.Username == "user2").ProxyRequired.ShouldBeTrue();
authUsers.Users.Single(u => u.Username == "user1").ProxyRequired.ShouldBeFalse();
authUsers.Nkeys.Single(n => n.Nkey.StartsWith("UCAR", StringComparison.Ordinal)).ProxyRequired.ShouldBeTrue();
}
[Fact] // T:2588
public void WebsocketPingIntervalConfig_ShouldSucceed()
{
var opts = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var parseError = ServerOptions.ParseWebsocket(
Map(("port", 8080L), ("ping_interval", "30s")),
opts,
errors,
warnings);
parseError.ShouldBeNull();
errors.ShouldBeEmpty();
opts.Websocket.PingInterval.ShouldBe(TimeSpan.FromSeconds(30));
parseError = ServerOptions.ParseWebsocket(
Map(("port", 8080L), ("ping_interval", 45L)),
opts,
errors,
warnings);
parseError.ShouldBeNull();
opts.Websocket.PingInterval.ShouldBe(TimeSpan.FromSeconds(45));
}
[Fact] // T:2586
public void WriteDeadlineConfigParsing_ShouldSucceed()
{
var options = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var leafError = ServerOptions.ParseLeafNodes(
new Dictionary<string, object?>
{
["write_deadline"] = "5s",
},
options,
errors,
warnings);
var gatewayError = ServerOptions.ParseGateway(
new Dictionary<string, object?>
{
["write_deadline"] = "6s",
},
options,
errors,
warnings);
var clusterError = ServerOptions.ParseCluster(
new Dictionary<string, object?>
{
["write_deadline"] = "7s",
},
options,
errors,
warnings);
options.WriteDeadline = ServerOptions.ParseDuration("write_deadline", "8s", errors, warnings);
leafError.ShouldBeNull();
gatewayError.ShouldBeNull();
clusterError.ShouldBeNull();
errors.ShouldBeEmpty();
options.LeafNode.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(5));
options.Gateway.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(6));
options.Cluster.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(7));
options.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(8));
}
[Fact] // T:2587
public void WriteTimeoutConfigParsing_ShouldSucceed()
{
var expectedPolicies = new Dictionary<string, WriteTimeoutPolicy>(StringComparer.Ordinal)
{
["default"] = WriteTimeoutPolicy.Default,
["retry"] = WriteTimeoutPolicy.Retry,
["close"] = WriteTimeoutPolicy.Close,
};
foreach (var (rawPolicy, expectedPolicy) in expectedPolicies)
{
var options = new ServerOptions();
var errors = new List<Exception>();
var warnings = new List<Exception>();
var leafError = ServerOptions.ParseLeafNodes(
new Dictionary<string, object?>
{
["write_timeout"] = rawPolicy,
},
options,
errors,
warnings);
var gatewayError = ServerOptions.ParseGateway(
new Dictionary<string, object?>
{
["write_timeout"] = rawPolicy,
},
options,
errors,
warnings);
var clusterError = ServerOptions.ParseCluster(
new Dictionary<string, object?>
{
["write_timeout"] = rawPolicy,
},
options,
errors,
warnings);
options.WriteTimeout = ServerOptions.ParseWriteDeadlinePolicy(rawPolicy, errors);
leafError.ShouldBeNull();
gatewayError.ShouldBeNull();
clusterError.ShouldBeNull();
errors.ShouldBeEmpty();
options.LeafNode.WriteTimeout.ShouldBe(expectedPolicy);
options.Gateway.WriteTimeout.ShouldBe(expectedPolicy);
options.Cluster.WriteTimeout.ShouldBe(expectedPolicy);
options.WriteTimeout.ShouldBe(expectedPolicy);
}
}
}