Files
natsdotnet/tests/NATS.Server.Monitoring.Tests/Monitoring/ClosedConnectionRingBufferTests.cs
Joseph Doherty 0c086522a4 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.
2026-03-12 15:44:12 -04:00

241 lines
7.5 KiB
C#

// Go reference: server/monitor.go — closedClients ring buffer, ClosedState tracking.
// These tests verify the fixed-size ring buffer used to track recently closed connections
// for the /connz?state=closed monitoring endpoint.
using NATS.Server.Monitoring;
namespace NATS.Server.Monitoring.Tests.Monitoring;
public class ClosedConnectionRingBufferTests
{
// -----------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------
private static ClosedClient MakeEntry(ulong cid, string reason = "normal") => new()
{
Cid = cid,
Ip = "127.0.0.1",
Port = 4222,
Start = DateTime.UtcNow.AddSeconds(-10),
Stop = DateTime.UtcNow,
Reason = reason,
};
// -----------------------------------------------------------------------
// 1. Add_IncreasesCount
// -----------------------------------------------------------------------
/// <summary>
/// Adding entries should increase Count up to capacity.
/// </summary>
[Fact]
public void Add_IncreasesCount()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
buf.Count.ShouldBe(0);
buf.Add(MakeEntry(1));
buf.Count.ShouldBe(1);
buf.Add(MakeEntry(2));
buf.Count.ShouldBe(2);
buf.Add(MakeEntry(3));
buf.Count.ShouldBe(3);
}
// -----------------------------------------------------------------------
// 2. Add_RingOverwrite_CapacityNotExceeded
// -----------------------------------------------------------------------
/// <summary>
/// When capacity is exceeded the count stays at capacity (oldest entry is overwritten).
/// </summary>
[Fact]
public void Add_RingOverwrite_CapacityNotExceeded()
{
const int capacity = 5;
var buf = new ClosedConnectionRingBuffer(capacity);
for (var i = 1; i <= capacity + 3; i++)
buf.Add(MakeEntry((ulong)i));
buf.Count.ShouldBe(capacity);
}
// -----------------------------------------------------------------------
// 3. GetAll_ReturnsNewestFirst
// -----------------------------------------------------------------------
/// <summary>
/// GetAll should return entries ordered newest-first.
/// </summary>
[Fact]
public void GetAll_ReturnsNewestFirst()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
buf.Add(MakeEntry(1));
buf.Add(MakeEntry(2));
buf.Add(MakeEntry(3));
var all = buf.GetAll();
all.Count.ShouldBe(3);
all[0].Cid.ShouldBe(3UL); // newest
all[1].Cid.ShouldBe(2UL);
all[2].Cid.ShouldBe(1UL); // oldest
}
// -----------------------------------------------------------------------
// 4. GetAll_EmptyBuffer_ReturnsEmpty
// -----------------------------------------------------------------------
/// <summary>
/// GetAll on an empty buffer should return an empty list.
/// </summary>
[Fact]
public void GetAll_EmptyBuffer_ReturnsEmpty()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
var all = buf.GetAll();
all.ShouldBeEmpty();
}
// -----------------------------------------------------------------------
// 5. GetRecent_ReturnsRequestedCount
// -----------------------------------------------------------------------
/// <summary>
/// GetRecent(n) where n &lt;= Count should return exactly n entries.
/// </summary>
[Fact]
public void GetRecent_ReturnsRequestedCount()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
for (var i = 1; i <= 8; i++)
buf.Add(MakeEntry((ulong)i));
var recent = buf.GetRecent(3);
recent.Count.ShouldBe(3);
recent[0].Cid.ShouldBe(8UL); // newest
recent[1].Cid.ShouldBe(7UL);
recent[2].Cid.ShouldBe(6UL);
}
// -----------------------------------------------------------------------
// 6. GetRecent_LessThanAvailable_ReturnsAll
// -----------------------------------------------------------------------
/// <summary>
/// GetRecent(n) where n &gt; Count should return all available entries.
/// </summary>
[Fact]
public void GetRecent_LessThanAvailable_ReturnsAll()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
buf.Add(MakeEntry(1));
buf.Add(MakeEntry(2));
var recent = buf.GetRecent(100);
recent.Count.ShouldBe(2);
}
// -----------------------------------------------------------------------
// 7. TotalClosed_TracksAllAdditions
// -----------------------------------------------------------------------
/// <summary>
/// TotalClosed should increment for every Add, even after the ring wraps around.
/// </summary>
[Fact]
public void TotalClosed_TracksAllAdditions()
{
const int capacity = 4;
var buf = new ClosedConnectionRingBuffer(capacity);
buf.TotalClosed.ShouldBe(0L);
for (var i = 1; i <= 10; i++)
buf.Add(MakeEntry((ulong)i));
buf.TotalClosed.ShouldBe(10L);
buf.Count.ShouldBe(capacity); // buffer is full but total reflects all 10
}
// -----------------------------------------------------------------------
// 8. Clear_ResetsCountAndBuffer
// -----------------------------------------------------------------------
/// <summary>
/// Clear should reset Count to zero and GetAll should return an empty list.
/// TotalClosed is intentionally not reset because it is a running lifetime counter.
/// </summary>
[Fact]
public void Clear_ResetsCountAndBuffer()
{
var buf = new ClosedConnectionRingBuffer(capacity: 10);
buf.Add(MakeEntry(1));
buf.Add(MakeEntry(2));
buf.Add(MakeEntry(3));
buf.TotalClosed.ShouldBe(3L);
buf.Clear();
buf.Count.ShouldBe(0);
buf.GetAll().ShouldBeEmpty();
// TotalClosed is a lifetime counter; it is not reset by Clear.
buf.TotalClosed.ShouldBe(3L);
}
// -----------------------------------------------------------------------
// 9. Capacity_ReturnsConfiguredSize
// -----------------------------------------------------------------------
/// <summary>
/// Capacity should reflect the value passed to the constructor.
/// </summary>
[Fact]
public void Capacity_ReturnsConfiguredSize()
{
var buf = new ClosedConnectionRingBuffer(capacity: 42);
buf.Capacity.ShouldBe(42);
}
// -----------------------------------------------------------------------
// 10. Add_WrapsCorrectly
// -----------------------------------------------------------------------
/// <summary>
/// After adding capacity+1 items the oldest entry (cid=1) should no longer be present,
/// and the buffer should contain the most recent 'capacity' items.
/// </summary>
[Fact]
public void Add_WrapsCorrectly()
{
const int capacity = 5;
var buf = new ClosedConnectionRingBuffer(capacity);
for (var i = 1; i <= capacity + 1; i++)
buf.Add(MakeEntry((ulong)i));
var all = buf.GetAll();
all.Count.ShouldBe(capacity);
// cid=1 (the oldest) should have been overwritten
all.Any(e => e.Cid == 1UL).ShouldBeFalse();
// The newest entry (cid=capacity+1) should be first
all[0].Cid.ShouldBe((ulong)(capacity + 1));
}
}