using MxGateway.Server.Galaxy; namespace MxGateway.Tests.Galaxy; public sealed class GalaxyHierarchyCacheTests { [Fact] public void Current_BeforeAnyRefresh_ReturnsEmpty() { GalaxyDeployNotifier notifier = new(); GalaxyHierarchyCache cache = CreateCache(notifier, new ManualTimeProvider()); GalaxyHierarchyCacheEntry entry = cache.Current; Assert.Equal(GalaxyCacheStatus.Unknown, entry.Status); Assert.False(entry.HasData); Assert.Equal(0, entry.ObjectCount); Assert.Empty(entry.Objects); } [Fact] public async Task RefreshAsync_WhenSqlIsUnreachable_MarksUnavailableAndDoesNotPublish() { GalaxyDeployNotifier notifier = new(); ManualTimeProvider clock = new(DateTimeOffset.Parse("2026-04-28T12:00:00Z")); GalaxyHierarchyCache cache = CreateCache(notifier, clock); await cache.RefreshAsync(CancellationToken.None); Assert.Equal(GalaxyCacheStatus.Unavailable, cache.Current.Status); Assert.False(string.IsNullOrWhiteSpace(cache.Current.LastError)); Assert.Null(notifier.Latest); Assert.True(cache.WaitForFirstLoadAsync(CancellationToken.None).IsCompletedSuccessfully); } [Fact] public void HasData_OnHealthyEntry_IsTrue() { GalaxyHierarchyCacheEntry entry = GalaxyHierarchyCacheEntry.Empty with { Status = GalaxyCacheStatus.Healthy, LastSuccessAt = DateTimeOffset.UtcNow, ObjectCount = 1, }; Assert.True(entry.HasData); } [Fact] public void HasData_OnUnknownEntry_IsFalse() { Assert.False(GalaxyHierarchyCacheEntry.Empty.HasData); } private static GalaxyHierarchyCache CreateCache(GalaxyDeployNotifier notifier, TimeProvider clock) { GalaxyRepositoryOptions options = new() { ConnectionString = "Server=127.0.0.1,65500;Database=ZB;Connection Timeout=1;Encrypt=False;", CommandTimeoutSeconds = 1, }; GalaxyRepository repository = new(options); return new GalaxyHierarchyCache(repository, notifier, clock); } private sealed class ManualTimeProvider(DateTimeOffset start = default) : TimeProvider { private DateTimeOffset _now = start == default ? DateTimeOffset.UtcNow : start; public override DateTimeOffset GetUtcNow() => _now; public void Advance(TimeSpan duration) => _now += duration; } }