// Port of Go server/accounts_test.go — TestSystemAccountDefaultCreation,
// TestSystemAccountSysSubjectRouting, TestNonSystemAccountCannotSubscribeToSys.
// Reference: golang/nats-server/server/accounts_test.go, server.go — initSystemAccount.
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth;
///
/// Tests for the $SYS system account functionality including:
/// - Default system account creation with IsSystemAccount flag
/// - $SYS.> subject routing to the system account's SubList
/// - Non-system accounts blocked from subscribing to $SYS.> subjects
/// - System account event publishing
/// Reference: Go server/accounts.go — isSystemAccount, isReservedSubject.
///
public class SystemAccountTests
{
// ─── Helpers ────────────────────────────────────────────────────────────
private static int GetFreePort()
{
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
return ((IPEndPoint)sock.LocalEndPoint!).Port;
}
private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{
var port = GetFreePort();
options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource();
_ = server.StartAsync(cts.Token);
await server.WaitForReadyAsync();
return (server, port, cts);
}
private static async Task RawConnectAsync(int port)
{
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await sock.ConnectAsync(IPAddress.Loopback, port);
var buf = new byte[4096];
await sock.ReceiveAsync(buf, SocketFlags.None);
return sock;
}
private static async Task ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
{
using var cts = new CancellationTokenSource(timeoutMs);
var sb = new StringBuilder();
var buf = new byte[4096];
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
{
int n;
try { n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); }
catch (OperationCanceledException) { break; }
if (n == 0) break;
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
}
return sb.ToString();
}
// ─── Tests ──────────────────────────────────────────────────────────────
///
/// Verifies that the server creates a $SYS system account by default with
/// IsSystemAccount set to true.
/// Reference: Go server/server.go — initSystemAccount.
///
[Fact]
public void Default_system_account_is_created()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
server.SystemAccount.ShouldNotBeNull();
server.SystemAccount.Name.ShouldBe(Account.SystemAccountName);
server.SystemAccount.IsSystemAccount.ShouldBeTrue();
}
///
/// Verifies that the system account constant matches "$SYS".
///
[Fact]
public void System_account_name_constant_is_correct()
{
Account.SystemAccountName.ShouldBe("$SYS");
}
///
/// Verifies that a non-system account does not have IsSystemAccount set.
///
[Fact]
public void Regular_account_is_not_system_account()
{
var account = new Account("test-account");
account.IsSystemAccount.ShouldBeFalse();
}
///
/// Verifies that IsSystemAccount can be explicitly set on an account.
///
[Fact]
public void IsSystemAccount_can_be_set()
{
var account = new Account("custom-sys") { IsSystemAccount = true };
account.IsSystemAccount.ShouldBeTrue();
}
///
/// Verifies that IsSystemSubject correctly identifies $SYS subjects.
/// Reference: Go server/server.go — isReservedSubject.
///
[Theory]
[InlineData("$SYS", true)]
[InlineData("$SYS.ACCOUNT.test.CONNECT", true)]
[InlineData("$SYS.SERVER.abc.STATSZ", true)]
[InlineData("$SYS.REQ.SERVER.PING.VARZ", true)]
[InlineData("foo.bar", false)]
[InlineData("$G", false)]
[InlineData("SYS.test", false)]
[InlineData("$JS.API.STREAM.LIST", false)]
[InlineData("$SYS.", true)]
public void IsSystemSubject_identifies_sys_subjects(string subject, bool expected)
{
NatsServer.IsSystemSubject(subject).ShouldBe(expected);
}
///
/// Verifies that the system account is listed among server accounts.
///
[Fact]
public void System_account_is_in_server_accounts()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
var accounts = server.GetAccounts().ToList();
accounts.ShouldContain(a => a.Name == Account.SystemAccountName && a.IsSystemAccount);
}
///
/// Verifies that IsSubscriptionAllowed blocks non-system accounts from $SYS.> subjects.
/// Reference: Go server/accounts.go — isReservedForSys.
///
[Fact]
public void Non_system_account_cannot_subscribe_to_sys_subjects()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
var regularAccount = new Account("regular");
server.IsSubscriptionAllowed(regularAccount, "$SYS.SERVER.abc.STATSZ").ShouldBeFalse();
server.IsSubscriptionAllowed(regularAccount, "$SYS.ACCOUNT.test.CONNECT").ShouldBeFalse();
server.IsSubscriptionAllowed(regularAccount, "$SYS.REQ.SERVER.PING.VARZ").ShouldBeFalse();
}
///
/// Verifies that the system account IS allowed to subscribe to $SYS.> subjects.
///
[Fact]
public void System_account_can_subscribe_to_sys_subjects()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
server.IsSubscriptionAllowed(server.SystemAccount, "$SYS.SERVER.abc.STATSZ").ShouldBeTrue();
server.IsSubscriptionAllowed(server.SystemAccount, "$SYS.ACCOUNT.test.CONNECT").ShouldBeTrue();
}
///
/// Verifies that any account can subscribe to non-$SYS subjects.
///
[Fact]
public void Any_account_can_subscribe_to_regular_subjects()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
var regularAccount = new Account("regular");
server.IsSubscriptionAllowed(regularAccount, "foo.bar").ShouldBeTrue();
server.IsSubscriptionAllowed(regularAccount, "$JS.API.STREAM.LIST").ShouldBeTrue();
server.IsSubscriptionAllowed(server.SystemAccount, "foo.bar").ShouldBeTrue();
}
///
/// Verifies that GetSubListForSubject routes $SYS subjects to the system account's SubList.
/// Reference: Go server/server.go — sublist routing for internal subjects.
///
[Fact]
public void GetSubListForSubject_routes_sys_to_system_account()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
var globalAccount = server.GetOrCreateAccount(Account.GlobalAccountName);
// $SYS subjects should route to the system account's SubList
var sysList = server.GetSubListForSubject(globalAccount, "$SYS.SERVER.abc.STATSZ");
sysList.ShouldBeSameAs(server.SystemAccount.SubList);
// Regular subjects should route to the specified account's SubList
var regularList = server.GetSubListForSubject(globalAccount, "foo.bar");
regularList.ShouldBeSameAs(globalAccount.SubList);
}
///
/// Verifies that the EventSystem publishes to the system account's SubList
/// and that internal subscriptions for monitoring are registered there.
/// The subscriptions are wired up during StartAsync via InitEventTracking.
///
[Fact]
public async Task Event_system_subscribes_in_system_account()
{
var (server, _, cts) = await StartServerAsync(new NatsOptions());
try
{
// The system account's SubList should have subscriptions registered
// by the internal event system (VARZ, HEALTHZ, etc.)
server.EventSystem.ShouldNotBeNull();
server.SystemAccount.SubList.Count.ShouldBeGreaterThan(0u);
}
finally
{
await cts.CancelAsync();
server.Dispose();
}
}
///
/// Verifies that the global account is separate from the system account.
///
[Fact]
public void Global_and_system_accounts_are_separate()
{
var options = new NatsOptions { Port = 0 };
using var server = new NatsServer(options, NullLoggerFactory.Instance);
var globalAccount = server.GetOrCreateAccount(Account.GlobalAccountName);
var systemAccount = server.SystemAccount;
globalAccount.ShouldNotBeSameAs(systemAccount);
globalAccount.Name.ShouldBe(Account.GlobalAccountName);
systemAccount.Name.ShouldBe(Account.SystemAccountName);
globalAccount.IsSystemAccount.ShouldBeFalse();
systemAccount.IsSystemAccount.ShouldBeTrue();
globalAccount.SubList.ShouldNotBeSameAs(systemAccount.SubList);
}
}