test(app): add overview and alert wiring integration tests

This commit is contained in:
dualfroz committed 2026-08-28 21:01:39 +02:00
1 parent 36fbba0250
commit da34e87e56
1 file changed
+132
@@ -0,0 +1,132 @@
using System.Collections.ObjectModel;
using Vantage.App.ViewModels;
using Vantage.Core.Alerts;
using Vantage.Core.Configuration;
using Vantage.Core.Docker;
using Vantage.Core.Health;
using Vantage.Core.History;
using Vantage.Core.Metrics;
using Vantage.Core.Models;
using Xunit;
namespace Vantage.App.Tests;
public class OverviewAndAlertWiringTests
{
private sealed class FixedMetricsProvider : IMetricsProvider
{
private readonly double cpu;
public FixedMetricsProvider(double cpu)
{
this.cpu = cpu;
}
public Task<HostMetrics> GetMetricsAsync(HostConfig host, CancellationToken cancellationToken) =>
Task.FromResult(new HostMetrics
{
CpuUsagePercent = cpu,
MemoryUsedBytes = 90,
MemoryTotalBytes = 100,
DiskUsedBytes = 10,
DiskTotalBytes = 100,
Load1 = 1,
CollectedAt = new DateTimeOffset(2026, 8, 26, 12, 0, 0, TimeSpan.Zero)
});
}
private static async Task<IConfigStore> SeededStore(AlertRule rule)
{
var store = new JsonConfigStore(
Path.Combine(Path.GetTempPath(), "vantage-wire-" + Guid.NewGuid().ToString("N"), "config.json"));
await store.SaveAsync(new VantageConfig
{
Hosts = { new HostConfig { Id = "h1", Name = "vps01" } },
AlertRules = { rule }
});
return store;
}
[Fact]
public void Overview_AggregatesOnlineOfflineAndPeaks()
{
var hosts = new ObservableCollection<HostViewModel>
{
new(new HostConfig { Name = "a" }, 10),
new(new HostConfig { Name = "b" }, 10),
new(new HostConfig { Name = "c" }, 10)
};
hosts[0].ApplyMetrics(new HostMetrics { CpuUsagePercent = 20, MemoryTotalBytes = 100, MemoryUsedBytes = 30 });
hosts[1].ApplyMetrics(new HostMetrics { CpuUsagePercent = 80, MemoryTotalBytes = 100, MemoryUsedBytes = 60 });
hosts[2].ApplyError("down");
var overview = new OverviewViewModel(hosts);
overview.Refresh();
Assert.Equal(3, overview.TotalHosts);
Assert.Equal(2, overview.OnlineHosts);
Assert.Equal(1, overview.OfflineHosts);
Assert.Equal(50, overview.AverageCpuPercent, 1);
Assert.Equal(80, overview.PeakCpuPercent, 1);
Assert.Equal("b", overview.PeakCpuHost);
}
[Fact]
public async Task Refresh_RecordsHistoryAndFiresAlerts()
{
await using var store = SqliteMetricsStore.InMemory();
var sink = new CollectingSink();
var rule = new AlertRule { Name = "CPU", Metric = "cpu", Comparison = AlertComparison.GreaterOrEqual, Threshold = 80, For = TimeSpan.Zero };
var engine = new AlertEngine(Array.Empty<AlertRule>(), new IAlertSink[] { sink });
var viewModel = new MainWindowViewModel(
new FixedMetricsProvider(95),
new MockDockerProvider(),
new HealthChecker(new HttpClient()),
await SeededStore(rule),
store,
engine);
await viewModel.InitializeAsync();
await viewModel.RefreshSelectedHostAsync(CancellationToken.None);
Assert.True(await store.CountAsync() >= 6);
Assert.Contains(sink.Received, n => n.Kind == AlertTransitionKind.Fired);
Assert.Equal(1, viewModel.FiringAlertCount);
Assert.NotEmpty(viewModel.Alerts);
}
[Fact]
public async Task Refresh_BelowThreshold_DoesNotFire()
{
await using var store = SqliteMetricsStore.InMemory();
var sink = new CollectingSink();
var rule = new AlertRule { Name = "CPU", Metric = "cpu", Comparison = AlertComparison.GreaterOrEqual, Threshold = 80, For = TimeSpan.Zero };
var engine = new AlertEngine(Array.Empty<AlertRule>(), new IAlertSink[] { sink });
var viewModel = new MainWindowViewModel(
new FixedMetricsProvider(15),
new MockDockerProvider(),
new HealthChecker(new HttpClient()),
await SeededStore(rule),
store,
engine);
await viewModel.InitializeAsync();
await viewModel.RefreshSelectedHostAsync(CancellationToken.None);
Assert.Empty(sink.Received);
Assert.Equal(0, viewModel.FiringAlertCount);
}
private sealed class CollectingSink : IAlertSink
{
public List<AlertNotification> Received { get; } = new();
public Task NotifyAsync(AlertNotification notification, CancellationToken cancellationToken = default)
{
Received.Add(notification);
return Task.CompletedTask;
}
}
}