test(core): add test scaffolding, fakes, and fixtures

This commit is contained in:
dualfroz committed 2026-08-27 12:10:53 +02:00
1 parent 90d047c29a
commit f95edd06e0
6 files changed
+239

No files matched your search

@@ -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<string, string> respond;
public FakeSshCommandRunner(string fixedResponse)
: this(_ => fixedResponse)
{
}
public FakeSshCommandRunner(Func<string, string> respond)
{
this.respond = respond;
}
public List<string> Commands { get; } = new();
public Task<string> RunAsync(HostConfig host, string command, CancellationToken cancellationToken)
{
Commands.Add(command);
return Task.FromResult(respond(command));
}
}
@@ -0,0 +1,59 @@
using System.Net;
namespace Vantage.Core.Tests.Fakes;
public sealed class StubHttpMessageHandler : HttpMessageHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler;
private StubHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> 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<HttpResponseMessage>(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<HttpRequestMessage> Requests { get; } = new();
public List<string> 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<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) =>
handler(request, cancellationToken);
}
@@ -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";
}
@@ -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;
}
@@ -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);
}
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Vantage.Core\Vantage.Core.csproj" />
</ItemGroup>
</Project>