refactor: extract NATS.Server.Auth.Tests project

Move 50 auth/accounts/permissions/JWT/NKey test files from
NATS.Server.Tests into a dedicated NATS.Server.Auth.Tests project.
Update namespaces, replace private GetFreePort/ReadUntilAsync helpers
with TestUtilities calls, replace Task.Delay with TaskCompletionSource
in test doubles, and add InternalsVisibleTo.

690 tests pass.
This commit is contained in:
Joseph Doherty
2026-03-12 15:54:07 -04:00
parent 0c086522a4
commit 36b9dfa654
53 changed files with 138 additions and 185 deletions

View File

@@ -13,6 +13,7 @@
<Project Path="tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj" /> <Project Path="tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj" />
<Project Path="tests/NATS.Server.Raft.Tests/NATS.Server.Raft.Tests.csproj" /> <Project Path="tests/NATS.Server.Raft.Tests/NATS.Server.Raft.Tests.csproj" />
<Project Path="tests/NATS.Server.Monitoring.Tests/NATS.Server.Monitoring.Tests.csproj" /> <Project Path="tests/NATS.Server.Monitoring.Tests/NATS.Server.Monitoring.Tests.csproj" />
<Project Path="tests/NATS.Server.Auth.Tests/NATS.Server.Auth.Tests.csproj" />
<Project Path="tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj" /> <Project Path="tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj" />
</Folder> </Folder>
</Solution> </Solution>

View File

@@ -8,6 +8,7 @@
<InternalsVisibleTo Include="NATS.Server.Clustering.Tests" /> <InternalsVisibleTo Include="NATS.Server.Clustering.Tests" />
<InternalsVisibleTo Include="NATS.Server.Raft.Tests" /> <InternalsVisibleTo Include="NATS.Server.Raft.Tests" />
<InternalsVisibleTo Include="NATS.Server.Monitoring.Tests" /> <InternalsVisibleTo Include="NATS.Server.Monitoring.Tests" />
<InternalsVisibleTo Include="NATS.Server.Auth.Tests" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />

View File

@@ -4,7 +4,9 @@ using Microsoft.Extensions.Logging.Abstractions;
using NATS.Client.Core; using NATS.Client.Core;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests;
public class AccountIsolationTests : IAsyncLifetime public class AccountIsolationTests : IAsyncLifetime
{ {
@@ -12,17 +14,9 @@ public class AccountIsolationTests : IAsyncLifetime
private int _port; private int _port;
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
private Task _serverTask = null!; private Task _serverTask = null!;
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;
}
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
_port = GetFreePort(); _port = TestPortAllocator.GetFreePort();
_server = new NatsServer(new NatsOptions _server = new NatsServer(new NatsOptions
{ {
Port = _port, Port = _port,
@@ -100,7 +94,8 @@ public class AccountIsolationTests : IAsyncLifetime
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
// Expected — no message received (timeout) // Expected — no message received means accounts are properly isolated
return;
} }
} }
} }

View File

@@ -1,6 +1,6 @@
using NATS.Server.Auth.Jwt; using NATS.Server.Auth.Jwt;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AccountResolverTests public class AccountResolverTests
{ {

View File

@@ -1,6 +1,6 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AccountStatsTests public class AccountStatsTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AccountTests public class AccountTests
{ {

View File

@@ -4,7 +4,9 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Accounts; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Accounts;
/// <summary> /// <summary>
/// Tests for cross-account stream/service export/import delivery, authorization, and mapping. /// Tests for cross-account stream/service export/import delivery, authorization, and mapping.
@@ -380,20 +382,9 @@ public class AccountImportExportTests
private static NatsServer CreateTestServer() private static NatsServer CreateTestServer()
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance); return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
} }
private static int GetFreePort()
{
using var sock = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
sock.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 0));
return ((System.Net.IPEndPoint)sock.LocalEndPoint!).Port;
}
/// <summary> /// <summary>
/// Minimal test double for INatsClient used in import/export tests. /// Minimal test double for INatsClient used in import/export tests.
/// </summary> /// </summary>

