using System.Diagnostics;
using SQLInsight.Core.Servers;
namespace SQLInsight.Servers.Tests;
/// What the agent remembers per server, and that a server that does not answer never holds the agent longer than its deadline.
public class ServerDeadlineTests
{
private sealed class Clock(DateTimeOffset now) : TimeProvider
{
public DateTimeOffset Now { get; set; } = now;
public override DateTimeOffset GetUtcNow() => Now;
}
[Fact]
public void A_fact_about_a_server_is_remembered_for_a_while_then_tried_again()
{
var clock = new Clock(new DateTimeOffset(2026, 10, 2, 12, 0, 0, TimeSpan.Zero));
var memory = new ServerHostMemory(clock, TimeSpan.FromHours(1));
memory.Remember("aos01.firma.local", "protocol", "DCOM");
clock.Now = clock.Now.AddMinutes(59);
Assert.Equal("DCOM", memory.Recall("AOS01.firma.local", "protocol"));
Assert.Null(memory.Recall("aos01.firma.local", "eventlog-rpc"));
clock.Now = clock.Now.AddMinutes(1);
Assert.Null(memory.Recall("aos01.firma.local", "protocol"));
memory.Remember("aos01", "protocol", "DCOM");
memory.Forget("aos01", "protocol");
Assert.Null(memory.Recall("aos01", "protocol"));
}
[Fact]
public void Operation_timeouts_are_bounded()
{
Assert.Equal(TimeSpan.FromSeconds(5), ServerConnectionOptions.BoundTimeout(TimeSpan.Zero));
Assert.Equal(TimeSpan.FromSeconds(5), ServerConnectionOptions.BoundTimeout(TimeSpan.FromSeconds(-1)));
Assert.Equal(TimeSpan.FromMinutes(5), ServerConnectionOptions.BoundTimeout(TimeSpan.FromHours(3)));
Assert.Equal(TimeSpan.FromSeconds(45), ServerConnectionOptions.BoundTimeout(TimeSpan.FromSeconds(45)));
}
/// A server that accepts the connection, then never answers any area (it honours cancellation, as the CIM source makes sure it is).
private sealed class HungServer(bool hangOnConnect = false) : IWindowsServerSource, IWindowsServerSession
{
public string Protocol => "DCOM";
public string? ProtocolNote => null;
public async Task ConnectAsync(string host, CancellationToken cancellationToken)
{
if (hangOnConnect)
{
await Hang(cancellationToken);
}
return this;
}
private static Task Hang(CancellationToken cancellationToken) => Task.Delay(Timeout.Infinite, cancellationToken);
public async Task GetOperatingSystemAsync(CancellationToken cancellationToken)
{
await Hang(cancellationToken);
throw new UnreachableException();
}
public Task GetPerformanceAsync(DateTimeOffset now, CancellationToken cancellationToken) => throw new UnreachableException();
public Task> GetDisksAsync(CancellationToken cancellationToken) =>
Task.FromResult>([new ServerDisk(@"C:\", null, "NTFS", 100, 50)]);
public async Task> GetServicesAsync(CancellationToken cancellationToken)
{
await Hang(cancellationToken);
throw new UnreachableException();
}
public Task GetServiceAsync(string name, CancellationToken cancellationToken) => throw new UnreachableException();
public async Task GetEventsAsync(IReadOnlyDictionary since, int max, CancellationToken cancellationToken)
{
await Hang(cancellationToken);
throw new UnreachableException();
}
public async Task GetTasksAsync(Func watch, int maxWatched, TimeSpan budget, CancellationToken cancellationToken)
{
await Hang(cancellationToken);
throw new UnreachableException();
}
public void Dispose()
{
}
}
[Fact]
public async Task A_test_of_a_server_that_hangs_ends_at_its_deadline_with_every_area_said()
{
var watch = Stopwatch.StartNew();
var result = await WindowsServerTester.TestAsync(new HungServer(), "aos01", null, DateTimeOffset.UtcNow, TimeSpan.FromMilliseconds(300), CancellationToken.None);
var hungAtConnect = await WindowsServerTester.TestAsync(new HungServer(hangOnConnect: true), "aos01", null, DateTimeOffset.UtcNow, TimeSpan.FromMilliseconds(300),
CancellationToken.None);
Assert.True(watch.Elapsed < TimeSpan.FromSeconds(30), $"took {watch.Elapsed}");
Assert.True(result.Reachable);
Assert.Equal(ServerArea.All, result.Areas.Select(a => a.Area));
Assert.All(result.Areas, a => Assert.False(a.Ok));
Assert.All(result.Areas, a => Assert.Contains("svarede ikke inden for", a.Detail, StringComparison.Ordinal));
Assert.False(hungAtConnect.Reachable);
Assert.Contains("svarede ikke inden for", hungAtConnect.Error, StringComparison.Ordinal);
}
[Fact]
public async Task Stopping_the_agent_still_cancels_a_test()
{
using var stop = new CancellationTokenSource(TimeSpan.FromMilliseconds(200));
await Assert.ThrowsAnyAsync(() =>
WindowsServerTester.TestAsync(new HungServer(hangOnConnect: true), "aos01", null, DateTimeOffset.UtcNow, TimeSpan.FromMinutes(5), stop.Token));
}
}