From 242d1169e8ffb92c6145e04a4a8dbabc194c0786 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 27 Aug 2026 16:32:49 +0200 Subject: [PATCH] test(core): add network and process collector tests --- .../Collectors/NetworkTests.cs | 83 +++++++++++++++++++ .../Collectors/ProcessTests.cs | 72 ++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 tests/Vantage.Core.Tests/Collectors/NetworkTests.cs create mode 100644 tests/Vantage.Core.Tests/Collectors/ProcessTests.cs diff --git a/tests/Vantage.Core.Tests/Collectors/NetworkTests.cs b/tests/Vantage.Core.Tests/Collectors/NetworkTests.cs new file mode 100644 index 0000000..4b61443 --- /dev/null +++ b/tests/Vantage.Core.Tests/Collectors/NetworkTests.cs @@ -0,0 +1,83 @@ +using Vantage.Core.Collectors.Network; +using Vantage.Core.Models; +using Vantage.Core.Tests.Fakes; +using Vantage.Core.Tests.Fixtures; +using Xunit; + +namespace Vantage.Core.Tests.Collectors; + +public class NetworkTests +{ + [Fact] + public void NetDevParser_ExtractsPerInterfaceCounters() + { + var interfaces = NetDevParser.Parse(CollectorFixtures.ProcNetDev); + + Assert.Equal(3, interfaces.Count); + var eth0 = interfaces.Single(i => i.Name == "eth0"); + Assert.Equal(9_000_000_000, eth0.RxBytes); + Assert.Equal(2_000_000_000, eth0.TxBytes); + Assert.Equal(1_200_000, eth0.RxPackets); + Assert.Equal(980_000, eth0.TxPackets); + Assert.True(interfaces.Single(i => i.Name == "lo").IsLoopback); + } + + [Fact] + public void NetDevParser_SkipsHeaderLines() + { + var interfaces = NetDevParser.Parse(CollectorFixtures.ProcNetDev); + Assert.DoesNotContain(interfaces, i => i.Name.Contains("Inter") || i.Name.Contains("face")); + } + + [Fact] + public void Throughput_ComputesBytesPerSecondFromDelta() + { + var t0 = new DateTimeOffset(2026, 8, 26, 12, 0, 0, TimeSpan.Zero); + var before = new NetworkSnapshot(t0, NetDevParser.Parse(CollectorFixtures.ProcNetDev)); + var after = new NetworkSnapshot(t0.AddSeconds(10), NetDevParser.Parse(CollectorFixtures.ProcNetDevLater)); + + var rates = NetworkThroughput.Between(before, after); + var eth0 = rates.Single(r => r.Name == "eth0"); + + Assert.Equal(1000, eth0.RxBytesPerSecond, 3); + Assert.Equal(400, eth0.TxBytesPerSecond, 3); + } + + [Fact] + public void Throughput_CounterResetYieldsZeroNotNegative() + { + var t0 = new DateTimeOffset(2026, 8, 26, 12, 0, 0, TimeSpan.Zero); + var before = new NetworkSnapshot(t0, new[] { new InterfaceCounters("eth0", 9_000_000_000, 8_000_000_000, 0, 0) }); + var after = new NetworkSnapshot(t0.AddSeconds(5), new[] { new InterfaceCounters("eth0", 1000, 500, 0, 0) }); + + var eth0 = NetworkThroughput.Between(before, after).Single(); + Assert.Equal(0, eth0.RxBytesPerSecond); + Assert.Equal(0, eth0.TxBytesPerSecond); + } + + [Fact] + public async Task SshNetworkProvider_ReadsProcNetDev() + { + var runner = new FakeSshCommandRunner(CollectorFixtures.ProcNetDev); + var provider = new SshNetworkProvider(runner); + + var snapshot = await provider.GetSnapshotAsync(new HostConfig { Id = "h1" }, CancellationToken.None); + + Assert.Equal("cat /proc/net/dev", runner.Commands.Single()); + Assert.Equal(2_000_000_000, snapshot.TotalTxBytes); + } + + [Fact] + public async Task MockNetworkProvider_CounterIncreasesOverTime() + { + var now = new DateTimeOffset(2026, 8, 26, 12, 0, 0, TimeSpan.Zero); + var provider = new MockNetworkProvider(() => now); + var host = new HostConfig { Id = "h1" }; + + var first = await provider.GetSnapshotAsync(host, CancellationToken.None); + now = now.AddSeconds(60); + var second = await provider.GetSnapshotAsync(host, CancellationToken.None); + + Assert.True(second.Find("eth0")!.Value.RxBytes > first.Find("eth0")!.Value.RxBytes); + } +} diff --git a/tests/Vantage.Core.Tests/Collectors/ProcessTests.cs b/tests/Vantage.Core.Tests/Collectors/ProcessTests.cs new file mode 100644 index 0000000..efbd1db --- /dev/null +++ b/tests/Vantage.Core.Tests/Collectors/ProcessTests.cs @@ -0,0 +1,72 @@ +using Vantage.Core.Collectors.Processes; +using Vantage.Core.Models; +using Vantage.Core.Tests.Fakes; +using Vantage.Core.Tests.Fixtures; +using Xunit; + +namespace Vantage.Core.Tests.Collectors; + +public class ProcessTests +{ + [Fact] + public void Parser_ReadsAllColumns() + { + var processes = ProcessListParser.Parse(CollectorFixtures.PsTopOutput); + + Assert.Equal(5, processes.Count); + var node = processes.Single(p => p.Pid == 842); + Assert.Equal("node", node.User); + Assert.Equal(12.6, node.CpuPercent, 3); + Assert.Equal(6.3, node.MemoryPercent, 3); + Assert.Equal("node server.js", node.Command); + } + + [Fact] + public void TopByCpu_SortsDescendingAndLimits() + { + var top = ProcessListParser.TopByCpu(CollectorFixtures.PsTopOutput, 2); + + Assert.Equal(2, top.Count); + Assert.Equal(842, top[0].Pid); + Assert.Equal(311, top[1].Pid); + } + + [Fact] + public void Parser_IgnoresLinesWithoutNumericPid() + { + var processes = ProcessListParser.Parse("PID USER %CPU %MEM COMMAND\n123 root 1.0 2.0 init\n"); + Assert.Single(processes); + Assert.Equal(123, processes[0].Pid); + } + + [Fact] + public void BuildCommand_SortsByCpuAndLimits() + { + var command = SshProcessProvider.BuildCommand(7); + Assert.Contains("--sort=-pcpu", command); + Assert.Contains("head -n 7", command); + } + + [Fact] + public async Task SshProcessProvider_ParsesRunnerOutput() + { + var runner = new FakeSshCommandRunner(CollectorFixtures.PsTopOutput); + var provider = new SshProcessProvider(runner); + + var top = await provider.GetTopProcessesAsync(new HostConfig(), 3, CancellationToken.None); + + Assert.Equal(3, top.Count); + Assert.Equal(842, top[0].Pid); + } + + [Fact] + public async Task MockProcessProvider_ReturnsTopNSortedByCpu() + { + var provider = new MockProcessProvider(); + var top = await provider.GetTopProcessesAsync(new HostConfig { Id = "h1" }, 4, CancellationToken.None); + + Assert.Equal(4, top.Count); + for (var i = 1; i < top.Count; i++) + Assert.True(top[i - 1].CpuPercent >= top[i].CpuPercent); + } +}