using Microsoft.Extensions.Logging.Abstractions; using Xunit; using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests; public sealed class GatewayHealthSnapshotTests { [Fact] public async Task Counters_track_success_and_failure() { var fake = new FakeHistorianGatewayClient { RawSamples = Array.Empty() }; var ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); await ds.ReadRawAsync("T", default, default, 1, TestContext.Current.CancellationToken); fake.ThrowOnRead = new InvalidOperationException("boom"); await Assert.ThrowsAnyAsync(() => ds.ReadRawAsync("T", default, default, 1, TestContext.Current.CancellationToken)); var h = ds.GetHealthSnapshot(); Assert.Equal(2, h.TotalQueries); Assert.Equal(1, h.TotalSuccesses); Assert.Equal(1, h.TotalFailures); Assert.Equal(1, h.ConsecutiveFailures); Assert.Equal(h.TotalQueries, h.TotalSuccesses + h.TotalFailures); // invariant } [Fact] public async Task Connection_state_reflects_GetConnectionStatus_flags() { var fake = new FakeHistorianGatewayClient { ConnectionStatus = new ConnectionStatus { ConnectedToServer = true, ConnectionKind = 0b11 }, // Process|Event }; var ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); await ds.RefreshConnectionStateAsync(TestContext.Current.CancellationToken); // internal probe used by health hosted-service var h = ds.GetHealthSnapshot(); Assert.True(h.ProcessConnectionOpen); Assert.True(h.EventConnectionOpen); } [Fact] public async Task GetHealthSnapshot_does_no_io_and_starts_with_connections_closed() { var fake = new FakeHistorianGatewayClient(); var ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); var h = ds.GetHealthSnapshot(); Assert.Equal(0, fake.GetConnectionStatusCallCount); // pure observation — never queries the gateway Assert.False(h.ProcessConnectionOpen); Assert.False(h.EventConnectionOpen); await ValueTask.CompletedTask; } [Fact] public async Task Disconnected_status_leaves_both_flags_closed() { var fake = new FakeHistorianGatewayClient { ConnectionStatus = new ConnectionStatus { ConnectedToServer = false, ConnectionKind = 0b11 }, }; var ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); await ds.RefreshConnectionStateAsync(TestContext.Current.CancellationToken); var h = ds.GetHealthSnapshot(); Assert.False(h.ProcessConnectionOpen); Assert.False(h.EventConnectionOpen); } [Fact] public async Task Failed_status_query_degrades_flags_without_throwing() { var fake = new FakeHistorianGatewayClient { ConnectionStatus = new ConnectionStatus { ConnectedToServer = true, ConnectionKind = 0b11 }, GetConnectionStatusThrows = new InvalidOperationException("gateway unreachable"), }; var ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); await ds.RefreshConnectionStateAsync(TestContext.Current.CancellationToken); // must not throw var h = ds.GetHealthSnapshot(); Assert.False(h.ProcessConnectionOpen); Assert.False(h.EventConnectionOpen); } }