using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.ParityTests; /// /// PR 5.6 — History-read parity. Phase-1 routing lifted history off the /// per-driver path onto the server-owned /// HistoryRouter + WonderwareHistorianBootstrap; neither /// Galaxy backend implements directly. So /// the parity surface here is the *routing decision*: both backends must /// identify the same set of historized attributes and produce the same /// full-reference for each, so HistoryRouter routes reads identically. /// [Trait("Category", "ParityE2E")] [Collection(nameof(ParityCollection))] public sealed class HistoryReadParityTests { private readonly ParityHarness _h; public HistoryReadParityTests(ParityHarness h) => _h = h; [Fact] public async Task Discover_emits_same_historized_attribute_set_for_both_backends() { _h.RequireBoth(); var snapshots = await _h.RunOnAvailableAsync(async (driver, ct) => { var b = new RecordingAddressSpaceBuilder(); await ((ITagDiscovery)driver).DiscoverAsync(b, ct); return b.Variables .Where(v => v.AttributeInfo.IsHistorized) .Select(v => v.AttributeInfo.FullName) .ToHashSet(StringComparer.OrdinalIgnoreCase); }, CancellationToken.None); var legacy = snapshots[ParityHarness.Backend.LegacyHost]; var mxgw = snapshots[ParityHarness.Backend.MxGateway]; if (legacy.Count == 0) { Assert.Skip("dev Galaxy has no historized attributes — history routing parity unverified for this rig"); } legacy.Except(mxgw, StringComparer.OrdinalIgnoreCase).ShouldBeEmpty( "every historized attribute discovered by the legacy backend must appear in the mxgw backend"); mxgw.Except(legacy, StringComparer.OrdinalIgnoreCase).ShouldBeEmpty( "every historized attribute discovered by the mxgw backend must appear in the legacy backend"); } [Fact] public async Task Neither_Galaxy_backend_implements_IHistoryProvider_directly() { // Pinning the architectural decision from Phase 1 (PR 1.3): per-driver // IHistoryProvider was retired in favor of the server-owned HistoryRouter. // If a regression brings IHistoryProvider back on either Galaxy driver, // this test fires. _h.RequireBoth(); (_h.LegacyDriver as IHistoryProvider).ShouldBeNull( "legacy GalaxyProxyDriver must not surface IHistoryProvider — history routes through HistoryRouter"); (_h.MxGatewayDriver as IHistoryProvider).ShouldBeNull( "in-process GalaxyDriver must not surface IHistoryProvider — history routes through HistoryRouter"); } }