Files
natsdotnet/tests/NATS.Server.Gateways.Tests/Gateways/ReplyMapCacheTests.cs
Joseph Doherty 88a82ee860 docs: add XML doc comments to server types and fix flaky test timings
Add XML doc comments to public properties across EventTypes, Connz, Varz,
NatsOptions, StreamConfig, IStreamStore, FileStore, MqttListener,
MqttSessionStore, MessageTraceContext, and JetStreamApiResponse. Fix flaky
tests by increasing timing margins (ResponseTracker expiry 1ms→50ms,
sleep 50ms→200ms) and document known flaky test patterns in tests.md.
2026-03-13 18:47:48 -04:00

163 lines
4.6 KiB
C#

using NATS.Server.Gateways;
using Shouldly;
namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary>
/// Tests for the ReplyMapCache LRU cache with TTL expiration.
/// Go reference: gateway.go — reply mapping cache.
/// </summary>
public class ReplyMapCacheTests
{
// Go: gateway.go — cache is initially empty, lookups return false
[Fact]
public void TryGet_returns_false_on_empty()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
var found = cache.TryGet("_INBOX.abc", out var value);
found.ShouldBeFalse();
value.ShouldBeNull();
}
// Go: gateway.go — cached mapping is retrievable after Set
[Fact]
public void Set_and_TryGet_round_trips()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.Set("_INBOX.abc", "_GR_.cluster1.42._INBOX.abc");
var found = cache.TryGet("_INBOX.abc", out var value);
found.ShouldBeTrue();
value.ShouldBe("_GR_.cluster1.42._INBOX.abc");
}
// Go: gateway.go — LRU eviction removes the least-recently-used entry when at capacity
[Fact]
public void LRU_eviction_at_capacity()
{
var cache = new ReplyMapCache(capacity: 2, ttlMs: 60_000);
cache.Set("key1", "val1");
cache.Set("key2", "val2");
// Adding a third entry should evict the LRU entry (key1, since key2 was added last)
cache.Set("key3", "val3");
cache.TryGet("key1", out _).ShouldBeFalse();
cache.TryGet("key2", out var v2).ShouldBeTrue();
v2.ShouldBe("val2");
cache.TryGet("key3", out var v3).ShouldBeTrue();
v3.ShouldBe("val3");
}
// Go: gateway.go — entries expire after the configured TTL window
[Fact]
public void TTL_expiration()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 50);
cache.Set("_INBOX.ttl", "_GR_.c1.1._INBOX.ttl");
Thread.Sleep(200); // Wait longer than the 50ms TTL
var found = cache.TryGet("_INBOX.ttl", out var value);
found.ShouldBeFalse();
value.ShouldBeNull();
}
// Go: gateway.go — hit counter tracks successful cache lookups
[Fact]
public void Hits_incremented_on_hit()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.Set("key", "value");
cache.TryGet("key", out _);
cache.TryGet("key", out _);
cache.Hits.ShouldBe(2);
cache.Misses.ShouldBe(0);
}
// Go: gateway.go — miss counter tracks failed lookups
[Fact]
public void Misses_incremented_on_miss()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.TryGet("nope", out _);
cache.TryGet("also-nope", out _);
cache.Misses.ShouldBe(2);
cache.Hits.ShouldBe(0);
}
// Go: gateway.go — Clear removes all entries from the cache
[Fact]
public void Clear_removes_all()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.Set("a", "1");
cache.Set("b", "2");
cache.Set("c", "3");
cache.Clear();
cache.Count.ShouldBe(0);
cache.TryGet("a", out _).ShouldBeFalse();
cache.TryGet("b", out _).ShouldBeFalse();
cache.TryGet("c", out _).ShouldBeFalse();
}
// Go: gateway.go — Set on existing key updates the value and promotes to MRU
[Fact]
public void Set_updates_existing()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.Set("key", "original");
cache.Set("key", "updated");
cache.TryGet("key", out var value).ShouldBeTrue();
value.ShouldBe("updated");
cache.Count.ShouldBe(1);
}
// Go: gateway.go — PurgeExpired removes only expired entries
[Fact]
public void PurgeExpired_removes_old_entries()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 50);
cache.Set("old1", "v1");
cache.Set("old2", "v2");
Thread.Sleep(200); // Ensure both entries are past the 50ms TTL
var purged = cache.PurgeExpired();
purged.ShouldBe(2);
cache.Count.ShouldBe(0);
}
// Go: gateway.go — Count reflects the current number of cached entries
[Fact]
public void Count_reflects_entries()
{
var cache = new ReplyMapCache(capacity: 16, ttlMs: 60_000);
cache.Count.ShouldBe(0);
cache.Set("a", "1");
cache.Count.ShouldBe(1);
cache.Set("b", "2");
cache.Count.ShouldBe(2);
cache.Set("c", "3");
cache.Count.ShouldBe(3);
cache.Clear();
cache.Count.ShouldBe(0);
}
}