PR 5.1 — Driver.Galaxy.ParityTests project shell + ParityHarness

Side-by-side fixture that boots both backends against the same dev Galaxy:

- Legacy GalaxyProxyDriver against an out-of-process Galaxy.Host EXE
  (skipped when ZB SQL on localhost:1433 isn't reachable or when the EXE
  hasn't been built).
- New in-process GalaxyDriver against an mxaccessgw gateway at
  http://localhost:5120 by default (skipped when the gateway isn't
  reachable). Endpoint, API key, and client name are env-var overridable
  for the central parity host.

Per-backend availability is independent — each scenario decides whether
to RequireBoth, GetDriver(specific), or use RunOnAvailableAsync to drive
both with the same closure and diff snapshots. PR 5.2–5.8 land scenarios
on top of this shell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-04-29 16:22:04 -04:00
parent 21cac4c8c4
commit 82cdf460c5
5 changed files with 420 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.ParityTests;
/// <summary>
/// Shape tests for the <see cref="ParityHarness"/> itself — these run regardless of
/// dev-environment availability. The scenario tests in PR 5.25.8 carry the actual
/// parity assertions and are guarded by <see cref="ParityHarness.RequireBoth"/>.
/// </summary>
[Collection(nameof(ParityCollection))]
public sealed class HarnessShapeTests
{
private readonly ParityHarness _h;
public HarnessShapeTests(ParityHarness h) => _h = h;
[Fact]
public void Harness_records_a_skip_reason_for_each_unavailable_backend()
{
// Either the backend resolved (driver != null, skipReason == null) or it didn't
// (driver == null, skipReason populated). Asserting the invariant lets the parity
// matrix doc (PR 5.W) faithfully report "n/a, reason: ..." for unreachable rigs.
(_h.LegacyDriver is null).ShouldBe(_h.LegacySkipReason is not null);
(_h.MxGatewayDriver is null).ShouldBe(_h.MxGatewaySkipReason is not null);
}
[Fact]
public async Task RunOnAvailableAsync_yields_one_entry_per_resolved_backend()
{
var calls = await _h.RunOnAvailableAsync(
(_, _) => Task.FromResult(1), CancellationToken.None);
var expected = (_h.LegacyDriver is null ? 0 : 1) + (_h.MxGatewayDriver is null ? 0 : 1);
calls.Count.ShouldBe(expected);
}
}