feat: add service export latency tracking with p50/p90/p99 (Gap 9.1)
Add ServiceLatencyTracker with sorted-sample histogram, percentile getters (p50/p90/p99), average/min/max, reset, and immutable snapshot. Wire LatencyTracker and RecordServiceLatency onto Account. Cover with 11 xUnit tests.
This commit is contained in:
137
tests/NATS.Server.Tests/Auth/ResponseThresholdTests.cs
Normal file
137
tests/NATS.Server.Tests/Auth/ResponseThresholdTests.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Tests for Account.SetServiceResponseThreshold / GetServiceResponseThreshold /
|
||||
// IsServiceResponseOverdue / CheckServiceResponse.
|
||||
// Go reference: server/accounts.go — SetServiceExportResponseThreshold (~line 2522),
|
||||
// ServiceExportResponseThreshold (~line 2510).
|
||||
|
||||
using NATS.Server.Auth;
|
||||
|
||||
namespace NATS.Server.Tests.Auth;
|
||||
|
||||
public class ResponseThresholdTests
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// SetServiceResponseThreshold / GetServiceResponseThreshold
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SetServiceResponseThreshold_StoresThreshold()
|
||||
{
|
||||
// Go ref: accounts.go SetServiceExportResponseThreshold (~line 2522)
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.foo", TimeSpan.FromSeconds(5));
|
||||
|
||||
account.ServiceResponseThresholds.ContainsKey("svc.foo").ShouldBeTrue();
|
||||
account.ServiceResponseThresholds["svc.foo"].ShouldBe(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceResponseThreshold_ReturnsStored()
|
||||
{
|
||||
// Go ref: accounts.go ServiceExportResponseThreshold (~line 2510)
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.bar", TimeSpan.FromMilliseconds(200));
|
||||
|
||||
account.GetServiceResponseThreshold("svc.bar").ShouldBe(TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceResponseThreshold_NotSet_ReturnsNull()
|
||||
{
|
||||
// Go ref: accounts.go ServiceExportResponseThreshold — returns error when export not found
|
||||
var account = new Account("test");
|
||||
|
||||
account.GetServiceResponseThreshold("svc.unknown").ShouldBeNull();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IsServiceResponseOverdue
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void IsServiceResponseOverdue_WithinThreshold_ReturnsFalse()
|
||||
{
|
||||
// Go ref: accounts.go respThresh check — elapsed < threshold ⇒ not overdue
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.a", TimeSpan.FromSeconds(10));
|
||||
|
||||
account.IsServiceResponseOverdue("svc.a", TimeSpan.FromSeconds(9)).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsServiceResponseOverdue_ExceedsThreshold_ReturnsTrue()
|
||||
{
|
||||
// Go ref: accounts.go respThresh check — elapsed > threshold ⇒ overdue
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.b", TimeSpan.FromSeconds(1));
|
||||
|
||||
account.IsServiceResponseOverdue("svc.b", TimeSpan.FromSeconds(2)).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsServiceResponseOverdue_NoThreshold_ReturnsFalse()
|
||||
{
|
||||
// Go ref: accounts.go — when no respThresh is set the timer never fires (never overdue)
|
||||
var account = new Account("test");
|
||||
|
||||
account.IsServiceResponseOverdue("svc.unregistered", TimeSpan.FromHours(1)).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetServiceResponseThreshold_OverwritesPrevious()
|
||||
{
|
||||
// Go ref: accounts.go SetServiceExportResponseThreshold — se.respThresh = maxTime overwrites
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.c", TimeSpan.FromSeconds(5));
|
||||
account.SetServiceResponseThreshold("svc.c", TimeSpan.FromSeconds(30));
|
||||
|
||||
account.GetServiceResponseThreshold("svc.c").ShouldBe(TimeSpan.FromSeconds(30));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CheckServiceResponse
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void CheckServiceResponse_Found_NotOverdue()
|
||||
{
|
||||
// Go ref: accounts.go ServiceExportResponseThreshold + respThresh timer — within window
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.d", TimeSpan.FromSeconds(10));
|
||||
|
||||
var result = account.CheckServiceResponse("svc.d", TimeSpan.FromSeconds(5));
|
||||
|
||||
result.Found.ShouldBeTrue();
|
||||
result.IsOverdue.ShouldBeFalse();
|
||||
result.Threshold.ShouldBe(TimeSpan.FromSeconds(10));
|
||||
result.Elapsed.ShouldBe(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckServiceResponse_Found_Overdue()
|
||||
{
|
||||
// Go ref: accounts.go respThresh timer fires — elapsed exceeded threshold
|
||||
var account = new Account("test");
|
||||
account.SetServiceResponseThreshold("svc.e", TimeSpan.FromSeconds(2));
|
||||
|
||||
var result = account.CheckServiceResponse("svc.e", TimeSpan.FromSeconds(5));
|
||||
|
||||
result.Found.ShouldBeTrue();
|
||||
result.IsOverdue.ShouldBeTrue();
|
||||
result.Threshold.ShouldBe(TimeSpan.FromSeconds(2));
|
||||
result.Elapsed.ShouldBe(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckServiceResponse_NotFound()
|
||||
{
|
||||
// Go ref: accounts.go — no export defined, returns error; here Found=false
|
||||
var account = new Account("test");
|
||||
|
||||
var result = account.CheckServiceResponse("svc.none", TimeSpan.FromSeconds(1));
|
||||
|
||||
result.Found.ShouldBeFalse();
|
||||
result.IsOverdue.ShouldBeFalse();
|
||||
result.Threshold.ShouldBeNull();
|
||||
result.Elapsed.ShouldBe(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user