using ArchestrA; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests.Backend; /// /// Driver.Historian.Wonderware-008 regression. The previous implementation unconditionally /// called HandleConnectionError() whenever StartQuery returned false, /// which tore down the (relatively expensive) shared SDK connection on a query-class error /// such as a bad tag name. A burst of bad-tag queries could therefore push an otherwise /// healthy cluster node into cooldown via the picker's MarkFailed. The fix /// classifies the SDK error code: connection-class codes drop the connection; query-class /// codes leave it intact. /// [Trait("Category", "Unit")] public sealed class HistorianDataSourceStartQueryClassificationTests { // ── Connection-class codes — the connection should be reset ─────────── [Theory] [InlineData(HistorianAccessError.ErrorValue.FailedToConnect)] [InlineData(HistorianAccessError.ErrorValue.FailedToCreateSession)] [InlineData(HistorianAccessError.ErrorValue.NoReply)] [InlineData(HistorianAccessError.ErrorValue.NotReady)] [InlineData(HistorianAccessError.ErrorValue.NotInitialized)] [InlineData(HistorianAccessError.ErrorValue.Stopping)] [InlineData(HistorianAccessError.ErrorValue.Win32Exception)] [InlineData(HistorianAccessError.ErrorValue.InvalidResponse)] public void Connection_class_codes_are_classified_as_connection_errors(HistorianAccessError.ErrorValue code) { HistorianDataSource.IsConnectionClassError(code).ShouldBeTrue( $"{code} is a connection/server failure — the SDK connection should be reset"); } // ── Query-class codes — the connection should NOT be reset ──────────── [Theory] [InlineData(HistorianAccessError.ErrorValue.InvalidArgument)] // bad tag name, etc. [InlineData(HistorianAccessError.ErrorValue.ValidationFailed)] // bad query args [InlineData(HistorianAccessError.ErrorValue.NotApplicable)] // wrong tag kind for query [InlineData(HistorianAccessError.ErrorValue.NotImplemented)] // unsupported aggregate [InlineData(HistorianAccessError.ErrorValue.NoData)] // empty range public void Query_class_codes_are_NOT_classified_as_connection_errors(HistorianAccessError.ErrorValue code) { HistorianDataSource.IsConnectionClassError(code).ShouldBeFalse( $"{code} is a query payload problem — must NOT tear down the SDK connection"); } }