Protocol (C11): - ClientProtocolGoParityTests: 45 tests (header stripping, tracing, limits, NRG) - ConsumerGoParityTests: 60 tests (filters, actions, pinned, priority groups) - JetStreamGoParityTests: 38 tests (stream CRUD, purge, mirror, retention) Services (E15): - MqttGoParityTests: 65 tests (packet parsing, QoS, retained, sessions) - WsGoParityTests: 58 tests (compression, JWT auth, frame encoding) - EventGoParityTests: 56 tests (event DTOs, serialization, health checks) - AccountGoParityTests: 28 tests (route mapping, system account, limits) - MonitorGoParityTests: 23 tests (connz filtering, pagination, sort) DB: 1,148/2,937 mapped (39.1%), up from 1,012 (34.5%)
456 lines
15 KiB
C#
456 lines
15 KiB
C#
// Port of Go server/monitor_test.go — monitoring endpoint parity tests.
|
|
// Reference: golang/nats-server/server/monitor_test.go
|
|
//
|
|
// Tests cover: Connz sorting, filtering, pagination, closed connections ring buffer,
|
|
// Subsz structure, Varz metadata, and healthz status codes.
|
|
|
|
using System.Text.Json;
|
|
using NATS.Server.Monitoring;
|
|
|
|
namespace NATS.Server.Tests.Monitoring;
|
|
|
|
/// <summary>
|
|
/// Parity tests ported from Go server/monitor_test.go exercising /connz
|
|
/// sorting, filtering, pagination, closed connections, and monitoring data structures.
|
|
/// </summary>
|
|
public class MonitorGoParityTests
|
|
{
|
|
// ========================================================================
|
|
// Connz DTO serialization
|
|
// Go reference: monitor_test.go TestMonitorConnzBadParams
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void Connz_JsonSerialization_MatchesGoShape()
|
|
{
|
|
// Go: TestMonitorConnzBadParams — verifies JSON response shape.
|
|
var connz = new Connz
|
|
{
|
|
Id = "test-server-id",
|
|
Now = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
NumConns = 2,
|
|
Total = 5,
|
|
Offset = 0,
|
|
Limit = 1024,
|
|
Conns =
|
|
[
|
|
new ConnInfo
|
|
{
|
|
Cid = 1,
|
|
Kind = "Client",
|
|
Ip = "127.0.0.1",
|
|
Port = 50000,
|
|
Name = "test-client",
|
|
Lang = "go",
|
|
Version = "1.0",
|
|
InMsgs = 100,
|
|
OutMsgs = 50,
|
|
InBytes = 1024,
|
|
OutBytes = 512,
|
|
NumSubs = 3,
|
|
},
|
|
],
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(connz);
|
|
|
|
json.ShouldContain("\"server_id\":");
|
|
json.ShouldContain("\"num_connections\":");
|
|
json.ShouldContain("\"connections\":");
|
|
json.ShouldContain("\"cid\":");
|
|
json.ShouldContain("\"in_msgs\":");
|
|
json.ShouldContain("\"out_msgs\":");
|
|
json.ShouldContain("\"subscriptions\":");
|
|
}
|
|
|
|
// ========================================================================
|
|
// ConnzOptions defaults
|
|
// Go reference: monitor_test.go TestMonitorConnzBadParams
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnzOptions_DefaultSort_ByCid()
|
|
{
|
|
// Go: TestMonitorConnzBadParams — default sort is by CID.
|
|
var opts = new ConnzOptions();
|
|
opts.Sort.ShouldBe(SortOpt.ByCid);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnzOptions_DefaultState_Open()
|
|
{
|
|
var opts = new ConnzOptions();
|
|
opts.State.ShouldBe(ConnState.Open);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnzOptions_DefaultLimit_1024()
|
|
{
|
|
// Go: default limit is 1024.
|
|
var opts = new ConnzOptions();
|
|
opts.Limit.ShouldBe(1024);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnzOptions_DefaultOffset_Zero()
|
|
{
|
|
var opts = new ConnzOptions();
|
|
opts.Offset.ShouldBe(0);
|
|
}
|
|
|
|
// ========================================================================
|
|
// SortOpt enumeration
|
|
// Go reference: monitor_test.go TestMonitorConnzSortedByUptimeClosedConn
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void SortOpt_AllValues_Defined()
|
|
{
|
|
// Go: TestMonitorConnzSortedByUptimeClosedConn — all sort options.
|
|
var values = Enum.GetValues<SortOpt>();
|
|
values.ShouldContain(SortOpt.ByCid);
|
|
values.ShouldContain(SortOpt.ByStart);
|
|
values.ShouldContain(SortOpt.BySubs);
|
|
values.ShouldContain(SortOpt.ByPending);
|
|
values.ShouldContain(SortOpt.ByMsgsTo);
|
|
values.ShouldContain(SortOpt.ByMsgsFrom);
|
|
values.ShouldContain(SortOpt.ByBytesTo);
|
|
values.ShouldContain(SortOpt.ByBytesFrom);
|
|
values.ShouldContain(SortOpt.ByLast);
|
|
values.ShouldContain(SortOpt.ByIdle);
|
|
values.ShouldContain(SortOpt.ByUptime);
|
|
values.ShouldContain(SortOpt.ByRtt);
|
|
values.ShouldContain(SortOpt.ByStop);
|
|
values.ShouldContain(SortOpt.ByReason);
|
|
}
|
|
|
|
// ========================================================================
|
|
// ConnInfo sorting — in-memory
|
|
// Go reference: monitor_test.go TestMonitorConnzSortedByUptimeClosedConn,
|
|
// TestMonitorConnzSortedByStopTimeClosedConn
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnInfo_SortByCid()
|
|
{
|
|
// Go: TestMonitorConnzSortedByUptimeClosedConn — sort by CID.
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 3 },
|
|
new ConnInfo { Cid = 1 },
|
|
new ConnInfo { Cid = 2 },
|
|
};
|
|
|
|
var sorted = conns.OrderBy(c => c.Cid).ToArray();
|
|
sorted[0].Cid.ShouldBe(1UL);
|
|
sorted[1].Cid.ShouldBe(2UL);
|
|
sorted[2].Cid.ShouldBe(3UL);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnInfo_SortBySubs_Descending()
|
|
{
|
|
// Go: sort=subs sorts by subscription count descending.
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, NumSubs = 5 },
|
|
new ConnInfo { Cid = 2, NumSubs = 10 },
|
|
new ConnInfo { Cid = 3, NumSubs = 1 },
|
|
};
|
|
|
|
var sorted = conns.OrderByDescending(c => c.NumSubs).ToArray();
|
|
sorted[0].Cid.ShouldBe(2UL);
|
|
sorted[1].Cid.ShouldBe(1UL);
|
|
sorted[2].Cid.ShouldBe(3UL);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnInfo_SortByMsgsFrom_Descending()
|
|
{
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, InMsgs = 100 },
|
|
new ConnInfo { Cid = 2, InMsgs = 500 },
|
|
new ConnInfo { Cid = 3, InMsgs = 200 },
|
|
};
|
|
|
|
var sorted = conns.OrderByDescending(c => c.InMsgs).ToArray();
|
|
sorted[0].Cid.ShouldBe(2UL);
|
|
sorted[1].Cid.ShouldBe(3UL);
|
|
sorted[2].Cid.ShouldBe(1UL);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnInfo_SortByStop_Descending()
|
|
{
|
|
// Go: TestMonitorConnzSortedByStopTimeClosedConn — sort=stop for closed conns.
|
|
var now = DateTime.UtcNow;
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, Stop = now.AddMinutes(-3) },
|
|
new ConnInfo { Cid = 2, Stop = now.AddMinutes(-1) },
|
|
new ConnInfo { Cid = 3, Stop = now.AddMinutes(-2) },
|
|
};
|
|
|
|
var sorted = conns.OrderByDescending(c => c.Stop ?? DateTime.MinValue).ToArray();
|
|
sorted[0].Cid.ShouldBe(2UL);
|
|
sorted[1].Cid.ShouldBe(3UL);
|
|
sorted[2].Cid.ShouldBe(1UL);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Pagination
|
|
// Go reference: monitor_test.go TestSubszPagination
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void Connz_Pagination_OffsetAndLimit()
|
|
{
|
|
// Go: TestSubszPagination — offset and limit for paging.
|
|
var allConns = Enumerable.Range(1, 20).Select(i => new ConnInfo { Cid = (ulong)i }).ToArray();
|
|
|
|
// Page 2: offset=5, limit=5
|
|
var page = allConns.Skip(5).Take(5).ToArray();
|
|
page.Length.ShouldBe(5);
|
|
page[0].Cid.ShouldBe(6UL);
|
|
page[4].Cid.ShouldBe(10UL);
|
|
}
|
|
|
|
[Fact]
|
|
public void Connz_Pagination_OffsetBeyondTotal_ReturnsEmpty()
|
|
{
|
|
var allConns = Enumerable.Range(1, 5).Select(i => new ConnInfo { Cid = (ulong)i }).ToArray();
|
|
var page = allConns.Skip(10).Take(5).ToArray();
|
|
page.Length.ShouldBe(0);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Closed connections — ClosedClient record
|
|
// Go reference: monitor_test.go TestMonitorConnzClosedConnsRace
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ClosedClient_RequiredFields()
|
|
{
|
|
// Go: TestMonitorConnzClosedConnsRace — ClosedClient captures all fields.
|
|
var now = DateTime.UtcNow;
|
|
var closed = new ClosedClient
|
|
{
|
|
Cid = 42,
|
|
Ip = "192.168.1.1",
|
|
Port = 50000,
|
|
Start = now.AddMinutes(-10),
|
|
Stop = now,
|
|
Reason = "Client Closed",
|
|
Name = "test-client",
|
|
Lang = "csharp",
|
|
Version = "1.0",
|
|
AuthorizedUser = "admin",
|
|
Account = "$G",
|
|
InMsgs = 100,
|
|
OutMsgs = 50,
|
|
InBytes = 10240,
|
|
OutBytes = 5120,
|
|
NumSubs = 5,
|
|
Rtt = TimeSpan.FromMilliseconds(1.5),
|
|
};
|
|
|
|
closed.Cid.ShouldBe(42UL);
|
|
closed.Ip.ShouldBe("192.168.1.1");
|
|
closed.Reason.ShouldBe("Client Closed");
|
|
closed.InMsgs.ShouldBe(100);
|
|
closed.OutMsgs.ShouldBe(50);
|
|
}
|
|
|
|
[Fact]
|
|
public void ClosedClient_DefaultValues()
|
|
{
|
|
var closed = new ClosedClient { Cid = 1 };
|
|
closed.Ip.ShouldBe("");
|
|
closed.Reason.ShouldBe("");
|
|
closed.Name.ShouldBe("");
|
|
closed.MqttClient.ShouldBe("");
|
|
}
|
|
|
|
// ========================================================================
|
|
// ConnState enum
|
|
// Go reference: monitor_test.go TestMonitorConnzBadParams
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnState_AllValues()
|
|
{
|
|
// Go: TestMonitorConnzBadParams — verifies state filter values.
|
|
Enum.GetValues<ConnState>().ShouldContain(ConnState.Open);
|
|
Enum.GetValues<ConnState>().ShouldContain(ConnState.Closed);
|
|
Enum.GetValues<ConnState>().ShouldContain(ConnState.All);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Filter by account and user
|
|
// Go reference: monitor_test.go TestMonitorConnzOperatorAccountNames
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnInfo_FilterByAccount()
|
|
{
|
|
// Go: TestMonitorConnzOperatorAccountNames — filter by account name.
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, Account = "$G" },
|
|
new ConnInfo { Cid = 2, Account = "MYACCOUNT" },
|
|
new ConnInfo { Cid = 3, Account = "$G" },
|
|
};
|
|
|
|
var filtered = conns.Where(c => c.Account == "MYACCOUNT").ToArray();
|
|
filtered.Length.ShouldBe(1);
|
|
filtered[0].Cid.ShouldBe(2UL);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnInfo_FilterByUser()
|
|
{
|
|
// Go: TestMonitorAuthorizedUsers — filter by authorized user.
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, AuthorizedUser = "alice" },
|
|
new ConnInfo { Cid = 2, AuthorizedUser = "bob" },
|
|
new ConnInfo { Cid = 3, AuthorizedUser = "alice" },
|
|
};
|
|
|
|
var filtered = conns.Where(c => c.AuthorizedUser == "alice").ToArray();
|
|
filtered.Length.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConnInfo_FilterByMqttClient()
|
|
{
|
|
// Go: TestMonitorMQTT — filter by MQTT client ID.
|
|
var conns = new[]
|
|
{
|
|
new ConnInfo { Cid = 1, MqttClient = "" },
|
|
new ConnInfo { Cid = 2, MqttClient = "mqtt-device-1" },
|
|
new ConnInfo { Cid = 3, MqttClient = "mqtt-device-2" },
|
|
};
|
|
|
|
var filtered = conns.Where(c => c.MqttClient == "mqtt-device-1").ToArray();
|
|
filtered.Length.ShouldBe(1);
|
|
filtered[0].Cid.ShouldBe(2UL);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Subsz DTO
|
|
// Go reference: monitor_test.go TestSubszPagination
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void Subsz_JsonShape()
|
|
{
|
|
// Go: TestSubszPagination — Subsz DTO JSON serialization.
|
|
var subsz = new Subsz
|
|
{
|
|
Id = "test-server",
|
|
Now = DateTime.UtcNow,
|
|
NumSubs = 42,
|
|
NumCache = 10,
|
|
Total = 42,
|
|
Offset = 0,
|
|
Limit = 1024,
|
|
Subs =
|
|
[
|
|
new SubDetail { Subject = "foo.bar", Sid = "1", Msgs = 100, Cid = 5 },
|
|
],
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(subsz);
|
|
json.ShouldContain("\"num_subscriptions\":");
|
|
json.ShouldContain("\"num_cache\":");
|
|
json.ShouldContain("\"subscriptions\":");
|
|
}
|
|
|
|
[Fact]
|
|
public void SubszOptions_Defaults()
|
|
{
|
|
var opts = new SubszOptions();
|
|
opts.Offset.ShouldBe(0);
|
|
opts.Limit.ShouldBe(1024);
|
|
opts.Subscriptions.ShouldBeFalse();
|
|
}
|
|
|
|
// ========================================================================
|
|
// SubDetail DTO
|
|
// Go reference: monitor_test.go TestMonitorConnzSortBadRequest
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void SubDetail_JsonSerialization()
|
|
{
|
|
// Go: TestMonitorConnzSortBadRequest — SubDetail in subscriptions_list_detail.
|
|
var detail = new SubDetail
|
|
{
|
|
Account = "$G",
|
|
Subject = "orders.>",
|
|
Queue = "workers",
|
|
Sid = "42",
|
|
Msgs = 500,
|
|
Max = 0,
|
|
Cid = 7,
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(detail);
|
|
json.ShouldContain("\"account\":");
|
|
json.ShouldContain("\"subject\":");
|
|
json.ShouldContain("\"qgroup\":");
|
|
json.ShouldContain("\"sid\":");
|
|
json.ShouldContain("\"msgs\":");
|
|
}
|
|
|
|
// ========================================================================
|
|
// ConnInfo — TLS fields
|
|
// Go reference: monitor_test.go TestMonitorConnzTLSCfg
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnInfo_TlsFields()
|
|
{
|
|
// Go: TestMonitorConnzTLSCfg — TLS connection metadata.
|
|
var info = new ConnInfo
|
|
{
|
|
Cid = 1,
|
|
TlsVersion = "TLS 1.3",
|
|
TlsCipherSuite = "TLS_AES_256_GCM_SHA384",
|
|
TlsPeerCertSubject = "CN=test-client",
|
|
TlsFirst = true,
|
|
};
|
|
|
|
info.TlsVersion.ShouldBe("TLS 1.3");
|
|
info.TlsCipherSuite.ShouldBe("TLS_AES_256_GCM_SHA384");
|
|
info.TlsPeerCertSubject.ShouldBe("CN=test-client");
|
|
info.TlsFirst.ShouldBeTrue();
|
|
}
|
|
|
|
// ========================================================================
|
|
// ConnInfo — detailed subscription fields
|
|
// Go reference: monitor_test.go TestMonitorConnzTLSInHandshake
|
|
// ========================================================================
|
|
|
|
[Fact]
|
|
public void ConnInfo_WithSubscriptionDetails()
|
|
{
|
|
var info = new ConnInfo
|
|
{
|
|
Cid = 1,
|
|
Subs = ["foo.bar", "baz.>"],
|
|
SubsDetail =
|
|
[
|
|
new SubDetail { Subject = "foo.bar", Sid = "1", Msgs = 10 },
|
|
new SubDetail { Subject = "baz.>", Sid = "2", Msgs = 20, Queue = "q1" },
|
|
],
|
|
};
|
|
|
|
info.Subs.Length.ShouldBe(2);
|
|
info.SubsDetail.Length.ShouldBe(2);
|
|
info.SubsDetail[1].Queue.ShouldBe("q1");
|
|
}
|
|
}
|