Fix E2E test gaps and add comprehensive E2E + parity test suites
- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
This commit is contained in:
98
tests/NATS.E2E.Tests/AccountIsolationTests.cs
Normal file
98
tests/NATS.E2E.Tests/AccountIsolationTests.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using NATS.Client.Core;
|
||||
using NATS.E2E.Tests.Infrastructure;
|
||||
|
||||
namespace NATS.E2E.Tests;
|
||||
|
||||
[Collection("E2E-Accounts")]
|
||||
public class AccountIsolationTests(AccountServerFixture fixture)
|
||||
{
|
||||
[Fact]
|
||||
public async Task Accounts_SameAccount_MessageDelivered()
|
||||
{
|
||||
await using var pub = fixture.CreateClientA();
|
||||
await using var sub = fixture.CreateClientA();
|
||||
|
||||
await pub.ConnectAsync();
|
||||
await sub.ConnectAsync();
|
||||
|
||||
await using var subscription = await sub.SubscribeCoreAsync<string>("acct.test");
|
||||
await sub.PingAsync();
|
||||
|
||||
await pub.PublishAsync("acct.test", "intra-account");
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
var msg = await subscription.Msgs.ReadAsync(cts.Token);
|
||||
|
||||
msg.Data.ShouldBe("intra-account");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Accounts_CrossAccount_MessageNotDelivered()
|
||||
{
|
||||
await using var pub = fixture.CreateClientA();
|
||||
await using var sub = fixture.CreateClientB();
|
||||
|
||||
await pub.ConnectAsync();
|
||||
await sub.ConnectAsync();
|
||||
|
||||
await using var subscription = await sub.SubscribeCoreAsync<string>("cross.test");
|
||||
await sub.PingAsync();
|
||||
|
||||
await pub.PublishAsync("cross.test", "cross-account");
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
var readTask = subscription.Msgs.ReadAsync(cts.Token).AsTask();
|
||||
var completed = await Task.WhenAny(readTask, Task.Delay(1000));
|
||||
|
||||
completed.ShouldNotBe(readTask);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Accounts_EachAccountHasOwnNamespace()
|
||||
{
|
||||
await using var pubA = fixture.CreateClientA();
|
||||
await using var subA = fixture.CreateClientA();
|
||||
await using var pubB = fixture.CreateClientB();
|
||||
await using var subB = fixture.CreateClientB();
|
||||
|
||||
await pubA.ConnectAsync();
|
||||
await subA.ConnectAsync();
|
||||
await pubB.ConnectAsync();
|
||||
await subB.ConnectAsync();
|
||||
|
||||
await using var subscriptionA = await subA.SubscribeCoreAsync<string>("shared.topic");
|
||||
await using var subscriptionB = await subB.SubscribeCoreAsync<string>("shared.topic");
|
||||
|
||||
await subA.PingAsync();
|
||||
await subB.PingAsync();
|
||||
|
||||
// Publish from ACCT_A — only ACCT_A subscriber should receive
|
||||
await pubA.PublishAsync("shared.topic", "from-a");
|
||||
|
||||
using var ctA = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
var msgA = await subscriptionA.Msgs.ReadAsync(ctA.Token);
|
||||
msgA.Data.ShouldBe("from-a");
|
||||
|
||||
using var ctsBNoMsg = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
var readBTask = subscriptionB.Msgs.ReadAsync(ctsBNoMsg.Token).AsTask();
|
||||
var completedB = await Task.WhenAny(readBTask, Task.Delay(1000));
|
||||
completedB.ShouldNotBe(readBTask);
|
||||
// Cancel the abandoned read so it doesn't consume the next message
|
||||
await ctsBNoMsg.CancelAsync();
|
||||
try { await readBTask; } catch (OperationCanceledException) { }
|
||||
|
||||
// Publish from ACCT_B — only ACCT_B subscriber should receive
|
||||
await pubB.PublishAsync("shared.topic", "from-b");
|
||||
|
||||
using var ctB = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
var msgB = await subscriptionB.Msgs.ReadAsync(ctB.Token);
|
||||
msgB.Data.ShouldBe("from-b");
|
||||
|
||||
using var ctsANoMsg = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
var readATask2 = subscriptionA.Msgs.ReadAsync(ctsANoMsg.Token).AsTask();
|
||||
var completedA2 = await Task.WhenAny(readATask2, Task.Delay(1000));
|
||||
completedA2.ShouldNotBe(readATask2);
|
||||
await ctsANoMsg.CancelAsync();
|
||||
try { await readATask2; } catch (OperationCanceledException) { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user