feat(batch6-task2): implement F1 opts parsing and verify features
This commit is contained in:
@@ -1,123 +1,313 @@
|
||||
using Shouldly;
|
||||
using ZB.MOM.NatsNet.Server;
|
||||
using ZB.MOM.NatsNet.Server.Internal;
|
||||
using ZB.MOM.NatsNet.Server.Auth;
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
||||
|
||||
public sealed class ServerOptionsTests
|
||||
{
|
||||
[Fact] // T:2552
|
||||
public void AccountUsersLoadedProperly_ShouldSucceed()
|
||||
[Fact]
|
||||
public void DeepCopyURLs_WithEntries_ReturnsIndependentCopy()
|
||||
{
|
||||
var goFile = "server/opts_test.go";
|
||||
|
||||
goFile.ShouldStartWith("server/");
|
||||
|
||||
ServerConstants.DefaultPort.ShouldBe(4222);
|
||||
|
||||
ServerConstants.Version.ShouldNotBeNullOrWhiteSpace();
|
||||
|
||||
if (goFile.Contains("jetstream", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
goFile.Contains("store", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
var source = new List<Uri>
|
||||
{
|
||||
new("nats://127.0.0.1:4222"),
|
||||
new("nats://127.0.0.1:4223"),
|
||||
};
|
||||
|
||||
JetStreamVersioning.JsApiLevel.ShouldBeGreaterThanOrEqualTo(0);
|
||||
var copy = ServerOptions.DeepCopyURLs(source);
|
||||
|
||||
JetStreamVersioning.GetRequiredApiLevel(new Dictionary<string, string>()).ShouldBe(string.Empty);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
ServerUtilities.ParseSize("123"u8).ShouldBe(123);
|
||||
|
||||
ServerUtilities.ParseInt64("456"u8).ShouldBe(456);
|
||||
|
||||
}
|
||||
|
||||
"AccountUsersLoadedProperly_ShouldSucceed".ShouldContain("Should");
|
||||
|
||||
"TestAccountUsersLoadedProperly".ShouldNotBeNullOrWhiteSpace();
|
||||
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] // T:2561
|
||||
public void SublistNoCacheConfigOnAccounts_ShouldSucceed()
|
||||
[Fact]
|
||||
public void ProcessConfigFile_WithValidFile_ReturnsParsedOptions()
|
||||
{
|
||||
var goFile = "server/opts_test.go";
|
||||
|
||||
goFile.ShouldStartWith("server/");
|
||||
|
||||
ServerConstants.DefaultPort.ShouldBe(4222);
|
||||
|
||||
ServerConstants.Version.ShouldNotBeNullOrWhiteSpace();
|
||||
|
||||
if (goFile.Contains("jetstream", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
goFile.Contains("store", StringComparison.OrdinalIgnoreCase))
|
||||
var tempFile = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(tempFile, """
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"port": 4444,
|
||||
"system_account": "$SYS"
|
||||
}
|
||||
""");
|
||||
|
||||
JetStreamVersioning.JsApiLevel.ShouldBeGreaterThanOrEqualTo(0);
|
||||
|
||||
JetStreamVersioning.GetRequiredApiLevel(new Dictionary<string, string>()).ShouldBe(string.Empty);
|
||||
var options = ServerOptions.ProcessConfigFile(tempFile);
|
||||
|
||||
options.Host.ShouldBe("127.0.0.1");
|
||||
options.Port.ShouldBe(4444);
|
||||
options.SystemAccount.ShouldBe("$SYS");
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
finally
|
||||
{
|
||||
|
||||
ServerUtilities.ParseSize("123"u8).ShouldBe(123);
|
||||
|
||||
ServerUtilities.ParseInt64("456"u8).ShouldBe(456);
|
||||
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
|
||||
"SublistNoCacheConfigOnAccounts_ShouldSucceed".ShouldContain("Should");
|
||||
|
||||
"TestSublistNoCacheConfigOnAccounts".ShouldNotBeNullOrWhiteSpace();
|
||||
}
|
||||
|
||||
[Fact] // T:2585
|
||||
public void NewServerFromConfigVsLoadConfig_ShouldSucceed()
|
||||
[Fact]
|
||||
public void ConfigureSystemAccount_WithSystemAccountString_SetsValue()
|
||||
{
|
||||
var goFile = "server/opts_test.go";
|
||||
|
||||
goFile.ShouldStartWith("server/");
|
||||
|
||||
ServerConstants.DefaultPort.ShouldBe(4222);
|
||||
|
||||
ServerConstants.Version.ShouldNotBeNullOrWhiteSpace();
|
||||
|
||||
if (goFile.Contains("jetstream", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
goFile.Contains("store", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
var options = new ServerOptions();
|
||||
var config = new Dictionary<string, object?>
|
||||
{
|
||||
["system_account"] = "$SYSX",
|
||||
};
|
||||
|
||||
JetStreamVersioning.JsApiLevel.ShouldBeGreaterThanOrEqualTo(0);
|
||||
var error = ServerOptions.ConfigureSystemAccount(options, config);
|
||||
|
||||
JetStreamVersioning.GetRequiredApiLevel(new Dictionary<string, string>()).ShouldBe(string.Empty);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
ServerUtilities.ParseSize("123"u8).ShouldBe(123);
|
||||
|
||||
ServerUtilities.ParseInt64("456"u8).ShouldBe(456);
|
||||
|
||||
}
|
||||
|
||||
"NewServerFromConfigVsLoadConfig_ShouldSucceed".ShouldContain("Should");
|
||||
|
||||
"TestNewServerFromConfigVsLoadConfig".ShouldNotBeNullOrWhiteSpace();
|
||||
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:2586
|
||||
public void WriteDeadlineConfigParsing_ShouldSucceed()
|
||||
{
|
||||
var options = new ServerOptions();
|
||||
var errors = new List<Exception>();
|
||||
var warnings = new List<Exception>();
|
||||
|
||||
ServerOptions.ParseCluster(
|
||||
new Dictionary<string, object?>
|
||||
{
|
||||
["write_deadline"] = "12s",
|
||||
},
|
||||
options,
|
||||
errors,
|
||||
warnings);
|
||||
|
||||
errors.ShouldBeEmpty();
|
||||
options.Cluster.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(12));
|
||||
}
|
||||
|
||||
[Fact] // T:2587
|
||||
public void WriteTimeoutConfigParsing_ShouldSucceed()
|
||||
{
|
||||
var options = new ServerOptions();
|
||||
var errors = new List<Exception>();
|
||||
var warnings = new List<Exception>();
|
||||
|
||||
ServerOptions.ParseGateway(
|
||||
new Dictionary<string, object?>
|
||||
{
|
||||
["write_timeout"] = "retry",
|
||||
},
|
||||
options,
|
||||
errors,
|
||||
warnings);
|
||||
|
||||
errors.ShouldBeEmpty();
|
||||
options.Gateway.WriteTimeout.ShouldBe(WriteTimeoutPolicy.Retry);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user