refactor: rename remaining tests to NATS.Server.Core.Tests
- Rename tests/NATS.Server.Tests -> tests/NATS.Server.Core.Tests - Update solution file, InternalsVisibleTo, and csproj references - Remove JETSTREAM_INTEGRATION_MATRIX and NATS.NKeys from csproj (moved to JetStream.Tests and Auth.Tests) - Update all namespaces from NATS.Server.Tests.* to NATS.Server.Core.Tests.* - Replace private GetFreePort/ReadUntilAsync helpers with TestUtilities calls - Fix stale namespace in Transport.Tests/NetworkingGoParityTests.cs
This commit is contained in:
0
tests/NATS.Server.Core.Tests/Internal/Gsl/.gitkeep
Normal file
0
tests/NATS.Server.Core.Tests/Internal/Gsl/.gitkeep
Normal file
@@ -0,0 +1,429 @@
|
||||
// Go reference: server/gsl/gsl_test.go
|
||||
// Tests for GenericSubjectList<T> trie-based subject matching.
|
||||
|
||||
using NATS.Server.Internal.Gsl;
|
||||
|
||||
namespace NATS.Server.Core.Tests.Internal.Gsl;
|
||||
|
||||
public class GenericSubjectListTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper: count matches for a subject.
|
||||
/// </summary>
|
||||
private static int CountMatches<T>(GenericSubjectList<T> s, string subject) where T : IEquatable<T>
|
||||
{
|
||||
var count = 0;
|
||||
s.Match(subject, _ => count++);
|
||||
return count;
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistInit server/gsl/gsl_test.go:23
|
||||
[Fact]
|
||||
public void Init_EmptyList()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Count.ShouldBe(0u);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistInsertCount server/gsl/gsl_test.go:29
|
||||
[Fact]
|
||||
public void InsertCount_TracksCorrectly()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo", 1);
|
||||
s.Insert("bar", 2);
|
||||
s.Insert("foo.bar", 3);
|
||||
s.Count.ShouldBe(3u);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistSimple server/gsl/gsl_test.go:37
|
||||
[Fact]
|
||||
public void Simple_ExactMatch()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo", 1);
|
||||
CountMatches(s, "foo").ShouldBe(1);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistSimpleMultiTokens server/gsl/gsl_test.go:43
|
||||
[Fact]
|
||||
public void SimpleMultiTokens_Match()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo.bar.baz", 1);
|
||||
CountMatches(s, "foo.bar.baz").ShouldBe(1);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistPartialWildcard server/gsl/gsl_test.go:49
|
||||
[Fact]
|
||||
public void PartialWildcard_StarMatches()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("a.b.c", 1);
|
||||
s.Insert("a.*.c", 2);
|
||||
CountMatches(s, "a.b.c").ShouldBe(2);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistPartialWildcardAtEnd server/gsl/gsl_test.go:56
|
||||
[Fact]
|
||||
public void PartialWildcardAtEnd_StarMatches()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("a.b.c", 1);
|
||||
s.Insert("a.b.*", 2);
|
||||
CountMatches(s, "a.b.c").ShouldBe(2);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistFullWildcard server/gsl/gsl_test.go:63
|
||||
[Fact]
|
||||
public void FullWildcard_GreaterThanMatches()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("a.b.c", 1);
|
||||
s.Insert("a.>", 2);
|
||||
CountMatches(s, "a.b.c").ShouldBe(2);
|
||||
CountMatches(s, "a.>").ShouldBe(1);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistRemove server/gsl/gsl_test.go:71
|
||||
[Fact]
|
||||
public void Remove_DecreasesCount()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
|
||||
s.Insert("a.b.c.d", 1);
|
||||
s.Count.ShouldBe(1u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(1);
|
||||
|
||||
s.Remove("a.b.c.d", 1);
|
||||
s.Count.ShouldBe(0u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistRemoveWildcard server/gsl/gsl_test.go:83
|
||||
[Fact]
|
||||
public void RemoveWildcard_CleansUp()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
|
||||
s.Insert("a.b.c.d", 11);
|
||||
s.Insert("a.b.*.d", 22);
|
||||
s.Insert("a.b.>", 33);
|
||||
s.Count.ShouldBe(3u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(3);
|
||||
|
||||
s.Remove("a.b.*.d", 22);
|
||||
s.Count.ShouldBe(2u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(2);
|
||||
|
||||
s.Remove("a.b.>", 33);
|
||||
s.Count.ShouldBe(1u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(1);
|
||||
|
||||
s.Remove("a.b.c.d", 11);
|
||||
s.Count.ShouldBe(0u);
|
||||
CountMatches(s, "a.b.c.d").ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistRemoveCleanup server/gsl/gsl_test.go:105
|
||||
[Fact]
|
||||
public void RemoveCleanup_PrunesEmptyNodes()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.NumLevels().ShouldBe(0);
|
||||
s.Insert("a.b.c.d.e.f", 1);
|
||||
s.NumLevels().ShouldBe(6);
|
||||
s.Remove("a.b.c.d.e.f", 1);
|
||||
s.NumLevels().ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistRemoveCleanupWildcards server/gsl/gsl_test.go:114
|
||||
[Fact]
|
||||
public void RemoveCleanupWildcards_PrunesEmptyNodes()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.NumLevels().ShouldBe(0);
|
||||
s.Insert("a.b.*.d.e.>", 1);
|
||||
s.NumLevels().ShouldBe(6);
|
||||
s.Remove("a.b.*.d.e.>", 1);
|
||||
s.NumLevels().ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistInvalidSubjectsInsert server/gsl/gsl_test.go:123
|
||||
[Fact]
|
||||
public void InvalidSubjectsInsert_RejectsInvalid()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
|
||||
// Empty tokens and FWC not terminal
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert(".foo", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert("foo.", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert("foo..bar", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert("foo.bar..baz", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert("foo.>.baz", 1));
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistBadSubjectOnRemove server/gsl/gsl_test.go:134
|
||||
[Fact]
|
||||
public void BadSubjectOnRemove_RejectsInvalid()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
Should.Throw<InvalidOperationException>(() => s.Insert("a.b..d", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Remove("a.b..d", 1));
|
||||
Should.Throw<InvalidOperationException>(() => s.Remove("a.>.b", 1));
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistTwoTokenPubMatchSingleTokenSub server/gsl/gsl_test.go:141
|
||||
[Fact]
|
||||
public void TwoTokenPub_DoesNotMatchSingleTokenSub()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo", 1);
|
||||
CountMatches(s, "foo").ShouldBe(1);
|
||||
CountMatches(s, "foo.bar").ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistInsertWithWildcardsAsLiterals server/gsl/gsl_test.go:148
|
||||
[Fact]
|
||||
public void InsertWithWildcardsAsLiterals_TreatsAsLiteral()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
var subjects = new[] { "foo.*-", "foo.>-" };
|
||||
for (var i = 0; i < subjects.Length; i++)
|
||||
{
|
||||
s.Insert(subjects[i], i);
|
||||
CountMatches(s, "foo.bar").ShouldBe(0);
|
||||
CountMatches(s, subjects[i]).ShouldBe(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistRemoveWithWildcardsAsLiterals server/gsl/gsl_test.go:157
|
||||
[Fact]
|
||||
public void RemoveWithWildcardsAsLiterals_RemovesCorrectly()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
var subjects = new[] { "foo.*-", "foo.>-" };
|
||||
for (var i = 0; i < subjects.Length; i++)
|
||||
{
|
||||
s.Insert(subjects[i], i);
|
||||
CountMatches(s, "foo.bar").ShouldBe(0);
|
||||
CountMatches(s, subjects[i]).ShouldBe(1);
|
||||
Should.Throw<KeyNotFoundException>(() => s.Remove("foo.bar", i));
|
||||
s.Count.ShouldBe(1u);
|
||||
s.Remove(subjects[i], i);
|
||||
s.Count.ShouldBe(0u);
|
||||
}
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistMatchWithEmptyTokens server/gsl/gsl_test.go:170
|
||||
[Theory]
|
||||
[InlineData(".foo")]
|
||||
[InlineData("..foo")]
|
||||
[InlineData("foo..")]
|
||||
[InlineData("foo.")]
|
||||
[InlineData("foo..bar")]
|
||||
[InlineData("foo...bar")]
|
||||
public void MatchWithEmptyTokens_HandlesEdgeCase(string subject)
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert(">", 1);
|
||||
CountMatches(s, subject).ShouldBe(0);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistHasInterest server/gsl/gsl_test.go:180
|
||||
[Fact]
|
||||
public void HasInterest_ReturnsTrueForMatchingSubjects()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo", 11);
|
||||
|
||||
// Expect to find that "foo" matches but "bar" doesn't.
|
||||
s.HasInterest("foo").ShouldBeTrue();
|
||||
s.HasInterest("bar").ShouldBeFalse();
|
||||
|
||||
// Call Match on a subject we know there is no match.
|
||||
CountMatches(s, "bar").ShouldBe(0);
|
||||
s.HasInterest("bar").ShouldBeFalse();
|
||||
|
||||
// Remove fooSub and check interest again
|
||||
s.Remove("foo", 11);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
|
||||
// Try with partial wildcard *
|
||||
s.Insert("foo.*", 22);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeTrue();
|
||||
s.HasInterest("foo.bar.baz").ShouldBeFalse();
|
||||
|
||||
// Remove sub, there should be no interest
|
||||
s.Remove("foo.*", 22);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar.baz").ShouldBeFalse();
|
||||
|
||||
// Try with full wildcard >
|
||||
s.Insert("foo.>", 33);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeTrue();
|
||||
s.HasInterest("foo.bar.baz").ShouldBeTrue();
|
||||
|
||||
s.Remove("foo.>", 33);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar.baz").ShouldBeFalse();
|
||||
|
||||
// Try with *.>
|
||||
s.Insert("*.>", 44);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeTrue();
|
||||
s.HasInterest("foo.baz").ShouldBeTrue();
|
||||
s.Remove("*.>", 44);
|
||||
|
||||
// Try with *.bar
|
||||
s.Insert("*.bar", 55);
|
||||
s.HasInterest("foo").ShouldBeFalse();
|
||||
s.HasInterest("foo.bar").ShouldBeTrue();
|
||||
s.HasInterest("foo.baz").ShouldBeFalse();
|
||||
s.Remove("*.bar", 55);
|
||||
|
||||
// Try with *
|
||||
s.Insert("*", 66);
|
||||
s.HasInterest("foo").ShouldBeTrue();
|
||||
s.HasInterest("foo.bar").ShouldBeFalse();
|
||||
s.Remove("*", 66);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistHasInterestOverlapping server/gsl/gsl_test.go:237
|
||||
[Fact]
|
||||
public void HasInterestOverlapping_HandlesOverlap()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("stream.A.child", 11);
|
||||
s.Insert("stream.*", 11);
|
||||
s.HasInterest("stream.A.child").ShouldBeTrue();
|
||||
s.HasInterest("stream.A").ShouldBeTrue();
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistHasInterestStartingInRace server/gsl/gsl_test.go:247
|
||||
[Fact]
|
||||
public async Task HasInterestStartingIn_ThreadSafe()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
|
||||
// Pre-populate with some patterns
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
s.Insert("foo.bar.baz", i);
|
||||
s.Insert("foo.*.baz", i + 10);
|
||||
s.Insert("foo.>", i + 20);
|
||||
}
|
||||
|
||||
const int iterations = 1000;
|
||||
var tasks = new List<Task>();
|
||||
|
||||
// Task 1: repeatedly call HasInterestStartingIn
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
s.HasInterestStartingIn("foo");
|
||||
s.HasInterestStartingIn("foo.bar");
|
||||
s.HasInterestStartingIn("foo.bar.baz");
|
||||
s.HasInterestStartingIn("other.subject");
|
||||
}
|
||||
}));
|
||||
|
||||
// Task 2: repeatedly modify the sublist
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
var val = 1000 + i;
|
||||
var ch = (char)('a' + (i % 26));
|
||||
s.Insert($"test.subject.{ch}", val);
|
||||
s.Insert("foo.*.test", val);
|
||||
s.Remove($"test.subject.{ch}", val);
|
||||
s.Remove("foo.*.test", val);
|
||||
}
|
||||
}));
|
||||
|
||||
// Task 3: also call HasInterest (which does lock)
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
s.HasInterest("foo.bar.baz");
|
||||
s.HasInterest("foo.something.baz");
|
||||
}
|
||||
}));
|
||||
|
||||
// Wait for all tasks - should not throw (no deadlocks or data races)
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
// Go: TestGenericSublistNumInterest server/gsl/gsl_test.go:298
|
||||
[Fact]
|
||||
public void NumInterest_CountsMatchingSubscriptions()
|
||||
{
|
||||
var s = new GenericSubjectList<int>();
|
||||
s.Insert("foo", 11);
|
||||
|
||||
// Helper to check both Match count and NumInterest agree
|
||||
void RequireNumInterest(string subj, int expected)
|
||||
{
|
||||
CountMatches(s, subj).ShouldBe(expected);
|
||||
s.NumInterest(subj).ShouldBe(expected);
|
||||
}
|
||||
|
||||
// Expect to find that "foo" matches but "bar" doesn't.
|
||||
RequireNumInterest("foo", 1);
|
||||
RequireNumInterest("bar", 0);
|
||||
|
||||
// Remove fooSub and check interest again
|
||||
s.Remove("foo", 11);
|
||||
RequireNumInterest("foo", 0);
|
||||
|
||||
// Try with partial wildcard *
|
||||
s.Insert("foo.*", 22);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 1);
|
||||
RequireNumInterest("foo.bar.baz", 0);
|
||||
|
||||
// Remove sub, there should be no interest
|
||||
s.Remove("foo.*", 22);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 0);
|
||||
RequireNumInterest("foo.bar.baz", 0);
|
||||
|
||||
// Full wildcard >
|
||||
s.Insert("foo.>", 33);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 1);
|
||||
RequireNumInterest("foo.bar.baz", 1);
|
||||
|
||||
s.Remove("foo.>", 33);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 0);
|
||||
RequireNumInterest("foo.bar.baz", 0);
|
||||
|
||||
// *.>
|
||||
s.Insert("*.>", 44);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 1);
|
||||
RequireNumInterest("foo.bar.baz", 1);
|
||||
s.Remove("*.>", 44);
|
||||
|
||||
// *.bar
|
||||
s.Insert("*.bar", 55);
|
||||
RequireNumInterest("foo", 0);
|
||||
RequireNumInterest("foo.bar", 1);
|
||||
RequireNumInterest("foo.bar.baz", 0);
|
||||
s.Remove("*.bar", 55);
|
||||
|
||||
// *
|
||||
s.Insert("*", 66);
|
||||
RequireNumInterest("foo", 1);
|
||||
RequireNumInterest("foo.bar", 0);
|
||||
s.Remove("*", 66);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user