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)); } }