using Microsoft.Extensions.Logging.Abstractions; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests; /// /// Gitea #686: the gateway's ReadRaw streams ascending only and rejects start > end with /// InvalidArgument, which used to be recorded as a backend health failure. The source declares it cannot read /// newest-first (so the server walks back with forward reads), and refuses a reverse window itself without sending /// anything or counting a failure. /// public sealed class GatewayRawDirectionTests { private static CancellationToken Ct => TestContext.Current.CancellationToken; [Fact] public void The_gateway_source_declares_it_cannot_read_raw_newest_first() { IHistorianDataSource ds = new GatewayHistorianDataSource( new FakeHistorianGatewayClient(), NullLogger.Instance); ds.SupportsReverseRaw.ShouldBeFalse(); } [Fact] public async Task A_reverse_raw_window_is_refused_unsent_and_is_not_a_backend_failure() { var fake = new FakeHistorianGatewayClient(); IHistorianDataSource ds = new GatewayHistorianDataSource(fake, NullLogger.Instance); var end = new DateTime(2026, 10, 12, 18, 7, 45, DateTimeKind.Utc); await Should.ThrowAsync(() => ds.ReadRawAsync("Gate681", end, new DateTime(0, DateTimeKind.Utc), 3, Ct)); fake.ReadRawCallCount.ShouldBe(0); var health = ds.GetHealthSnapshot(); health.TotalFailures.ShouldBe(0); health.ConsecutiveFailures.ShouldBe(0); // Positive control: a forward read of the same window is sent. await ds.ReadRawAsync("Gate681", new DateTime(2026, 10, 12, 18, 0, 0, DateTimeKind.Utc), end, 3, Ct); fake.ReadRawCallCount.ShouldBe(1); } }