View File

@@ -7,7 +7,9 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Accounts; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Accounts;
/// <summary> /// <summary>
/// Tests for account creation, registration, isolation, and basic account lifecycle. /// Tests for account creation, registration, isolation, and basic account lifecycle.
@@ -17,16 +19,9 @@ namespace NATS.Server.Tests.Accounts;
/// </summary> /// </summary>
public class AccountIsolationTests public class AccountIsolationTests
{ {
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 NatsServer CreateTestServer(NatsOptions? options = null) private static NatsServer CreateTestServer(NatsOptions? options = null)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options ??= new NatsOptions(); options ??= new NatsOptions();
options.Port = port; options.Port = port;
return new NatsServer(options, NullLoggerFactory.Instance); return new NatsServer(options, NullLoggerFactory.Instance);
@@ -34,7 +29,7 @@ public class AccountIsolationTests
private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options) private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -208,7 +203,8 @@ public class AccountIsolationTests
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
// Expected // Expected — timeout confirms cross-account isolation prevented delivery
return;
} }
} }
finally finally
@@ -301,7 +297,8 @@ public class AccountIsolationTests
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
// Expected // Expected — timeout confirms different-account isolation blocks delivery
return;
} }
} }
finally finally
@@ -434,7 +431,8 @@ public class AccountIsolationTests
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
// Expected — accounts are isolated // Expected — timeout confirms subject-mapped accounts remain isolated
return;
} }
} }
finally finally

View File

@@ -8,7 +8,9 @@ using NATS.Server.Imports;
using NATS.Server.Protocol; using NATS.Server.Protocol;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Accounts; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Accounts;
/// <summary> /// <summary>
/// Tests for auth callout behavior, account limits (max connections / max subscriptions), /// Tests for auth callout behavior, account limits (max connections / max subscriptions),
@@ -19,16 +21,9 @@ namespace NATS.Server.Tests.Accounts;
/// </summary> /// </summary>
public class AuthCalloutTests public class AuthCalloutTests
{ {
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 NatsServer CreateTestServer(NatsOptions? options = null) private static NatsServer CreateTestServer(NatsOptions? options = null)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options ??= new NatsOptions(); options ??= new NatsOptions();
options.Port = port; options.Port = port;
return new NatsServer(options, NullLoggerFactory.Instance); return new NatsServer(options, NullLoggerFactory.Instance);
@@ -36,7 +31,7 @@ public class AuthCalloutTests
private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options) private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -772,7 +767,10 @@ public class AuthCalloutTests
{ {
public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct) public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct)
{ {
await Task.Delay(delay, ct); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
await using var reg = ct.Register(() => tcs.TrySetCanceled(ct));
using var timer = new Timer(_ => tcs.TrySetResult(true), null, delay, Timeout.InfiniteTimeSpan);
await tcs.Task;
return new ExternalAuthDecision(true, "delayed"); return new ExternalAuthDecision(true, "delayed");
} }
} }

View File

@@ -6,7 +6,9 @@ using NATS.Server;
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests.Accounts; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Accounts;
/// <summary> /// <summary>
/// Tests for authentication mechanisms: username/password, token, NKey-based auth, /// Tests for authentication mechanisms: username/password, token, NKey-based auth,
@@ -16,16 +18,9 @@ namespace NATS.Server.Tests.Accounts;
/// </summary> /// </summary>
public class AuthMechanismTests public class AuthMechanismTests
{ {
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) private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();

View File

@@ -5,7 +5,9 @@ using NATS.Client.Core;
using NATS.Server; using NATS.Server;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Accounts; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Accounts;
/// <summary> /// <summary>
/// Tests for publish/subscribe permission enforcement, account-level limits, /// Tests for publish/subscribe permission enforcement, account-level limits,
@@ -15,16 +17,9 @@ namespace NATS.Server.Tests.Accounts;
/// </summary> /// </summary>
public class PermissionTests public class PermissionTests
{ {
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) private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -356,7 +351,8 @@ public class PermissionTests
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
// Expected — message was blocked by permissions // Expected — timeout confirms permission denial blocked the message
return;
} }
} }
finally finally

