refactor: extract NATS.Server.Monitoring.Tests project
Move 39 monitoring, events, and system endpoint test files from NATS.Server.Tests into a dedicated NATS.Server.Monitoring.Tests project. Update namespaces, replace private GetFreePort/ReadUntilAsync with TestUtilities shared helpers, add InternalsVisibleTo, and register in the solution file. All 439 tests pass.
This commit is contained in:
286
tests/NATS.Server.Monitoring.Tests/Events/AuthErrorEventTests.cs
Normal file
286
tests/NATS.Server.Monitoring.Tests/Events/AuthErrorEventTests.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
// Port of Go server/events_test.go — auth error advisory publication tests.
|
||||
// Go reference: golang/nats-server/server/events.go:2631 sendAuthErrorEvent.
|
||||
//
|
||||
// Tests cover: SendAuthErrorEvent counter, enqueue behaviour, record field
|
||||
// preservation, SendConnectEvent, SendDisconnectEvent, and the supporting
|
||||
// detail record types AuthErrorDetail, ConnectEventDetail, DisconnectEventDetail.
|
||||
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.Events;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Monitoring.Tests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="InternalEventSystem.SendAuthErrorEvent"/>,
|
||||
/// <see cref="InternalEventSystem.SendConnectEvent"/>,
|
||||
/// <see cref="InternalEventSystem.SendDisconnectEvent"/>, and the three
|
||||
/// companion detail record types.
|
||||
/// Go reference: events_test.go TestSystemAccountDisconnectBadLogin,
|
||||
/// TestSystemAccountNewConnection.
|
||||
/// </summary>
|
||||
public class AuthErrorEventTests : IAsyncLifetime
|
||||
{
|
||||
private NatsServer _server = null!;
|
||||
private int _port;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
_ = _server.StartAsync(CancellationToken.None);
|
||||
await _server.WaitForReadyAsync();
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await _server.ShutdownAsync();
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// AuthErrorEventCount
|
||||
// Go reference: events.go:2631 sendAuthErrorEvent — counter per advisory
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// AuthErrorEventCount starts at zero before any advisories are sent.
|
||||
/// Go reference: events_test.go TestSystemAccountDisconnectBadLogin.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AuthErrorEventCount_StartsAtZero()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountDisconnectBadLogin — no events at startup.
|
||||
var es = _server.EventSystem!;
|
||||
es.AuthErrorEventCount.ShouldBe(0L);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling SendAuthErrorEvent once increments the counter to 1.
|
||||
/// Go reference: events.go:2631 sendAuthErrorEvent — each call is one advisory.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SendAuthErrorEvent_IncrementsCounter()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountDisconnectBadLogin.
|
||||
var es = _server.EventSystem!;
|
||||
var detail = new AuthErrorDetail(
|
||||
ClientId: 42,
|
||||
RemoteAddress: "127.0.0.1:5000",
|
||||
AccountName: "$G",
|
||||
UserName: "alice",
|
||||
Reason: "Authorization Violation",
|
||||
OccurredAt: DateTime.UtcNow);
|
||||
|
||||
es.SendAuthErrorEvent(_server.ServerId, detail);
|
||||
|
||||
es.AuthErrorEventCount.ShouldBe(1L);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Each call to SendAuthErrorEvent enqueues a message (counter grows by one per call).
|
||||
/// Go reference: events.go:2687 sendInternalMsg — advisory is always enqueued.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SendAuthErrorEvent_EnqueuesMessage()
|
||||
{
|
||||
// Go reference: events.go sendAuthErrorEvent publishes via sendInternalMsg.
|
||||
var es = _server.EventSystem!;
|
||||
var detail = new AuthErrorDetail(
|
||||
ClientId: 7,
|
||||
RemoteAddress: "10.0.0.1:4222",
|
||||
AccountName: null,
|
||||
UserName: null,
|
||||
Reason: "Authentication Timeout",
|
||||
OccurredAt: DateTime.UtcNow);
|
||||
|
||||
var before = es.AuthErrorEventCount;
|
||||
es.SendAuthErrorEvent(_server.ServerId, detail);
|
||||
var after = es.AuthErrorEventCount;
|
||||
|
||||
// The counter increment is the observable side-effect of the enqueue path.
|
||||
(after - before).ShouldBe(1L);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sending multiple auth error events increments the counter for each.
|
||||
/// Go reference: events.go:2631 sendAuthErrorEvent — cumulative count.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AuthErrorEventCount_MultipleSends_Incremented()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountDisconnectBadLogin.
|
||||
var es = _server.EventSystem!;
|
||||
var detail = new AuthErrorDetail(
|
||||
ClientId: 1,
|
||||
RemoteAddress: "192.168.1.1:9999",
|
||||
AccountName: "myacc",
|
||||
UserName: "bob",
|
||||
Reason: "Bad credentials",
|
||||
OccurredAt: DateTime.UtcNow);
|
||||
|
||||
var before = es.AuthErrorEventCount;
|
||||
|
||||
const int count = 5;
|
||||
for (var i = 0; i < count; i++)
|
||||
es.SendAuthErrorEvent(_server.ServerId, detail);
|
||||
|
||||
(es.AuthErrorEventCount - before).ShouldBe(count);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// SendConnectEvent
|
||||
// Go reference: events.go postConnectEvent / sendConnect
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// SendConnectEvent enqueues a message without throwing.
|
||||
/// Go reference: events.go postConnectEvent — advisory fired on client connect.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SendConnectEvent_EnqueuesMessage()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountNewConnection.
|
||||
var es = _server.EventSystem!;
|
||||
var detail = new ConnectEventDetail(
|
||||
ClientId: 10,
|
||||
RemoteAddress: "127.0.0.1:6000",
|
||||
AccountName: "$G",
|
||||
UserName: "user1",
|
||||
ConnectedAt: DateTime.UtcNow);
|
||||
|
||||
var ex = Record.Exception(() => es.SendConnectEvent(_server.ServerId, detail));
|
||||
ex.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// SendDisconnectEvent
|
||||
// Go reference: events.go postDisconnectEvent / sendDisconnect
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// SendDisconnectEvent enqueues a message without throwing.
|
||||
/// Go reference: events.go postDisconnectEvent — advisory fired on client disconnect.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SendDisconnectEvent_EnqueuesMessage()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountNewConnection (disconnect part).
|
||||
var es = _server.EventSystem!;
|
||||
var detail = new DisconnectEventDetail(
|
||||
ClientId: 20,
|
||||
RemoteAddress: "127.0.0.1:7000",
|
||||
AccountName: "$G",
|
||||
Reason: "Client Closed",
|
||||
DisconnectedAt: DateTime.UtcNow);
|
||||
|
||||
var ex = Record.Exception(() => es.SendDisconnectEvent(_server.ServerId, detail));
|
||||
ex.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// AuthErrorDetail record
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// AuthErrorDetail preserves all fields passed to its constructor.
|
||||
/// Go reference: events.go:2631 — all client fields captured in the advisory.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AuthErrorDetail_PreservesAllFields()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountDisconnectBadLogin.
|
||||
var now = DateTime.UtcNow;
|
||||
var detail = new AuthErrorDetail(
|
||||
ClientId: 99,
|
||||
RemoteAddress: "10.0.0.2:1234",
|
||||
AccountName: "test-account",
|
||||
UserName: "testuser",
|
||||
Reason: "Authorization Violation",
|
||||
OccurredAt: now);
|
||||
|
||||
detail.ClientId.ShouldBe(99UL);
|
||||
detail.RemoteAddress.ShouldBe("10.0.0.2:1234");
|
||||
detail.AccountName.ShouldBe("test-account");
|
||||
detail.UserName.ShouldBe("testuser");
|
||||
detail.Reason.ShouldBe("Authorization Violation");
|
||||
detail.OccurredAt.ShouldBe(now);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AuthErrorDetail accepts a non-empty Reason (the key advisory field).
|
||||
/// Go reference: events.go:2631 sendAuthErrorEvent — reason is always set.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AuthErrorDetail_ReasonRequired()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountDisconnectBadLogin — reason distinguishes error types.
|
||||
var detail = new AuthErrorDetail(
|
||||
ClientId: 1,
|
||||
RemoteAddress: "127.0.0.1:0",
|
||||
AccountName: null,
|
||||
UserName: null,
|
||||
Reason: "Authentication Timeout",
|
||||
OccurredAt: DateTime.UtcNow);
|
||||
|
||||
detail.Reason.ShouldNotBeNullOrEmpty();
|
||||
detail.Reason.ShouldBe("Authentication Timeout");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// ConnectEventDetail record
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// ConnectEventDetail preserves all constructor fields.
|
||||
/// Go reference: events.go postConnectEvent — all fields captured on connect.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectEventDetail_PreservesFields()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountNewConnection.
|
||||
var connectedAt = new DateTime(2026, 2, 25, 10, 0, 0, DateTimeKind.Utc);
|
||||
var detail = new ConnectEventDetail(
|
||||
ClientId: 55,
|
||||
RemoteAddress: "192.168.0.5:8080",
|
||||
AccountName: "prod-account",
|
||||
UserName: "svc-user",
|
||||
ConnectedAt: connectedAt);
|
||||
|
||||
detail.ClientId.ShouldBe(55UL);
|
||||
detail.RemoteAddress.ShouldBe("192.168.0.5:8080");
|
||||
detail.AccountName.ShouldBe("prod-account");
|
||||
detail.UserName.ShouldBe("svc-user");
|
||||
detail.ConnectedAt.ShouldBe(connectedAt);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// DisconnectEventDetail record
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// DisconnectEventDetail preserves all constructor fields.
|
||||
/// Go reference: events.go postDisconnectEvent — all fields captured on disconnect.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DisconnectEventDetail_PreservesFields()
|
||||
{
|
||||
// Go reference: events_test.go TestSystemAccountNewConnection (disconnect part).
|
||||
var disconnectedAt = new DateTime(2026, 2, 25, 11, 0, 0, DateTimeKind.Utc);
|
||||
var detail = new DisconnectEventDetail(
|
||||
ClientId: 77,
|
||||
RemoteAddress: "172.16.0.3:3000",
|
||||
AccountName: "staging-account",
|
||||
Reason: "Slow Consumer",
|
||||
DisconnectedAt: disconnectedAt);
|
||||
|
||||
detail.ClientId.ShouldBe(77UL);
|
||||
detail.RemoteAddress.ShouldBe("172.16.0.3:3000");
|
||||
detail.AccountName.ShouldBe("staging-account");
|
||||
detail.Reason.ShouldBe("Slow Consumer");
|
||||
detail.DisconnectedAt.ShouldBe(disconnectedAt);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user