using Xunit; using ZB.MOM.WW.OtOpcUa.Runtime.Historian; namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Historian; /// /// Covers the gateway-shaped warnings: a disabled or /// correctly-configured historian is silent; an enabled one with a blank Endpoint or blank /// ApiKey surfaces an operator-facing warning (carrying no secret value text). /// public sealed class ServerHistorianOptionsTests { [Fact] public void Disabled_yields_no_warnings() => Assert.Empty(new ServerHistorianOptions { Enabled = false }.Validate()); [Fact] public void Enabled_without_endpoint_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "", ApiKey = "histgw_x_y" }.Validate(); Assert.Contains(w, m => m.Contains("Endpoint")); } [Fact] public void Enabled_with_malformed_endpoint_warns() { // archreview 06/S-11 warning parity: a non-empty but unparseable / non-absolute Endpoint warns // too, so the Runtime-side registration logs describe malformed endpoints (not just empty ones), // keeping this surface consistent with the Host's fail-fast ServerHistorianOptionsValidator. var w = new ServerHistorianOptions { Enabled = true, Endpoint = "not a uri", ApiKey = "histgw_x_y" }.Validate(); Assert.Contains(w, m => m.Contains("Endpoint")); } [Fact] public void Enabled_without_apikey_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "" }.Validate(); Assert.Contains(w, m => m.Contains("ApiKey")); } [Fact] public void Valid_config_is_clean() => Assert.Empty(new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "histgw_x_y" }.Validate()); [Fact] public void Enabled_with_nonpositive_MaxTieClusterOverfetch_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "histgw_x_y", MaxTieClusterOverfetch = 0 } .Validate(); Assert.Contains(w, m => m.Contains("MaxTieClusterOverfetch")); } // ── S3 HistoryRead concurrency knobs (arch-review 03/S3) ──────────────────────────────────── [Fact] public void HistoryRead_knob_defaults() { var o = new ServerHistorianOptions(); Assert.Equal(4, o.HistoryReadBatchParallelism); Assert.Equal(16, o.MaxConcurrentHistoryReadBatches); Assert.Equal(TimeSpan.FromSeconds(60), o.HistoryReadDeadline); } [Fact] public void Enabled_with_nonpositive_HistoryReadBatchParallelism_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "histgw_x_y", HistoryReadBatchParallelism = 0 } .Validate(); Assert.Contains(w, m => m.Contains("HistoryReadBatchParallelism")); } [Fact] public void Enabled_with_nonpositive_MaxConcurrentHistoryReadBatches_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "histgw_x_y", MaxConcurrentHistoryReadBatches = 0 } .Validate(); Assert.Contains(w, m => m.Contains("MaxConcurrentHistoryReadBatches")); } [Fact] public void Enabled_with_nonpositive_HistoryReadDeadline_warns() { var w = new ServerHistorianOptions { Enabled = true, Endpoint = "https://h:5222", ApiKey = "histgw_x_y", HistoryReadDeadline = TimeSpan.Zero } .Validate(); Assert.Contains(w, m => m.Contains("HistoryReadDeadline")); } [Fact] public void Disabled_with_nonpositive_HistoryRead_knobs_is_silent() { var w = new ServerHistorianOptions { Enabled = false, HistoryReadBatchParallelism = 0, MaxConcurrentHistoryReadBatches = 0, HistoryReadDeadline = TimeSpan.Zero, }.Validate(); Assert.Empty(w); } }