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:
@@ -0,0 +1,308 @@
|
||||
using System.Text.Json;
|
||||
using NATS.Server.Events;
|
||||
|
||||
namespace NATS.Server.Monitoring.Tests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Tests that EventBuilder produces fully-populated system event messages
|
||||
/// and that all field contracts are met.
|
||||
/// Go reference: events.go sendConnectEventMsg, sendDisconnectEventMsg,
|
||||
/// sendAccountNumConns, sendStatsz — Gap 10.6.
|
||||
/// </summary>
|
||||
public class FullEventPayloadTests
|
||||
{
|
||||
// ========================================================================
|
||||
// BuildConnectEvent
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void BuildConnectEvent_AllFieldsPopulated()
|
||||
{
|
||||
// Go reference: events.go sendConnectEventMsg — all client fields must be set.
|
||||
var evt = EventBuilder.BuildConnectEvent(
|
||||
serverId: "SRV-001",
|
||||
serverName: "test-server",
|
||||
cluster: "east-cluster",
|
||||
clientId: 42,
|
||||
host: "10.0.0.1",
|
||||
account: "$G",
|
||||
user: "alice",
|
||||
name: "my-client",
|
||||
lang: "csharp",
|
||||
version: "1.0.0");
|
||||
|
||||
evt.ShouldNotBeNull();
|
||||
evt.Id.ShouldNotBeNullOrEmpty();
|
||||
evt.Time.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
evt.Server.Id.ShouldBe("SRV-001");
|
||||
evt.Server.Name.ShouldBe("test-server");
|
||||
evt.Server.Cluster.ShouldBe("east-cluster");
|
||||
evt.Client.Id.ShouldBe(42UL);
|
||||
evt.Client.Host.ShouldBe("10.0.0.1");
|
||||
evt.Client.Account.ShouldBe("$G");
|
||||
evt.Client.User.ShouldBe("alice");
|
||||
evt.Client.Name.ShouldBe("my-client");
|
||||
evt.Client.Lang.ShouldBe("csharp");
|
||||
evt.Client.Version.ShouldBe("1.0.0");
|
||||
evt.Client.Start.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildConnectEvent_HasCorrectEventType()
|
||||
{
|
||||
// Go reference: events.go — connect advisory type constant.
|
||||
var evt = EventBuilder.BuildConnectEvent(
|
||||
serverId: "SRV-001",
|
||||
serverName: "test-server",
|
||||
cluster: null,
|
||||
clientId: 1,
|
||||
host: "127.0.0.1",
|
||||
account: null,
|
||||
user: null,
|
||||
name: null,
|
||||
lang: null,
|
||||
version: null);
|
||||
|
||||
evt.Type.ShouldBe(ConnectEventMsg.EventType);
|
||||
evt.Type.ShouldBe("io.nats.server.advisory.v1.client_connect");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// BuildDisconnectEvent
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void BuildDisconnectEvent_AllFieldsPopulated()
|
||||
{
|
||||
// Go reference: events.go sendDisconnectEventMsg — sent, received, and reason required.
|
||||
var sent = new DataStats { Msgs = 100, Bytes = 2_048 };
|
||||
var received = new DataStats { Msgs = 50, Bytes = 1_024 };
|
||||
|
||||
var evt = EventBuilder.BuildDisconnectEvent(
|
||||
serverId: "SRV-002",
|
||||
serverName: "test-server",
|
||||
cluster: "west-cluster",
|
||||
clientId: 99,
|
||||
host: "192.168.1.5",
|
||||
account: "MYACC",
|
||||
user: "bob",
|
||||
reason: "Client Closed",
|
||||
sent: sent,
|
||||
received: received);
|
||||
|
||||
evt.ShouldNotBeNull();
|
||||
evt.Id.ShouldNotBeNullOrEmpty();
|
||||
evt.Time.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
evt.Server.Id.ShouldBe("SRV-002");
|
||||
evt.Server.Name.ShouldBe("test-server");
|
||||
evt.Server.Cluster.ShouldBe("west-cluster");
|
||||
evt.Client.Id.ShouldBe(99UL);
|
||||
evt.Client.Host.ShouldBe("192.168.1.5");
|
||||
evt.Client.Account.ShouldBe("MYACC");
|
||||
evt.Client.User.ShouldBe("bob");
|
||||
evt.Client.Stop.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
evt.Reason.ShouldBe("Client Closed");
|
||||
evt.Sent.Msgs.ShouldBe(100);
|
||||
evt.Sent.Bytes.ShouldBe(2_048);
|
||||
evt.Received.Msgs.ShouldBe(50);
|
||||
evt.Received.Bytes.ShouldBe(1_024);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildDisconnectEvent_HasCorrectEventType()
|
||||
{
|
||||
// Go reference: events.go — disconnect advisory type constant.
|
||||
var evt = EventBuilder.BuildDisconnectEvent(
|
||||
serverId: "SRV-002",
|
||||
serverName: "test-server",
|
||||
cluster: null,
|
||||
clientId: 1,
|
||||
host: "127.0.0.1",
|
||||
account: null,
|
||||
user: null,
|
||||
reason: "Server Closed",
|
||||
sent: new DataStats(),
|
||||
received: new DataStats());
|
||||
|
||||
evt.Type.ShouldBe(DisconnectEventMsg.EventType);
|
||||
evt.Type.ShouldBe("io.nats.server.advisory.v1.client_disconnect");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// BuildAccountConnsEvent
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void BuildAccountConnsEvent_AllFieldsPopulated()
|
||||
{
|
||||
// Go reference: events.go sendAccountNumConns — all AccountStat fields must transfer.
|
||||
var sent = new DataStats { Msgs = 500, Bytes = 10_000 };
|
||||
var received = new DataStats { Msgs = 400, Bytes = 8_000 };
|
||||
|
||||
var evt = EventBuilder.BuildAccountConnsEvent(
|
||||
serverId: "SRV-003",
|
||||
serverName: "test-server",
|
||||
accountName: "ACCT-A",
|
||||
connections: 7,
|
||||
leafNodes: 2,
|
||||
totalConnections: 150,
|
||||
numSubscriptions: 33,
|
||||
sent: sent,
|
||||
received: received,
|
||||
slowConsumers: 3);
|
||||
|
||||
evt.ShouldNotBeNull();
|
||||
evt.Id.ShouldNotBeNullOrEmpty();
|
||||
evt.Time.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
evt.Server.Id.ShouldBe("SRV-003");
|
||||
evt.Server.Name.ShouldBe("test-server");
|
||||
evt.AccountName.ShouldBe("ACCT-A");
|
||||
evt.Connections.ShouldBe(7);
|
||||
evt.LeafNodes.ShouldBe(2);
|
||||
evt.TotalConnections.ShouldBe(150);
|
||||
evt.NumSubscriptions.ShouldBe(33u);
|
||||
evt.Sent.Msgs.ShouldBe(500);
|
||||
evt.Received.Bytes.ShouldBe(8_000);
|
||||
evt.SlowConsumers.ShouldBe(3);
|
||||
evt.Type.ShouldBe(AccountNumConns.EventType);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// BuildServerStats
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void BuildServerStats_AllFieldsPopulated()
|
||||
{
|
||||
// Go reference: events.go sendStatsz — all stat fields must be set.
|
||||
var sent = new DataStats { Msgs = 1_000, Bytes = 50_000 };
|
||||
var received = new DataStats { Msgs = 800, Bytes = 40_000 };
|
||||
|
||||
var msg = EventBuilder.BuildServerStats(
|
||||
serverId: "SRV-004",
|
||||
serverName: "stats-server",
|
||||
mem: 134_217_728,
|
||||
cores: 8,
|
||||
cpu: 15.5,
|
||||
connections: 12,
|
||||
totalConnections: 600,
|
||||
activeAccounts: 4,
|
||||
subscriptions: 55,
|
||||
sent: sent,
|
||||
received: received);
|
||||
|
||||
msg.ShouldNotBeNull();
|
||||
msg.Server.Id.ShouldBe("SRV-004");
|
||||
msg.Server.Name.ShouldBe("stats-server");
|
||||
msg.Stats.Mem.ShouldBe(134_217_728);
|
||||
msg.Stats.Cores.ShouldBe(8);
|
||||
msg.Stats.Cpu.ShouldBe(15.5);
|
||||
msg.Stats.Connections.ShouldBe(12);
|
||||
msg.Stats.TotalConnections.ShouldBe(600);
|
||||
msg.Stats.ActiveAccounts.ShouldBe(4);
|
||||
msg.Stats.Subscriptions.ShouldBe(55);
|
||||
msg.Stats.Sent.Msgs.ShouldBe(1_000);
|
||||
msg.Stats.Received.Bytes.ShouldBe(40_000);
|
||||
// Flat counters mirror the structured stats
|
||||
msg.Stats.OutMsgs.ShouldBe(1_000);
|
||||
msg.Stats.InMsgs.ShouldBe(800);
|
||||
msg.Stats.OutBytes.ShouldBe(50_000);
|
||||
msg.Stats.InBytes.ShouldBe(40_000);
|
||||
msg.Stats.Start.ShouldBeGreaterThan(DateTime.MinValue);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// GenerateEventId
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void GenerateEventId_UniquePerCall()
|
||||
{
|
||||
// Go reference: events.go uses nuid.Next() — every call produces a distinct ID.
|
||||
var ids = Enumerable.Range(0, 20).Select(_ => EventBuilder.GenerateEventId()).ToList();
|
||||
|
||||
ids.Distinct().Count().ShouldBe(20);
|
||||
foreach (var id in ids)
|
||||
{
|
||||
id.ShouldNotBeNullOrEmpty();
|
||||
id.Length.ShouldBe(32); // Guid.ToString("N") is always 32 hex chars
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// GetTimestamp
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void GetTimestamp_ReturnsIso8601()
|
||||
{
|
||||
// Go reference: events use RFC3339Nano timestamps; .NET "O" format is ISO 8601.
|
||||
var ts = EventBuilder.GetTimestamp();
|
||||
|
||||
ts.ShouldNotBeNullOrEmpty();
|
||||
// "O" format: 2025-02-25T12:34:56.7890000Z — parseable as UTC DateTimeOffset
|
||||
var parsed = DateTimeOffset.Parse(ts);
|
||||
parsed.Year.ShouldBeGreaterThanOrEqualTo(2024);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// DataStats default values
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void DataStats_DefaultZeroValues()
|
||||
{
|
||||
// Go reference: zero-value DataStats is valid and all fields default to 0.
|
||||
var ds = new DataStats();
|
||||
|
||||
ds.Msgs.ShouldBe(0L);
|
||||
ds.Bytes.ShouldBe(0L);
|
||||
ds.Gateways.ShouldBeNull();
|
||||
ds.Routes.ShouldBeNull();
|
||||
ds.Leafs.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// JSON roundtrip
|
||||
// ========================================================================
|
||||
|
||||
[Fact]
|
||||
public void ConnectEventMsg_SerializesToJson()
|
||||
{
|
||||
// Go reference: events.go — connect event serializes to JSON and round-trips.
|
||||
var original = EventBuilder.BuildConnectEvent(
|
||||
serverId: "SRV-RT",
|
||||
serverName: "roundtrip-server",
|
||||
cluster: "rt-cluster",
|
||||
clientId: 77,
|
||||
host: "10.1.2.3",
|
||||
account: "RTACC",
|
||||
user: "rtuser",
|
||||
name: "rt-client",
|
||||
lang: "dotnet",
|
||||
version: "2.0.0");
|
||||
|
||||
var json = JsonSerializer.Serialize(original);
|
||||
|
||||
json.ShouldNotBeNullOrEmpty();
|
||||
json.ShouldContain("\"type\":");
|
||||
json.ShouldContain(ConnectEventMsg.EventType);
|
||||
json.ShouldContain("\"server\":");
|
||||
json.ShouldContain("\"client\":");
|
||||
json.ShouldContain("\"SRV-RT\"");
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<ConnectEventMsg>(json);
|
||||
|
||||
deserialized.ShouldNotBeNull();
|
||||
deserialized!.Type.ShouldBe(ConnectEventMsg.EventType);
|
||||
deserialized.Id.ShouldBe(original.Id);
|
||||
deserialized.Server.Id.ShouldBe("SRV-RT");
|
||||
deserialized.Server.Name.ShouldBe("roundtrip-server");
|
||||
deserialized.Server.Cluster.ShouldBe("rt-cluster");
|
||||
deserialized.Client.Id.ShouldBe(77UL);
|
||||
deserialized.Client.Account.ShouldBe("RTACC");
|
||||
deserialized.Client.User.ShouldBe("rtuser");
|
||||
deserialized.Client.Lang.ShouldBe("dotnet");
|
||||
deserialized.Client.Version.ShouldBe("2.0.0");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user