From 77ab104be3f55c65ffc5b62b27676bcd27166b2d Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 28 Aug 2026 12:13:39 +0200 Subject: [PATCH] test(core): add metrics provider and parser tests --- .../Metrics/MetricsBundleParserTests.cs | 31 +++++++++++ .../Metrics/MetricsHistoryTests.cs | 37 +++++++++++++ .../Metrics/MockMetricsProviderTests.cs | 54 +++++++++++++++++++ .../Metrics/SshMetricsProviderTests.cs | 33 ++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 tests/Vantage.Core.Tests/Metrics/MetricsBundleParserTests.cs create mode 100644 tests/Vantage.Core.Tests/Metrics/MetricsHistoryTests.cs create mode 100644 tests/Vantage.Core.Tests/Metrics/MockMetricsProviderTests.cs create mode 100644 tests/Vantage.Core.Tests/Metrics/SshMetricsProviderTests.cs diff --git a/tests/Vantage.Core.Tests/Metrics/MetricsBundleParserTests.cs b/tests/Vantage.Core.Tests/Metrics/MetricsBundleParserTests.cs new file mode 100644 index 0000000..7f02675 --- /dev/null +++ b/tests/Vantage.Core.Tests/Metrics/MetricsBundleParserTests.cs @@ -0,0 +1,31 @@ +using Vantage.Core.Metrics; +using Vantage.Core.Tests.Fixtures; +using Xunit; + +namespace Vantage.Core.Tests.Metrics; + +public class MetricsBundleParserTests +{ + [Fact] + public void Parse_BuildsHostMetricsFromFullBundle() + { + var metrics = MetricsBundleParser.Parse(MetricsFixtures.FullBundle()); + + Assert.Equal(80.0, metrics.CpuUsagePercent, 1); + Assert.Equal(8000000L * 1024, metrics.MemoryTotalBytes); + Assert.Equal(6000000L * 1024, metrics.MemoryUsedBytes); + Assert.Equal(75.0, metrics.MemoryUsedPercent, 1); + Assert.Equal(100000000000L, metrics.DiskTotalBytes); + Assert.Equal(40000000000L, metrics.DiskUsedBytes); + Assert.Equal(40.0, metrics.DiskUsedPercent, 1); + Assert.Equal(0.50, metrics.Load1, 3); + Assert.Equal(123456.78, metrics.Uptime.TotalSeconds, 2); + } + + [Fact] + public void Parse_ThrowsWhenSectionMissing() + { + var truncated = "@@vantage:loadavg\n" + MetricsFixtures.Loadavg + "\n"; + Assert.Throws(() => MetricsBundleParser.Parse(truncated)); + } +} diff --git a/tests/Vantage.Core.Tests/Metrics/MetricsHistoryTests.cs b/tests/Vantage.Core.Tests/Metrics/MetricsHistoryTests.cs new file mode 100644 index 0000000..823af9a --- /dev/null +++ b/tests/Vantage.Core.Tests/Metrics/MetricsHistoryTests.cs @@ -0,0 +1,37 @@ +using Vantage.Core.Metrics; +using Xunit; + +namespace Vantage.Core.Tests.Metrics; + +public class MetricsHistoryTests +{ + [Fact] + public void Snapshot_ReturnsValuesInInsertionOrder() + { + var history = new MetricsHistory(5); + history.Add(1); + history.Add(2); + history.Add(3); + + Assert.Equal(new double[] { 1, 2, 3 }, history.Snapshot()); + Assert.Equal(3, history.Latest); + } + + [Fact] + public void Add_WrapsAroundAndKeepsMostRecentWindow() + { + var history = new MetricsHistory(3); + for (var i = 1; i <= 5; i++) + history.Add(i); + + Assert.Equal(3, history.Count); + Assert.Equal(new double[] { 3, 4, 5 }, history.Snapshot()); + Assert.Equal(5, history.Latest); + } + + [Fact] + public void Constructor_RejectsNonPositiveCapacity() + { + Assert.Throws(() => new MetricsHistory(0)); + } +} diff --git a/tests/Vantage.Core.Tests/Metrics/MockMetricsProviderTests.cs b/tests/Vantage.Core.Tests/Metrics/MockMetricsProviderTests.cs new file mode 100644 index 0000000..5a39954 --- /dev/null +++ b/tests/Vantage.Core.Tests/Metrics/MockMetricsProviderTests.cs @@ -0,0 +1,54 @@ +using Vantage.Core.Metrics; +using Vantage.Core.Models; +using Xunit; + +namespace Vantage.Core.Tests.Metrics; + +public class MockMetricsProviderTests +{ + [Fact] + public async Task GetMetricsAsync_ProducesValuesInPlausibleRanges() + { + var provider = new MockMetricsProvider(() => DateTimeOffset.UnixEpoch.AddSeconds(500)); + var host = new HostConfig { Id = "host-a", Name = "a" }; + + var metrics = await provider.GetMetricsAsync(host, CancellationToken.None); + + Assert.InRange(metrics.CpuUsagePercent, 0, 100); + Assert.InRange(metrics.MemoryUsedPercent, 0, 100); + Assert.True(metrics.MemoryTotalBytes > 0); + Assert.True(metrics.MemoryUsedBytes <= metrics.MemoryTotalBytes); + Assert.True(metrics.Uptime > TimeSpan.Zero); + } + + [Fact] + public async Task GetMetricsAsync_VariesOverTimeButKeepsTotalsStable() + { + var now = DateTimeOffset.UnixEpoch; + var provider = new MockMetricsProvider(() => now); + var host = new HostConfig { Id = "host-b" }; + + now = DateTimeOffset.UnixEpoch.AddSeconds(1000); + var first = await provider.GetMetricsAsync(host, CancellationToken.None); + now = DateTimeOffset.UnixEpoch.AddSeconds(1012); + var second = await provider.GetMetricsAsync(host, CancellationToken.None); + + Assert.NotEqual(first.CpuUsagePercent, second.CpuUsagePercent); + Assert.Equal(first.MemoryTotalBytes, second.MemoryTotalBytes); + Assert.Equal(first.DiskTotalBytes, second.DiskTotalBytes); + } + + [Fact] + public async Task GetMetricsAsync_IsDeterministicPerHostIdentity() + { + var at = DateTimeOffset.UnixEpoch.AddSeconds(2000); + var providerOne = new MockMetricsProvider(() => at); + var providerTwo = new MockMetricsProvider(() => at); + + var one = await providerOne.GetMetricsAsync(new HostConfig { Id = "same" }, CancellationToken.None); + var two = await providerTwo.GetMetricsAsync(new HostConfig { Id = "same" }, CancellationToken.None); + + Assert.Equal(one.MemoryTotalBytes, two.MemoryTotalBytes); + Assert.Equal(one.DiskTotalBytes, two.DiskTotalBytes); + } +} diff --git a/tests/Vantage.Core.Tests/Metrics/SshMetricsProviderTests.cs b/tests/Vantage.Core.Tests/Metrics/SshMetricsProviderTests.cs new file mode 100644 index 0000000..d1f83e3 --- /dev/null +++ b/tests/Vantage.Core.Tests/Metrics/SshMetricsProviderTests.cs @@ -0,0 +1,33 @@ +using Vantage.Core.Metrics; +using Vantage.Core.Models; +using Vantage.Core.Tests.Fakes; +using Vantage.Core.Tests.Fixtures; +using Xunit; + +namespace Vantage.Core.Tests.Metrics; + +public class SshMetricsProviderTests +{ + [Fact] + public async Task GetMetricsAsync_ParsesBundleFromRunner() + { + var runner = new FakeSshCommandRunner(MetricsFixtures.FullBundle()); + var provider = new SshMetricsProvider(runner); + + var metrics = await provider.GetMetricsAsync(new HostConfig { DiskMount = "/srv" }, CancellationToken.None); + + Assert.Equal(80.0, metrics.CpuUsagePercent, 1); + Assert.Equal(75.0, metrics.MemoryUsedPercent, 1); + } + + [Fact] + public async Task GetMetricsAsync_UsesConfiguredMountInCommand() + { + var runner = new FakeSshCommandRunner(MetricsFixtures.FullBundle()); + var provider = new SshMetricsProvider(runner); + + await provider.GetMetricsAsync(new HostConfig { DiskMount = "/srv/data" }, CancellationToken.None); + + Assert.Contains("df -B1 -P '/srv/data'", runner.Commands.Single()); + } +}