feat(historian-gateway): GetHealthSnapshot via Probe/GetConnectionStatus (counter discipline)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
||||
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<HistorianSample>() };
|
||||
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
||||
await ds.ReadRawAsync("T", default, default, 1, TestContext.Current.CancellationToken);
|
||||
fake.ThrowOnRead = new InvalidOperationException("boom");
|
||||
await Assert.ThrowsAnyAsync<Exception>(() => 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<GatewayHistorianDataSource>.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<GatewayHistorianDataSource>.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<GatewayHistorianDataSource>.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<GatewayHistorianDataSource>.Instance);
|
||||
await ds.RefreshConnectionStateAsync(TestContext.Current.CancellationToken); // must not throw
|
||||
var h = ds.GetHealthSnapshot();
|
||||
Assert.False(h.ProcessConnectionOpen);
|
||||
Assert.False(h.EventConnectionOpen);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user