Files
scadalink-design/lmxproxy/tests/ZB.MOM.WW.LmxProxy.Host.Tests/Metrics/PerformanceMetricsTests.cs
2026-03-22 00:14:40 -04:00

148 lines
4.6 KiB
C#

using System;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Metrics;
namespace ZB.MOM.WW.LmxProxy.Host.Tests.Metrics
{
public class PerformanceMetricsTests
{
[Fact]
public void RecordOperation_TracksCountAndDuration()
{
using var metrics = new PerformanceMetrics();
for (int i = 0; i < 5; i++)
{
metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), true);
}
var stats = metrics.GetStatistics();
stats.Should().ContainKey("TestOp");
stats["TestOp"].TotalCount.Should().Be(5);
}
[Fact]
public void RecordOperation_TracksSuccessAndFailure()
{
using var metrics = new PerformanceMetrics();
for (int i = 0; i < 3; i++)
{
metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), true);
}
for (int i = 0; i < 2; i++)
{
metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), false);
}
var stats = metrics.GetStatistics();
stats["TestOp"].SuccessRate.Should().BeApproximately(0.6, 0.001);
}
[Fact]
public void GetStatistics_CalculatesP95Correctly()
{
using var metrics = new PerformanceMetrics();
for (int i = 1; i <= 100; i++)
{
metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(i), true);
}
var stats = metrics.GetStatistics();
stats["TestOp"].Percentile95Milliseconds.Should().BeApproximately(95.0, 1.0);
}
[Fact]
public void RollingBuffer_CapsAt1000Samples()
{
using var metrics = new PerformanceMetrics();
for (int i = 0; i < 1500; i++)
{
metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(i), true);
}
var stats = metrics.GetStatistics();
// TotalCount tracks all 1500 but percentile is computed from the last 1000
stats["TestOp"].TotalCount.Should().Be(1500);
// The rolling buffer should have entries from 500-1499
// P95 of 500..1499 should be around 1449
stats["TestOp"].Percentile95Milliseconds.Should().BeGreaterThan(1000);
}
[Fact]
public void BeginOperation_RecordsDurationOnDispose()
{
using var metrics = new PerformanceMetrics();
using (var scope = metrics.BeginOperation("TestOp"))
{
System.Threading.Thread.Sleep(50);
}
var stats = metrics.GetStatistics();
stats.Should().ContainKey("TestOp");
stats["TestOp"].TotalCount.Should().Be(1);
stats["TestOp"].AverageMilliseconds.Should().BeGreaterOrEqualTo(40);
}
[Fact]
public void TimingScope_DefaultsToSuccess()
{
using var metrics = new PerformanceMetrics();
using (metrics.BeginOperation("TestOp"))
{
// Do nothing — default is success
}
var stats = metrics.GetStatistics();
stats["TestOp"].SuccessCount.Should().Be(1);
}
[Fact]
public void TimingScope_RespectsSetSuccessFalse()
{
using var metrics = new PerformanceMetrics();
using (var scope = metrics.BeginOperation("TestOp"))
{
scope.SetSuccess(false);
}
var stats = metrics.GetStatistics();
stats["TestOp"].SuccessCount.Should().Be(0);
stats["TestOp"].TotalCount.Should().Be(1);
}
[Fact]
public void GetMetrics_ReturnsNullForUnknownOperation()
{
using var metrics = new PerformanceMetrics();
var result = metrics.GetMetrics("DoesNotExist");
result.Should().BeNull();
}
[Fact]
public void GetAllMetrics_ReturnsAllTrackedOperations()
{
using var metrics = new PerformanceMetrics();
metrics.RecordOperation("Read", TimeSpan.FromMilliseconds(10), true);
metrics.RecordOperation("Write", TimeSpan.FromMilliseconds(20), true);
metrics.RecordOperation("Subscribe", TimeSpan.FromMilliseconds(5), true);
var all = metrics.GetAllMetrics();
all.Should().ContainKey("Read");
all.Should().ContainKey("Write");
all.Should().ContainKey("Subscribe");
all.Count.Should().Be(3);
}
}
}