sqlinsight-pro/tests/SQLInsight.Servers.Tests/LocalCimTests.cs
Søren Eggert Lundsteen Olsen 6eae719e95 Agent: monitor Windows servers and run synthetic checks; server tests and check runs on request
- ServerMonitoringService keeps the SQL Server hosts in step with the monitored
  instances (SQLInsight:Servers:IncludeSqlHosts), picks up servers added, paused or
  changed in SQLInsight Web within a minute, and runs each server's due areas with
  a limit on parallelism. Default intervals: 1 min CPU/memory, 15 min disks and
  tasks, 5 min services and events (SQLInsight:Servers:Intervals).
- ServerMonitor: one CIM session per cycle; per-minute samples, disk samples and
  the "fuld om ca. N dage" forecast from the stored history, grouped events (read
  from where the last read stopped), services and tasks. A missing right makes
  only that area "ikke målbar" with the exact reason, logged once per change; a
  server that does not answer is notified once, and again when it answers.
  Survives an agent restart (samples, boot time, event position).
- SyntheticCheckService runs checks on their interval, opens a SQL login password
  sealed for the agent (AgentCredentialKey.OpenContext), looks up the linked
  Windows service when a check fails, stores results and findings, notifies with
  links to /servere/fund/{id}.
- AgentRequestService hands ServerTest and SyntheticCheckRun to ServerRequestHandler.
- appsettings.json: SQLInsight:Servers and SQLInsight:SyntheticChecks with every
  threshold, interval and retention.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:58:36 +02:00

93 lines
4.1 KiB
C#

using SQLInsight.Core.Servers;
using SQLInsight.Servers;
namespace SQLInsight.Servers.Tests;
/// <summary>
/// The CIM implementation against this computer (a local CIM session, as for the agent's own server). Remote access over
/// WS-Man/DCOM with least privilege cannot be tested here; it needs a domain with a second server.
/// </summary>
[Trait("Category", "Windows")]
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public class LocalCimTests
{
private static async Task<IWindowsServerSession?> ConnectAsync()
{
if (!OperatingSystem.IsWindows())
{
return null;
}
return await new CimWindowsServerSource(new ServerConnectionOptions()).ConnectAsync(Environment.MachineName, CancellationToken.None);
}
[Fact]
public async Task Reads_os_performance_disks_services_events_and_tasks()
{
using var session = await ConnectAsync();
if (session is null)
{
return;
}
var os = await session.GetOperatingSystemAsync(CancellationToken.None);
var first = await session.GetPerformanceAsync(DateTimeOffset.UtcNow, CancellationToken.None);
await Task.Delay(1500);
var second = await session.GetPerformanceAsync(DateTimeOffset.UtcNow, CancellationToken.None);
var sample = ServerPerformanceMath.Sample(first, second, os);
var disks = await session.GetDisksAsync(CancellationToken.None);
var services = await session.GetServicesAsync(CancellationToken.None);
var service = await session.GetServiceAsync(services[0].Name, CancellationToken.None);
var events = await session.GetEventsAsync(DateTimeOffset.UtcNow.AddDays(-7), 200, CancellationToken.None);
var tasks = await session.GetTasksAsync(name => !TaskResults.IsMicrosoftTask(name), 3, CancellationToken.None);
Assert.Equal("lokal", session.Protocol);
Assert.Contains("Windows", os.Caption, StringComparison.OrdinalIgnoreCase);
Assert.True(os.LastBootUpTime < DateTimeOffset.UtcNow);
Assert.True(os.TotalMemoryMb > 0);
Assert.True(os.LogicalProcessors > 0);
Assert.InRange(sample.CpuPercent!.Value, 0, 100);
Assert.True(sample.AvailableMb > 0);
Assert.InRange(sample.CommitPercent!.Value, 0, 100);
Assert.NotEmpty(disks);
Assert.All(disks, d => Assert.False(d.Volume.StartsWith(@"\\?\", StringComparison.Ordinal)));
Assert.All(disks, d => Assert.InRange(d.FreeGb, 0, d.SizeGb));
Assert.Contains(services, s => s.IsRunning && s.IsAutomatic);
Assert.Equal(services[0].Name, service!.Name);
Assert.Null(await session.GetServiceAsync("SQLInsight-findes-ikke", CancellationToken.None));
Assert.Equal("Event Log (RPC)", events.Method);
Assert.All(events.Entries, e => Assert.InRange(e.Level, 1, 2));
Assert.True(tasks.Visible > 0);
Assert.All(tasks.Watched, t => Assert.False(TaskResults.IsMicrosoftTask(t.FullName)));
}
[Fact]
public async Task The_test_reports_every_area()
{
if (!OperatingSystem.IsWindows())
{
return;
}
var result = await WindowsServerTester.TestAsync(new CimWindowsServerSource(new ServerConnectionOptions()), "localhost", "test", DateTimeOffset.UtcNow, CancellationToken.None);
Assert.True(result.Reachable, result.Error);
Assert.Equal(ServerArea.All, result.Areas.Select(a => a.Area));
Assert.All(result.Areas, a => Assert.True(a.Ok, a.Detail));
}
[Fact]
public async Task An_unknown_host_is_unreachable_with_a_reason()
{
if (!OperatingSystem.IsWindows())
{
return;
}
var source = new CimWindowsServerSource(new ServerConnectionOptions { OperationTimeout = TimeSpan.FromSeconds(5) });
var error = await Assert.ThrowsAsync<ServerAccessException>(() => source.ConnectAsync("sqlinsight-findes-ikke.invalid", CancellationToken.None));
Assert.True(error.Problem is ServerAccessProblem.Unreachable or ServerAccessProblem.Timeout, error.Message);
Assert.Contains("sqlinsight-findes-ikke.invalid", error.Message, StringComparison.Ordinal);
}
}