Move 25 gateway-related test files from NATS.Server.Tests into a dedicated NATS.Server.Gateways.Tests project. Update namespaces, replace private ReadUntilAsync with SocketTestHelper from TestUtilities, inline TestServerFactory usage, add InternalsVisibleTo, and register the project in the solution file. All 261 tests pass.
141 lines
5.0 KiB
C#
141 lines
5.0 KiB
C#
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NATS.Client.Core;
|
|
using NATS.Server.Auth;
|
|
using NATS.Server.Configuration;
|
|
|
|
namespace NATS.Server.Gateways.Tests.Gateways;
|
|
|
|
public class GatewayAccountScopedDeliveryTests
|
|
{
|
|
[Fact]
|
|
public async Task Remote_message_delivery_uses_target_account_sublist_not_global_sublist()
|
|
{
|
|
const string subject = "orders.created";
|
|
await using var fixture = await GatewayAccountDeliveryFixture.StartAsync();
|
|
|
|
await using var remoteAccountA = await fixture.ConnectAsync(fixture.Remote, "a_sub");
|
|
await using var remoteAccountB = await fixture.ConnectAsync(fixture.Remote, "b_sub");
|
|
await using var publisher = await fixture.ConnectAsync(fixture.Local, "a_pub");
|
|
|
|
await using var subA = await remoteAccountA.SubscribeCoreAsync<string>(subject);
|
|
await using var subB = await remoteAccountB.SubscribeCoreAsync<string>(subject);
|
|
await remoteAccountA.PingAsync();
|
|
await remoteAccountB.PingAsync();
|
|
await fixture.WaitForRemoteInterestOnLocalAsync("A", subject);
|
|
|
|
await publisher.PublishAsync(subject, "from-gateway-a");
|
|
|
|
using var receiveTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
|
var msgA = await subA.Msgs.ReadAsync(receiveTimeout.Token);
|
|
msgA.Data.ShouldBe("from-gateway-a");
|
|
|
|
using var leakTimeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
|
|
await Should.ThrowAsync<OperationCanceledException>(async () =>
|
|
await subB.Msgs.ReadAsync(leakTimeout.Token));
|
|
}
|
|
}
|
|
|
|
internal sealed class GatewayAccountDeliveryFixture : IAsyncDisposable
|
|
{
|
|
private readonly CancellationTokenSource _localCts;
|
|
private readonly CancellationTokenSource _remoteCts;
|
|
|
|
private GatewayAccountDeliveryFixture(NatsServer local, NatsServer remote, CancellationTokenSource localCts, CancellationTokenSource remoteCts)
|
|
{
|
|
Local = local;
|
|
Remote = remote;
|
|
_localCts = localCts;
|
|
_remoteCts = remoteCts;
|
|
}
|
|
|
|
public NatsServer Local { get; }
|
|
public NatsServer Remote { get; }
|
|
|
|
public static async Task<GatewayAccountDeliveryFixture> StartAsync()
|
|
{
|
|
var users = new User[]
|
|
{
|
|
new() { Username = "a_pub", Password = "pass", Account = "A" },
|
|
new() { Username = "a_sub", Password = "pass", Account = "A" },
|
|
new() { Username = "b_sub", Password = "pass", Account = "B" },
|
|
};
|
|
|
|
var localOptions = new NatsOptions
|
|
{
|
|
Host = "127.0.0.1",
|
|
Port = 0,
|
|
Users = users,
|
|
Gateway = new GatewayOptions
|
|
{
|
|
Name = "LOCAL",
|
|
Host = "127.0.0.1",
|
|
Port = 0,
|
|
},
|
|
};
|
|
|
|
var local = new NatsServer(localOptions, NullLoggerFactory.Instance);
|
|
var localCts = new CancellationTokenSource();
|
|
_ = local.StartAsync(localCts.Token);
|
|
await local.WaitForReadyAsync();
|
|
|
|
var remoteOptions = new NatsOptions
|
|
{
|
|
Host = "127.0.0.1",
|
|
Port = 0,
|
|
Users = users,
|
|
Gateway = new GatewayOptions
|
|
{
|
|
Name = "REMOTE",
|
|
Host = "127.0.0.1",
|
|
Port = 0,
|
|
Remotes = [local.GatewayListen!],
|
|
},
|
|
};
|
|
|
|
var remote = new NatsServer(remoteOptions, NullLoggerFactory.Instance);
|
|
var remoteCts = new CancellationTokenSource();
|
|
_ = remote.StartAsync(remoteCts.Token);
|
|
await remote.WaitForReadyAsync();
|
|
|
|
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
|
while (!timeout.IsCancellationRequested && (local.Stats.Gateways == 0 || remote.Stats.Gateways == 0))
|
|
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
|
|
|
|
return new GatewayAccountDeliveryFixture(local, remote, localCts, remoteCts);
|
|
}
|
|
|
|
public async Task<NatsConnection> ConnectAsync(NatsServer server, string username)
|
|
{
|
|
var connection = new NatsConnection(new NatsOpts
|
|
{
|
|
Url = $"nats://{username}:pass@127.0.0.1:{server.Port}",
|
|
});
|
|
await connection.ConnectAsync();
|
|
return connection;
|
|
}
|
|
|
|
public async Task WaitForRemoteInterestOnLocalAsync(string account, string subject)
|
|
{
|
|
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
|
while (!timeout.IsCancellationRequested)
|
|
{
|
|
if (Local.HasRemoteInterest(account, subject))
|
|
return;
|
|
|
|
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
|
|
}
|
|
|
|
throw new TimeoutException($"Timed out waiting for remote interest {account}:{subject}.");
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
await _localCts.CancelAsync();
|
|
await _remoteCts.CancelAsync();
|
|
Local.Dispose();
|
|
Remote.Dispose();
|
|
_localCts.Dispose();
|
|
_remoteCts.Dispose();
|
|
}
|
|
}
|