test(core): add test scaffolding, fakes, and fixtures
This commit is contained in:
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>
|
||||
Reference in new issue
Block a user