using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Recorder; using ZB.MOM.WW.OtOpcUa.Runtime.Historian; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests; /// /// Continuous historization on the Gateway backend silently loses what the SQL backends keep — every sample's /// quality (the gateway stores Good), comms-loss outage markers and non-numeric values — so building the live writer /// (which only happens when continuous historization is enabled) logs one startup Warning naming the gaps. /// public sealed class GatewayContinuousHistorizationWarningTests { private sealed class CapturingProvider : ILoggerProvider { public readonly List<(LogLevel Level, string Message)> Entries = new(); public ILogger CreateLogger(string categoryName) => new L(this); public void Dispose() { } private sealed class L(CapturingProvider owner) : ILogger { public IDisposable? BeginScope(TState state) where TState : notnull => null; public bool IsEnabled(LogLevel logLevel) => true; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { lock (owner.Entries) owner.Entries.Add((logLevel, formatter(state, exception))); } } } [Fact] public async Task Building_the_gateway_live_writer_warns_once_about_what_the_backend_drops() { var capture = new CapturingProvider(); using var sp = new ServiceCollection().AddLogging(b => b.AddProvider(capture)).BuildServiceProvider(); var provider = new GatewayHistorianProvider( new ServerHistorianOptions { Endpoint = "http://localhost:5222", UseTls = false, ApiKey = "histgw_test_secret" }); var writer = Assert.IsType(provider.CreateValueWriter(sp)); var warning = capture.Entries.Where(e => e.Level == LogLevel.Warning).ShouldHaveSingleItem().Message; warning.ShouldContain("quality"); warning.ShouldContain("Good"); warning.ShouldContain("outage"); warning.ShouldContain("non-numeric"); warning.ShouldContain("SqlServer"); await writer.DisposeAsync(); // Positive control in the other direction: the read path alone (no continuous historization) does not warn. capture.Entries.Clear(); await using var ds = (GatewayHistorianDataSource)provider.CreateDataSource(sp); capture.Entries.ShouldNotContain(e => e.Level == LogLevel.Warning); } }