View File

@@ -3,7 +3,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AccountClaimReloadTests public class AccountClaimReloadTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
// Go reference: server/accounts.go — account expiry / SetExpirationTimer // Go reference: server/accounts.go — account expiry / SetExpirationTimer

View File

@@ -5,7 +5,7 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using ServerSubscriptions = NATS.Server.Subscriptions; using ServerSubscriptions = NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
/// <summary> /// <summary>
/// Parity tests ported from Go server/accounts_test.go exercising account /// Parity tests ported from Go server/accounts_test.go exercising account

View File

@@ -4,7 +4,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AccountImportExportTests public class AccountImportExportTests
{ {

View File

@@ -3,7 +3,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AccountLimitsTests public class AccountLimitsTests
{ {

View File

@@ -2,7 +2,7 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AccountResponseAndInterestParityBatch1Tests public class AccountResponseAndInterestParityBatch1Tests
{ {

View File

@@ -6,7 +6,7 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
/// <summary> /// <summary>
/// Parity tests ported from Go server/accounts_test.go covering: /// Parity tests ported from Go server/accounts_test.go covering:

View File

@@ -6,7 +6,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class ActivationExpirationTests public class ActivationExpirationTests
{ {

View File

@@ -7,7 +7,7 @@ using System.Security.Cryptography.X509Certificates;
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
/// <summary> /// <summary>
/// Parity tests ported from Go server/auth_callout_test.go covering the auth callout /// Parity tests ported from Go server/auth_callout_test.go covering the auth callout
@@ -1399,7 +1399,10 @@ internal sealed class SlowCalloutClient : IExternalAuthClient
public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct) public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct)
{ {
await Task.Delay(_delay, ct); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
await using var reg = ct.Register(() => tcs.TrySetCanceled(ct));
using var timer = new Timer(_ => tcs.TrySetResult(true), null, _delay, Timeout.InfiniteTimeSpan);
await tcs.Task;
return new ExternalAuthDecision(true, _identity ?? request.Username ?? "slow_user", _account); return new ExternalAuthDecision(true, _identity ?? request.Username ?? "slow_user", _account);
} }
} }

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AuthExtensionParityTests public class AuthExtensionParityTests
{ {

View File

@@ -1,6 +1,6 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AuthModelAndCalloutConstantsParityTests public class AuthModelAndCalloutConstantsParityTests
{ {

View File

@@ -2,7 +2,7 @@ using NATS.NKeys;
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class AuthServiceParityBatch4Tests public class AuthServiceParityBatch4Tests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class ExternalAuthCalloutTests public class ExternalAuthCalloutTests
{ {
@@ -51,7 +51,10 @@ public class ExternalAuthCalloutTests
{ {
public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct) public async Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct)
{ {
await Task.Delay(delay, ct); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
await using var reg = ct.Register(() => tcs.TrySetCanceled(ct));
using var timer = new Timer(_ => tcs.TrySetResult(true), null, delay, Timeout.InfiniteTimeSpan);
await tcs.Task;
return new ExternalAuthDecision(true, "slow"); return new ExternalAuthDecision(true, "slow");
} }
} }

View File

@@ -5,7 +5,7 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class ImportShadowingTests public class ImportShadowingTests
{ {

View File

@@ -11,7 +11,7 @@ using NATS.Server.Auth;
using NATS.Server.Auth.Jwt; using NATS.Server.Auth.Jwt;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests.Auth namespace NATS.Server.Auth.Tests.Auth
{ {
/// <summary> /// <summary>
@@ -1767,4 +1767,4 @@ internal sealed class JwtTestSubjectPerm
[JsonPropertyName("deny")] public string[]? Deny { get; set; } [JsonPropertyName("deny")] public string[]? Deny { get; set; }
} }
} // namespace NATS.Server.Tests.Auth } // namespace NATS.Server.Auth.Tests.Auth

View File

@@ -4,7 +4,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class NKeyRevocationTests public class NKeyRevocationTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class ProxyAuthTests public class ProxyAuthTests
{ {

View File

@@ -5,7 +5,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class ResponseThresholdTests public class ResponseThresholdTests
{ {

View File

@@ -3,7 +3,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class ReverseResponseMapTests public class ReverseResponseMapTests
{ {

View File

@@ -3,7 +3,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class ServiceLatencyTrackerTests public class ServiceLatencyTrackerTests
{ {

View File

@@ -3,7 +3,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class StreamImportCycleTests public class StreamImportCycleTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
/// <summary> /// <summary>
/// Tests for SUB permission caching and generation-based invalidation. /// Tests for SUB permission caching and generation-based invalidation.

View File

@@ -8,7 +8,9 @@ using System.Text;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests.Auth;
/// <summary> /// <summary>
/// Tests for the $SYS system account functionality including: /// Tests for the $SYS system account functionality including:
@@ -22,16 +24,9 @@ public class SystemAccountTests
{ {
// ─── Helpers ──────────────────────────────────────────────────────────── // ─── 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) private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -40,31 +35,6 @@ public class SystemAccountTests
return (server, port, cts); return (server, port, cts);
} }
private static async Task<Socket> 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<string> 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 ────────────────────────────────────────────────────────────── // ─── Tests ──────────────────────────────────────────────────────────────
/// <summary> /// <summary>

View File

@@ -2,7 +2,7 @@ using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class TlsMapAuthParityBatch1Tests public class TlsMapAuthParityBatch1Tests
{ {

View File

@@ -4,7 +4,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
namespace NATS.Server.Tests.Auth; namespace NATS.Server.Auth.Tests.Auth;
public class WildcardExportTests public class WildcardExportTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server; using NATS.Server;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AuthConfigTests public class AuthConfigTests
{ {

View File

@@ -5,17 +5,12 @@ using NATS.Client.Core;
using NATS.Server; using NATS.Server;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests;
public class AuthIntegrationTests public class AuthIntegrationTests
{ {
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;
}
/// <summary> /// <summary>
/// Checks whether any exception in the chain contains the given substring. /// Checks whether any exception in the chain contains the given substring.
/// The NATS client wraps server errors in outer NatsException messages, /// The NATS client wraps server errors in outer NatsException messages,
@@ -36,7 +31,7 @@ public class AuthIntegrationTests
private static (NatsServer server, int port, CancellationTokenSource cts) StartServer(NatsOptions options) private static (NatsServer server, int port, CancellationTokenSource cts) StartServer(NatsOptions options)
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
options.Port = port; options.Port = port;
var server = new NatsServer(options, NullLoggerFactory.Instance); var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();

View File

@@ -1,7 +1,7 @@
using System.Text.Json; using System.Text.Json;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AuthProtocolTests public class AuthProtocolTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class AuthServiceTests public class AuthServiceTests
{ {

View File

@@ -1,6 +1,6 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class ClientPermissionsTests public class ClientPermissionsTests
{ {

View File

@@ -4,7 +4,9 @@ using NATS.Server.Auth;
using NATS.Server.Imports; using NATS.Server.Imports;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests;
public class ImportExportTests public class ImportExportTests
{ {
@@ -298,20 +300,9 @@ public class ImportExportTests
private static NatsServer CreateTestServer() private static NatsServer CreateTestServer()
{ {
var port = GetFreePort(); var port = TestPortAllocator.GetFreePort();
return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance); return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
} }
private static int GetFreePort()
{
using var sock = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
sock.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 0));
return ((System.Net.IPEndPoint)sock.LocalEndPoint!).Port;
}
/// <summary> /// <summary>
/// Minimal test double for INatsClient used in import/export tests. /// Minimal test double for INatsClient used in import/export tests.
/// </summary> /// </summary>

View File

@@ -4,7 +4,7 @@ using NATS.Server.Auth;
using NATS.Server.Auth.Jwt; using NATS.Server.Auth.Jwt;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class JwtAuthenticatorTests public class JwtAuthenticatorTests
{ {

View File

@@ -3,7 +3,7 @@ using System.Text.Json;
using NATS.NKeys; using NATS.NKeys;
using NATS.Server.Auth.Jwt; using NATS.Server.Auth.Jwt;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class JwtTests public class JwtTests
{ {

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NATS.Client.Core" />
<PackageReference Include="NATS.NKeys" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
<Using Include="Shouldly" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NATS.Server\NATS.Server.csproj" />
<ProjectReference Include="..\NATS.Server.TestUtilities\NATS.Server.TestUtilities.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@ using NATS.NKeys;
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class NKeyAuthenticatorTests public class NKeyAuthenticatorTests
{ {

View File

@@ -5,7 +5,9 @@ using NATS.Client.Core;
using NATS.NKeys; using NATS.NKeys;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests;
public class NKeyIntegrationTests : IAsyncLifetime public class NKeyIntegrationTests : IAsyncLifetime
{ {
@@ -16,17 +18,9 @@ public class NKeyIntegrationTests : IAsyncLifetime
private KeyPair _userKeyPair = null!; private KeyPair _userKeyPair = null!;
private string _userSeed = null!; private string _userSeed = null!;
private string _userPublicKey = null!; private string _userPublicKey = null!;
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;
}
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
_port = GetFreePort(); _port = TestPortAllocator.GetFreePort();
_userKeyPair = KeyPair.CreatePair(PrefixByte.User); _userKeyPair = KeyPair.CreatePair(PrefixByte.User);
_userPublicKey = _userKeyPair.GetPublicKey(); _userPublicKey = _userKeyPair.GetPublicKey();
_userSeed = _userKeyPair.GetSeed(); _userSeed = _userKeyPair.GetSeed();

View File

@@ -4,7 +4,9 @@ using Microsoft.Extensions.Logging.Abstractions;
using NATS.Client.Core; using NATS.Client.Core;
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; using NATS.Server.TestUtilities;
namespace NATS.Server.Auth.Tests;
public class PermissionIntegrationTests : IAsyncLifetime public class PermissionIntegrationTests : IAsyncLifetime
{ {
@@ -12,17 +14,9 @@ public class PermissionIntegrationTests : IAsyncLifetime
private int _port; private int _port;
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
private Task _serverTask = null!; private Task _serverTask = null!;
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;
}
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
_port = GetFreePort(); _port = TestPortAllocator.GetFreePort();
_server = new NatsServer(new NatsOptions _server = new NatsServer(new NatsOptions
{ {
Port = _port, Port = _port,

View File

@@ -1,6 +1,6 @@
using NATS.Server.Auth; using NATS.Server.Auth;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class PermissionLruCacheTests public class PermissionLruCacheTests
{ {

View File

@@ -1,4 +1,4 @@
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
using NATS.Server.Auth.Jwt; using NATS.Server.Auth.Jwt;

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class SimpleUserPasswordAuthenticatorTests public class SimpleUserPasswordAuthenticatorTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class TokenAuthenticatorTests public class TokenAuthenticatorTests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Protocol; using NATS.Server.Protocol;
namespace NATS.Server.Tests; namespace NATS.Server.Auth.Tests;
public class UserPasswordAuthenticatorTests public class UserPasswordAuthenticatorTests
{ {