diff --git a/tests/Vantage.Core.Tests/Fakes/FakeSshCommandRunner.cs b/tests/Vantage.Core.Tests/Fakes/FakeSshCommandRunner.cs new file mode 100644 index 0000000..89e08a4 --- /dev/null +++ b/tests/Vantage.Core.Tests/Fakes/FakeSshCommandRunner.cs @@ -0,0 +1,27 @@ +using Vantage.Core.Models; +using Vantage.Core.Ssh; + +namespace Vantage.Core.Tests.Fakes; + +public sealed class FakeSshCommandRunner : ISshCommandRunner +{ + private readonly Func respond; + + public FakeSshCommandRunner(string fixedResponse) + : this(_ => fixedResponse) + { + } + + public FakeSshCommandRunner(Func respond) + { + this.respond = respond; + } + + public List Commands { get; } = new(); + + public Task RunAsync(HostConfig host, string command, CancellationToken cancellationToken) + { + Commands.Add(command); + return Task.FromResult(respond(command)); + } +} diff --git a/tests/Vantage.Core.Tests/Fakes/StubHttpMessageHandler.cs b/tests/Vantage.Core.Tests/Fakes/StubHttpMessageHandler.cs new file mode 100644 index 0000000..c8ddcf5 --- /dev/null +++ b/tests/Vantage.Core.Tests/Fakes/StubHttpMessageHandler.cs @@ -0,0 +1,59 @@ +using System.Net; + +namespace Vantage.Core.Tests.Fakes; + +public sealed class StubHttpMessageHandler : HttpMessageHandler +{ + private readonly Func> handler; + + private StubHttpMessageHandler(Func> handler) + { + this.handler = handler; + } + + public static StubHttpMessageHandler WithStatus(HttpStatusCode status) => + new((_, _) => Task.FromResult(new HttpResponseMessage(status))); + + public static StubHttpMessageHandler Throwing(Exception exception) => + new((_, _) => Task.FromException(exception)); + + public static StubHttpMessageHandler Delayed(TimeSpan delay, HttpStatusCode status) => + new(async (_, ct) => + { + await Task.Delay(delay, ct); + return new HttpResponseMessage(status); + }); + + public static StubHttpMessageHandler ReturningText(string body, HttpStatusCode status = HttpStatusCode.OK) => + new((_, _) => Task.FromResult(new HttpResponseMessage(status) { Content = new StringContent(body) })); + + public static StubHttpMessageHandler Sequence(params string[] bodies) + { + var index = 0; + return new((_, _) => + { + var body = bodies[Math.Min(index, bodies.Length - 1)]; + index++; + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(body) }); + }); + } + + public List Requests { get; } = new(); + public List Bodies { get; } = new(); + + public static StubHttpMessageHandler Capturing(HttpStatusCode status = HttpStatusCode.OK) + { + StubHttpMessageHandler? self = null; + var handler = new StubHttpMessageHandler(async (request, _) => + { + self!.Requests.Add(request); + self.Bodies.Add(request.Content is null ? "" : await request.Content.ReadAsStringAsync()); + return new HttpResponseMessage(status); + }); + self = handler; + return handler; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + handler(request, cancellationToken); +} diff --git a/tests/Vantage.Core.Tests/Fixtures/CollectorFixtures.cs b/tests/Vantage.Core.Tests/Fixtures/CollectorFixtures.cs new file mode 100644 index 0000000..fa8a9e6 --- /dev/null +++ b/tests/Vantage.Core.Tests/Fixtures/CollectorFixtures.cs @@ -0,0 +1,50 @@ +namespace Vantage.Core.Tests.Fixtures; + +public static class CollectorFixtures +{ + public const string ProcNetDev = + "Inter-| Receive | Transmit\n" + + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" + + " lo: 1234567 8901 0 0 0 0 0 0 1234567 8901 0 0 0 0 0 0\n" + + " eth0: 9000000000 1200000 0 0 0 0 0 0 2000000000 980000 0 0 0 0 0 0\n" + + " wlan0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; + + public const string ProcNetDevLater = + "Inter-| Receive | Transmit\n" + + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" + + " lo: 1234567 8901 0 0 0 0 0 0 1234567 8901 0 0 0 0 0 0\n" + + " eth0: 9000010000 1200100 0 0 0 0 0 0 2000004000 980040 0 0 0 0 0 0\n"; + + public const string PsTopOutput = + " 842 node 12.6 6.3 node server.js\n" + + " 311 root 5.4 2.1 dockerd\n" + + " 1290 postgres 3.1 8.7 postgres\n" + + " 1 root 0.3 0.4 systemd\n" + + " 777 www-data 1.2 0.9 nginx\n"; + + public const string SystemctlOutput = + "docker.service loaded active running Docker Application Container Engine\n" + + "nginx.service loaded active running A high performance web server\n" + + "\u25cf backup.service loaded failed failed Nightly backup job\n" + + "systemd-journald.service loaded active running Journal Service\n" + + "not-a-service.timer loaded active waiting Some timer\n"; + + public const string ThermalZones = + "x86_pkg_temp:52000\n" + + "coretemp Core 0:48000\n" + + "nvme Composite:38900\n" + + "acpitz:41000\n"; + + public const string NvidiaSmiCsv = + "NVIDIA GeForce RTX 3060, 52, 34, 3200, 12288\n"; + + public static string SensorBundle() => + ThermalZones + SshSensorGpuMarker + "\n" + NvidiaSmiCsv; + + public const string SshSensorGpuMarker = "@@vantage:gpu"; + + public const string JournalOutput = + "2026-08-26T14:22:01+0200 vps01 nginx[1234]: GET /api/status 200 12ms\n" + + "2026-08-26T14:22:03+0200 vps01 nginx[1234]: GET /health 200 3ms\n" + + "2026-08-26T14:22:05+0200 vps01 sshd[9911]: Accepted publickey for foksiu\n"; +} diff --git a/tests/Vantage.Core.Tests/Fixtures/MetricsFixtures.cs b/tests/Vantage.Core.Tests/Fixtures/MetricsFixtures.cs new file mode 100644 index 0000000..0cb42a6 --- /dev/null +++ b/tests/Vantage.Core.Tests/Fixtures/MetricsFixtures.cs @@ -0,0 +1,37 @@ +namespace Vantage.Core.Tests.Fixtures; + +public static class MetricsFixtures +{ + public const string Loadavg = "0.50 0.40 0.30 1/234 5678"; + + public const string Uptime = "123456.78 234567.89"; + + public const string Meminfo = + "MemTotal: 8000000 kB\n" + + "MemFree: 1000000 kB\n" + + "MemAvailable: 2000000 kB\n" + + "Buffers: 100000 kB\n" + + "Cached: 500000 kB\n"; + + public const string Df = + "Filesystem 1B-blocks Used Available Use% Mounted on\n" + + "/dev/sda1 100000000000 40000000000 60000000000 40% /\n"; + + public const string Stat1 = + "cpu 100 0 100 800 0 0 0 0 0 0\n" + + "cpu0 50 0 50 400 0 0 0 0 0 0\n" + + "intr 12345\n"; + + public const string Stat2 = + "cpu 400 0 200 900 0 0 0 0 0 0\n" + + "cpu0 200 0 100 450 0 0 0 0 0 0\n" + + "intr 23456\n"; + + public static string FullBundle() => + "@@vantage:loadavg\n" + Loadavg + "\n" + + "@@vantage:uptime\n" + Uptime + "\n" + + "@@vantage:meminfo\n" + Meminfo + + "@@vantage:df\n" + Df + + "@@vantage:stat1\n" + Stat1 + + "@@vantage:stat2\n" + Stat2; +} diff --git a/tests/Vantage.Core.Tests/Fixtures/PrometheusFixtures.cs b/tests/Vantage.Core.Tests/Fixtures/PrometheusFixtures.cs new file mode 100644 index 0000000..26b3a5e --- /dev/null +++ b/tests/Vantage.Core.Tests/Fixtures/PrometheusFixtures.cs @@ -0,0 +1,45 @@ +namespace Vantage.Core.Tests.Fixtures; + +public static class PrometheusFixtures +{ + public const string Mixed = + "# HELP http_requests_total The total number of HTTP requests.\n" + + "# TYPE http_requests_total counter\n" + + "http_requests_total{method=\"post\",code=\"200\"} 1027 1395066363000\n" + + "http_requests_total{method=\"post\",code=\"400\"} 3\n" + + "\n" + + "metric_without_labels 12.47\n" + + "msdos_file_access_time_seconds{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1.2\n" + + "something_weird{problem=\"division by zero\"} +Inf\n" + + "gauge_nan_value NaN\n"; + + public static string NodeExporter( + long cpu0Idle, + long cpu1Idle, + long cpu0User, + long cpu1User) => + "# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.\n" + + "# TYPE node_cpu_seconds_total counter\n" + + $"node_cpu_seconds_total{{cpu=\"0\",mode=\"idle\"}} {cpu0Idle}\n" + + $"node_cpu_seconds_total{{cpu=\"0\",mode=\"user\"}} {cpu0User}\n" + + "node_cpu_seconds_total{cpu=\"0\",mode=\"system\"} 50\n" + + $"node_cpu_seconds_total{{cpu=\"1\",mode=\"idle\"}} {cpu1Idle}\n" + + $"node_cpu_seconds_total{{cpu=\"1\",mode=\"user\"}} {cpu1User}\n" + + "node_cpu_seconds_total{cpu=\"1\",mode=\"system\"} 60\n" + + "# TYPE node_memory_MemTotal_bytes gauge\n" + + "node_memory_MemTotal_bytes 8000000000\n" + + "node_memory_MemAvailable_bytes 2000000000\n" + + "node_filesystem_size_bytes{mountpoint=\"/boot\"} 500000000\n" + + "node_filesystem_avail_bytes{mountpoint=\"/boot\"} 400000000\n" + + "node_filesystem_size_bytes{mountpoint=\"/\"} 100000000000\n" + + "node_filesystem_avail_bytes{mountpoint=\"/\"} 60000000000\n" + + "node_load1 0.5\n" + + "node_load5 0.4\n" + + "node_load15 0.3\n" + + "node_boot_time_seconds 1000000\n" + + "node_time_seconds 1086400\n"; + + public static string NodeScrape1() => NodeExporter(1000, 900, 100, 120); + + public static string NodeScrape2() => NodeExporter(1004, 904, 101, 121); +} diff --git a/tests/Vantage.Core.Tests/Vantage.Core.Tests.csproj b/tests/Vantage.Core.Tests/Vantage.Core.Tests.csproj new file mode 100644 index 0000000..23819a2 --- /dev/null +++ b/tests/Vantage.Core.Tests/Vantage.Core.Tests.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + latest + false + + + + + + + + + + + + +