test(core): add system parsing and Prometheus tests

This commit is contained in:
dualfroz committed 2026-08-28 14:25:19 +02:00
1 parent 77ab104be3
commit d679231fed
3 files changed
+195

No files matched your search

@@ -0,0 +1,71 @@
using Vantage.Core.Metrics.Parsing;
using Vantage.Core.Tests.Fixtures;
using Xunit;
namespace Vantage.Core.Tests.Parsing;
public class ParsersTests
{
[Fact]
public void LoadAvgParser_ReadsFirstThreeValues()
{
var load = LoadAvgParser.Parse(MetricsFixtures.Loadavg);
Assert.Equal(0.50, load.Load1, 3);
Assert.Equal(0.40, load.Load5, 3);
Assert.Equal(0.30, load.Load15, 3);
}
[Fact]
public void UptimeParser_ReadsSeconds()
{
var uptime = UptimeParser.Parse(MetricsFixtures.Uptime);
Assert.Equal(123456.78, uptime.TotalSeconds, 2);
}
[Fact]
public void MemInfoParser_UsesMemAvailable()
{
var memory = MemInfoParser.Parse(MetricsFixtures.Meminfo);
Assert.Equal(8000000L * 1024, memory.TotalBytes);
Assert.Equal(6000000L * 1024, memory.UsedBytes);
}
[Fact]
public void MemInfoParser_FallsBackToMemFreeWhenNoAvailable()
{
var memory = MemInfoParser.Parse("MemTotal: 4000 kB\nMemFree: 1000 kB\n");
Assert.Equal(4000L * 1024, memory.TotalBytes);
Assert.Equal(3000L * 1024, memory.UsedBytes);
}
[Fact]
public void DfParser_ReadsTotalAndUsedFromDataRow()
{
var disk = DfParser.Parse(MetricsFixtures.Df);
Assert.Equal(100000000000L, disk.TotalBytes);
Assert.Equal(40000000000L, disk.UsedBytes);
}
[Fact]
public void ProcStatParser_SumsTotalAndIdle()
{
var times = ProcStatParser.Parse(MetricsFixtures.Stat1);
Assert.Equal(1000, times.TotalTime);
Assert.Equal(800, times.IdleTime);
}
[Fact]
public void CpuUsage_ComputesBusyFractionFromTwoSnapshots()
{
var before = ProcStatParser.Parse(MetricsFixtures.Stat1);
var after = ProcStatParser.Parse(MetricsFixtures.Stat2);
Assert.Equal(80.0, CpuUsage.Compute(before, after), 1);
}
[Fact]
public void CpuUsage_ReturnsZeroWhenNoDelta()
{
var times = new CpuTimes(800, 1000);
Assert.Equal(0, CpuUsage.Compute(times, times));
}
}
@@ -0,0 +1,56 @@
using Vantage.Core.Prometheus;
using Vantage.Core.Tests.Fixtures;
using Xunit;
namespace Vantage.Core.Tests.Prometheus;
public class PrometheusParserTests
{
[Fact]
public void Parse_SkipsCommentsAndBlankLines()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.Mixed);
Assert.DoesNotContain(samples, s => s.Name.StartsWith("#"));
}
[Fact]
public void Parse_ReadsLabelsValueAndTimestamp()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.Mixed);
var post200 = samples.Single(s =>
s.Name == "http_requests_total" && s.HasLabel("code", "200"));
Assert.Equal("post", post200.Label("method"));
Assert.Equal(1027, post200.Value);
Assert.Equal(1395066363000, post200.TimestampMs);
}
[Fact]
public void Parse_MetricWithoutLabels()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.Mixed);
var bare = samples.Single(s => s.Name == "metric_without_labels");
Assert.Empty(bare.Labels);
Assert.Equal(12.47, bare.Value, 3);
}
[Fact]
public void Parse_HandlesEscapedLabelValues()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.Mixed);
var msdos = samples.Single(s => s.Name == "msdos_file_access_time_seconds");
Assert.Equal("C:\\DIR\\FILE.TXT", msdos.Label("path"));
Assert.Contains("\n", msdos.Label("error"));
Assert.Contains("\"FILE.TXT\"", msdos.Label("error"));
}
[Fact]
public void Parse_HandlesInfinityAndNaN()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.Mixed);
Assert.True(double.IsPositiveInfinity(samples.Single(s => s.Name == "something_weird").Value));
Assert.True(double.IsNaN(samples.Single(s => s.Name == "gauge_nan_value").Value));
}
}
@@ -0,0 +1,68 @@
using Vantage.Core.Models;
using Vantage.Core.Prometheus;
using Vantage.Core.Tests.Fakes;
using Vantage.Core.Tests.Fixtures;
using Xunit;
namespace Vantage.Core.Tests.Prometheus;
public class PrometheusProviderTests
{
private static readonly DateTimeOffset Now = new(2026, 8, 26, 12, 0, 0, TimeSpan.Zero);
[Fact]
public void Mapper_MapsMemoryDiskLoadAndUptime()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.NodeScrape1());
var metrics = PrometheusNodeMapper.Map(samples, "/", cpuPercent: 0, Now);
Assert.Equal(8_000_000_000, metrics.MemoryTotalBytes);
Assert.Equal(6_000_000_000, metrics.MemoryUsedBytes);
Assert.Equal(75.0, metrics.MemoryUsedPercent, 1);
Assert.Equal(100_000_000_000, metrics.DiskTotalBytes);
Assert.Equal(40_000_000_000, metrics.DiskUsedBytes);
Assert.Equal(0.5, metrics.Load1, 3);
Assert.Equal(TimeSpan.FromSeconds(86400), metrics.Uptime);
}
[Fact]
public void Mapper_SelectsFilesystemByMountpoint()
{
var samples = PrometheusTextParser.Parse(PrometheusFixtures.NodeScrape1());
var boot = PrometheusNodeMapper.Map(samples, "/boot", cpuPercent: 0, Now);
Assert.Equal(500_000_000, boot.DiskTotalBytes);
Assert.Equal(100_000_000, boot.DiskUsedBytes);
}
[Fact]
public void CpuPercent_ComputedFromCounterDelta()
{
var first = PrometheusNodeMapper.ExtractCpuCounters(PrometheusTextParser.Parse(PrometheusFixtures.NodeScrape1()));
var second = PrometheusNodeMapper.ExtractCpuCounters(PrometheusTextParser.Parse(PrometheusFixtures.NodeScrape2()));
Assert.Equal(20.0, PrometheusNodeMapper.CpuPercent(first, second), 1);
}
[Fact]
public async Task Provider_FirstScrapeZeroCpu_SecondScrapeComputesCpu()
{
var handler = StubHttpMessageHandler.Sequence(PrometheusFixtures.NodeScrape1(), PrometheusFixtures.NodeScrape2());
var provider = new PrometheusMetricsProvider(new HttpClient(handler), () => Now);
var host = new HostConfig { Id = "h1", Address = "10.0.0.1", MetricsEndpoint = "http://10.0.0.1:9100/metrics" };
var first = await provider.GetMetricsAsync(host, CancellationToken.None);
var second = await provider.GetMetricsAsync(host, CancellationToken.None);
Assert.Equal(0, first.CpuUsagePercent);
Assert.Equal(20.0, second.CpuUsagePercent, 1);
Assert.Equal(75.0, second.MemoryUsedPercent, 1);
}
[Fact]
public void ResolveMetricsEndpoint_DefaultsToNodeExporterPort()
{
var host = new HostConfig { Address = "10.0.0.5" };
Assert.Equal("http://10.0.0.5:9100/metrics", host.ResolveMetricsEndpoint());
}
}