Adds per-account subscription tracking to GatewayConnection via AddAccountSubscription/RemoveAccountSubscription/GetAccountSubscriptions/ AccountSubscriptionCount, with corresponding SendAccountSubscriptions and GetAccountSubscriptions helpers on GatewayManager. Covered by 10 new unit tests.
156 lines
5.2 KiB
C#
156 lines
5.2 KiB
C#
using System.Net;
|
|
using System.Net.Sockets;
|
|
using NATS.Server.Gateways;
|
|
using Shouldly;
|
|
|
|
namespace NATS.Server.Tests.Gateways;
|
|
|
|
/// <summary>
|
|
/// Tests for queue group subscription tracking on GatewayConnection.
|
|
/// Go reference: gateway.go — sendQueueSubsToGateway, queueSubscriptions state.
|
|
/// </summary>
|
|
public class QueueGroupPropagationTests : IAsyncDisposable
|
|
{
|
|
private readonly TcpListener _listener;
|
|
private readonly Socket _clientSocket;
|
|
private readonly Socket _serverSocket;
|
|
private readonly GatewayConnection _gw;
|
|
|
|
public QueueGroupPropagationTests()
|
|
{
|
|
_listener = new TcpListener(IPAddress.Loopback, 0);
|
|
_listener.Start();
|
|
var port = ((IPEndPoint)_listener.LocalEndpoint).Port;
|
|
|
|
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
_clientSocket.Connect(IPAddress.Loopback, port);
|
|
_serverSocket = _listener.AcceptSocket();
|
|
|
|
_gw = new GatewayConnection(_serverSocket);
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
await _gw.DisposeAsync();
|
|
_clientSocket.Dispose();
|
|
_listener.Stop();
|
|
}
|
|
|
|
// Go: gateway.go — sendQueueSubsToGateway registers queue group subscriptions per subject
|
|
[Fact]
|
|
public void AddQueueSubscription_registers_group()
|
|
{
|
|
_gw.AddQueueSubscription("orders.new", "workers");
|
|
|
|
_gw.HasQueueSubscription("orders.new", "workers").ShouldBeTrue();
|
|
}
|
|
|
|
// Go: gateway.go — getQueueGroups returns all groups for a subject
|
|
[Fact]
|
|
public void GetQueueGroups_returns_groups_for_subject()
|
|
{
|
|
_gw.AddQueueSubscription("payments.>", "billing");
|
|
_gw.AddQueueSubscription("payments.>", "audit");
|
|
|
|
var groups = _gw.GetQueueGroups("payments.>");
|
|
|
|
groups.ShouldContain("billing");
|
|
groups.ShouldContain("audit");
|
|
groups.Count.ShouldBe(2);
|
|
}
|
|
|
|
// Go: gateway.go — removeQueueSubscription removes a specific queue group
|
|
[Fact]
|
|
public void RemoveQueueSubscription_removes_group()
|
|
{
|
|
_gw.AddQueueSubscription("events.click", "analytics");
|
|
_gw.AddQueueSubscription("events.click", "logging");
|
|
|
|
_gw.RemoveQueueSubscription("events.click", "analytics");
|
|
|
|
_gw.HasQueueSubscription("events.click", "analytics").ShouldBeFalse();
|
|
_gw.HasQueueSubscription("events.click", "logging").ShouldBeTrue();
|
|
}
|
|
|
|
// Go: gateway.go — hasQueueSubscription returns true for registered subject/group pair
|
|
[Fact]
|
|
public void HasQueueSubscription_true_when_registered()
|
|
{
|
|
_gw.AddQueueSubscription("tasks.process", "pool");
|
|
|
|
_gw.HasQueueSubscription("tasks.process", "pool").ShouldBeTrue();
|
|
}
|
|
|
|
// Go: gateway.go — hasQueueSubscription returns false for unknown pair
|
|
[Fact]
|
|
public void HasQueueSubscription_false_when_not_registered()
|
|
{
|
|
_gw.HasQueueSubscription("unknown.subject", "nonexistent-group").ShouldBeFalse();
|
|
}
|
|
|
|
// Go: gateway.go — multiple queue groups can be registered for the same subject
|
|
[Fact]
|
|
public void Multiple_groups_per_subject()
|
|
{
|
|
_gw.AddQueueSubscription("jobs.run", "fast");
|
|
_gw.AddQueueSubscription("jobs.run", "slow");
|
|
_gw.AddQueueSubscription("jobs.run", "batch");
|
|
|
|
var groups = _gw.GetQueueGroups("jobs.run");
|
|
|
|
groups.Count.ShouldBe(3);
|
|
groups.ShouldContain("fast");
|
|
groups.ShouldContain("slow");
|
|
groups.ShouldContain("batch");
|
|
}
|
|
|
|
// Go: gateway.go — subscriptions for different subjects are tracked independently
|
|
[Fact]
|
|
public void Different_subjects_tracked_independently()
|
|
{
|
|
_gw.AddQueueSubscription("subject.a", "group1");
|
|
_gw.AddQueueSubscription("subject.b", "group2");
|
|
|
|
_gw.HasQueueSubscription("subject.a", "group2").ShouldBeFalse();
|
|
_gw.HasQueueSubscription("subject.b", "group1").ShouldBeFalse();
|
|
_gw.HasQueueSubscription("subject.a", "group1").ShouldBeTrue();
|
|
_gw.HasQueueSubscription("subject.b", "group2").ShouldBeTrue();
|
|
}
|
|
|
|
// Go: gateway.go — QueueSubscriptionCount reflects number of distinct subjects with queue groups
|
|
[Fact]
|
|
public void QueueSubscriptionCount_tracks_subjects()
|
|
{
|
|
_gw.QueueSubscriptionCount.ShouldBe(0);
|
|
|
|
_gw.AddQueueSubscription("foo", "g1");
|
|
_gw.QueueSubscriptionCount.ShouldBe(1);
|
|
|
|
_gw.AddQueueSubscription("bar", "g2");
|
|
_gw.QueueSubscriptionCount.ShouldBe(2);
|
|
|
|
// Adding a second group to an existing subject does not increase count
|
|
_gw.AddQueueSubscription("foo", "g3");
|
|
_gw.QueueSubscriptionCount.ShouldBe(2);
|
|
}
|
|
|
|
// Go: gateway.go — removing a queue group that was never added is a no-op
|
|
[Fact]
|
|
public void RemoveQueueSubscription_no_error_for_unknown()
|
|
{
|
|
// Should not throw even though neither subject nor group was registered
|
|
var act = () => _gw.RemoveQueueSubscription("never.registered", "ghost");
|
|
act.ShouldNotThrow();
|
|
}
|
|
|
|
// Go: gateway.go — GetQueueGroups returns empty set for unknown subject
|
|
[Fact]
|
|
public void GetQueueGroups_empty_for_unknown_subject()
|
|
{
|
|
var groups = _gw.GetQueueGroups("nonexistent.subject");
|
|
|
|
groups.ShouldNotBeNull();
|
|
groups.Count.ShouldBe(0);
|
|
}
|
|
}
|