// Copyright 2012-2025 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using Shouldly; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.Internal; /// /// Tests for . /// Mirrors server/util_test.go: TestParseSize (ID 3061), TestParseSInt64 (ID 3062), /// TestParseHostPort (ID 3063), TestURLsAreEqual (ID 3064), TestComma (ID 3065), /// TestURLRedaction (ID 3066), TestVersionAtLeast (ID 3067). /// Benchmarks (IDs 3068–3073) are n/a. /// public sealed class ServerUtilitiesTests { [Fact] public void ParseSize_ShouldParseValidAndRejectInvalid() { // Mirror: TestParseSize ServerUtilities.ParseSize(ReadOnlySpan.Empty).ShouldBe(-1, "nil/empty should return -1"); var n = "12345678"u8; ServerUtilities.ParseSize(n).ShouldBe(12345678); var bad = "12345invalid678"u8; ServerUtilities.ParseSize(bad).ShouldBe(-1, "non-digit chars should return -1"); } [Fact] public void ParseInt64_ShouldParseValidAndRejectInvalid() { // Mirror: TestParseSInt64 ServerUtilities.ParseInt64(ReadOnlySpan.Empty).ShouldBe(-1L, "empty should return -1"); var n = "12345678"u8; ServerUtilities.ParseInt64(n).ShouldBe(12345678L); var bad = "12345invalid678"u8; ServerUtilities.ParseInt64(bad).ShouldBe(-1L, "non-digit chars should return -1"); } [Fact] public void ParseHostPort_ShouldSplitCorrectly() { // Mirror: TestParseHostPort void Check(string hostPort, int defaultPort, string expectedHost, int expectedPort, bool expectError) { var (host, port, err) = ServerUtilities.ParseHostPort(hostPort, defaultPort); if (expectError) { err.ShouldNotBeNull($"expected error for hostPort={hostPort}"); return; } err.ShouldBeNull($"unexpected error for hostPort={hostPort}: {err?.Message}"); host.ShouldBe(expectedHost); port.ShouldBe(expectedPort); } Check("addr:1234", 5678, "addr", 1234, false); Check(" addr:1234 ", 5678, "addr", 1234, false); Check(" addr : 1234 ", 5678, "addr", 1234, false); Check("addr", 5678, "addr", 5678, false); // no port → default Check(" addr ", 5678, "addr", 5678, false); Check("addr:-1", 5678, "addr", 5678, false); // -1 → default Check(" addr:-1 ", 5678, "addr", 5678, false); Check(" addr : -1 ", 5678, "addr", 5678, false); Check("addr:0", 5678, "addr", 5678, false); // 0 → default Check(" addr:0 ", 5678, "addr", 5678, false); Check(" addr : 0 ", 5678, "addr", 5678, false); Check("addr:addr", 0, "", 0, true); // non-numeric port Check("addr:::1234", 0, "", 0, true); // ambiguous colons Check("", 0, "", 0, true); // empty } [Fact] public void UrlsAreEqual_ShouldCompareCorrectly() { // Mirror: TestURLsAreEqual void Check(string u1Str, string u2Str, bool expectedSame) { var u1 = new Uri(u1Str); var u2 = new Uri(u2Str); ServerUtilities.UrlsAreEqual(u1, u2).ShouldBe(expectedSame, $"expected {u1Str} and {u2Str} to be {(expectedSame ? "equal" : "different")}"); } Check("nats://localhost:4222", "nats://localhost:4222", true); Check("nats://ivan:pwd@localhost:4222", "nats://ivan:pwd@localhost:4222", true); Check("nats://ivan@localhost:4222", "nats://ivan@localhost:4222", true); Check("nats://ivan:@localhost:4222", "nats://ivan:@localhost:4222", true); Check("nats://host1:4222", "nats://host2:4222", false); } [Fact] public void Comma_ShouldFormatWithThousandSeparators() { // Mirror: TestComma var cases = new (long input, string expected)[] { (0, "0"), (10, "10"), (100, "100"), (1_000, "1,000"), (10_000, "10,000"), (100_000, "100,000"), (10_000_000, "10,000,000"), (10_100_000, "10,100,000"), (10_010_000, "10,010,000"), (10_001_000, "10,001,000"), (123_456_789, "123,456,789"), (9_223_372_036_854_775_807L, "9,223,372,036,854,775,807"), // long.MaxValue (long.MinValue, "-9,223,372,036,854,775,808"), (-123_456_789, "-123,456,789"), (-10_100_000, "-10,100,000"), (-10_010_000, "-10,010,000"), (-10_001_000, "-10,001,000"), (-10_000_000, "-10,000,000"), (-100_000, "-100,000"), (-10_000, "-10,000"), (-1_000, "-1,000"), (-100, "-100"), (-10, "-10"), }; foreach (var (input, expected) in cases) ServerUtilities.Comma(input).ShouldBe(expected, $"Comma({input})"); } [Fact] public void UrlRedaction_ShouldReplacePasswords() { // Mirror: TestURLRedaction var cases = new (string full, string safe)[] { ("nats://foo:bar@example.org", "nats://foo:xxxxx@example.org"), ("nats://foo@example.org", "nats://foo@example.org"), ("nats://example.org", "nats://example.org"), ("nats://example.org/foo?bar=1", "nats://example.org/foo?bar=1"), }; var listFull = new Uri[cases.Length]; var listSafe = new Uri[cases.Length]; for (var i = 0; i < cases.Length; i++) { ServerUtilities.RedactUrlString(cases[i].full).ShouldBe(cases[i].safe, $"RedactUrlString[{i}]"); listFull[i] = new Uri(cases[i].full); listSafe[i] = new Uri(cases[i].safe); } var results = ServerUtilities.RedactUrlList(listFull); for (var i = 0; i < results.Length; i++) results[i].ToString().ShouldBe(listSafe[i].ToString(), $"RedactUrlList[{i}]"); } [Fact] public void VersionAtLeast_ShouldReturnCorrectResult() { // Mirror: TestVersionAtLeast var cases = new (string version, int major, int minor, int update, bool result)[] { ("2.0.0-beta", 1, 9, 9, true), ("2.0.0", 1, 99, 9, true), ("2.2.0", 2, 1, 9, true), ("2.2.2", 2, 2, 2, true), ("2.2.2", 2, 2, 3, false), ("2.2.2", 2, 3, 2, false), ("2.2.2", 3, 2, 2, false), ("2.22.2", 3, 0, 0, false), ("2.2.22", 2, 3, 0, false), ("bad.version",1, 2, 3, false), }; foreach (var (version, major, minor, update, expected) in cases) { ServerUtilities.VersionAtLeast(version, major, minor, update) .ShouldBe(expected, $"VersionAtLeast({version}, {major}, {minor}, {update})"); } } }