test: lock SubList remote-key and match behavior
This commit is contained in:
@@ -47,6 +47,23 @@ public class RouteRemoteSubCleanupParityBatch2Tests
|
|||||||
sl.HasRemoteInterest("B", "orders.created").ShouldBeTrue();
|
sl.HasRemoteInterest("B", "orders.created").ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Applying_same_remote_subscription_twice_is_idempotent_for_interest_tracking()
|
||||||
|
{
|
||||||
|
using var sl = new SubList();
|
||||||
|
var changes = new List<InterestChange>();
|
||||||
|
sl.InterestChanged += changes.Add;
|
||||||
|
|
||||||
|
var sub = new RemoteSubscription("orders.*", "workers", "r1", "A");
|
||||||
|
|
||||||
|
sl.ApplyRemoteSub(sub);
|
||||||
|
sl.ApplyRemoteSub(sub);
|
||||||
|
|
||||||
|
sl.HasRemoteInterest("A", "orders.created").ShouldBeTrue();
|
||||||
|
sl.MatchRemote("A", "orders.created").Count.ShouldBe(1);
|
||||||
|
changes.Count(change => change.Kind == InterestChangeKind.RemoteAdded).ShouldBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Route_disconnect_cleans_remote_interest_without_explicit_rs_minus()
|
public async Task Route_disconnect_cleans_remote_interest_without_explicit_rs_minus()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using NATS.Server.Subscriptions;
|
||||||
|
|
||||||
|
namespace NATS.Server.Core.Tests;
|
||||||
|
|
||||||
|
public class SubListAllocationGuardTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Remote_subscription_dictionary_uses_dedicated_routed_sub_key_type()
|
||||||
|
{
|
||||||
|
var field = typeof(SubList).GetField("_remoteSubs", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
field.ShouldNotBeNull();
|
||||||
|
field.FieldType.IsGenericType.ShouldBeTrue();
|
||||||
|
field.FieldType.GetGenericArguments()[0].Name.ShouldBe("RoutedSubKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Has_remote_interest_supports_exact_and_wildcard_subjects_per_account()
|
||||||
|
{
|
||||||
|
using var sl = new SubList();
|
||||||
|
sl.ApplyRemoteSub(new RemoteSubscription("orders.created", null, "r1", "A"));
|
||||||
|
sl.ApplyRemoteSub(new RemoteSubscription("orders.*", null, "r2", "A"));
|
||||||
|
sl.ApplyRemoteSub(new RemoteSubscription("payments.*", null, "r3", "B"));
|
||||||
|
|
||||||
|
sl.HasRemoteInterest("A", "orders.created").ShouldBeTrue();
|
||||||
|
sl.HasRemoteInterest("A", "orders.updated").ShouldBeTrue();
|
||||||
|
sl.HasRemoteInterest("A", "payments.created").ShouldBeFalse();
|
||||||
|
sl.HasRemoteInterest("B", "payments.posted").ShouldBeTrue();
|
||||||
|
sl.HasRemoteInterest("B", "orders.created").ShouldBeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Match_remote_reflects_queue_weight_updates_for_existing_remote_queue_sub()
|
||||||
|
{
|
||||||
|
using var sl = new SubList();
|
||||||
|
var sub = new RemoteSubscription("orders.*", "workers", "r1", "A", QueueWeight: 1);
|
||||||
|
sl.ApplyRemoteSub(sub);
|
||||||
|
|
||||||
|
sl.MatchRemote("A", "orders.created").Count.ShouldBe(1);
|
||||||
|
|
||||||
|
sl.UpdateRemoteQSub(sub with { QueueWeight = 4 });
|
||||||
|
|
||||||
|
var matches = sl.MatchRemote("A", "orders.created");
|
||||||
|
matches.Count.ShouldBe(4);
|
||||||
|
matches.ShouldAllBe(match => match.Queue == "workers");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,6 +199,33 @@ public class SubListGoParityTests
|
|||||||
sl.Match("foo.bar").PlainSubs.Length.ShouldBe(3);
|
sl.Match("foo.bar").PlainSubs.Length.ShouldBe(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Cache_generation_bump_rebuilds_match_result_after_insert_and_remove()
|
||||||
|
{
|
||||||
|
var sl = new SubList();
|
||||||
|
var exact = MakeSub("foo.bar", sid: "1");
|
||||||
|
var wildcard = MakeSub("foo.*", sid: "2");
|
||||||
|
|
||||||
|
sl.Insert(exact);
|
||||||
|
|
||||||
|
var first = sl.Match("foo.bar");
|
||||||
|
var second = sl.Match("foo.bar");
|
||||||
|
ReferenceEquals(first, second).ShouldBeTrue();
|
||||||
|
first.PlainSubs.Select(sub => sub.Sid).ShouldBe(["1"]);
|
||||||
|
|
||||||
|
sl.Insert(wildcard);
|
||||||
|
|
||||||
|
var afterInsert = sl.Match("foo.bar");
|
||||||
|
ReferenceEquals(afterInsert, first).ShouldBeFalse();
|
||||||
|
afterInsert.PlainSubs.Select(sub => sub.Sid).OrderBy(x => x).ToArray().ShouldBe(["1", "2"]);
|
||||||
|
|
||||||
|
sl.Remove(wildcard);
|
||||||
|
|
||||||
|
var afterRemove = sl.Match("foo.bar");
|
||||||
|
ReferenceEquals(afterRemove, afterInsert).ShouldBeFalse();
|
||||||
|
afterRemove.PlainSubs.Select(sub => sub.Sid).ShouldBe(["1"]);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty result is a shared singleton — two calls that yield no matches return
|
/// Empty result is a shared singleton — two calls that yield no matches return
|
||||||
/// the same object reference.
|
/// the same object reference.
|
||||||
|
|||||||
Reference in New Issue
Block a user