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.
309 lines
11 KiB
C#
309 lines
11 KiB
C#
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");
|
|
}
|
|
}
|