PR 4.W — Galaxy:Backend wiring + server-side factory registration
- GalaxyDriver.InitializeAsync now builds the production gw runtime (MxGatewayClient, GalaxyMxSession, GatewayGalaxySubscriber, GatewayGalaxyDataWriter, ReconnectSupervisor, HostConnectivityForwarder, PerPlatformProbeWatcher) when no test seams are pre-injected; Dispose tears the chain down in order. - GetHealth surfaces supervisor.IsDegraded as DriverState.Degraded so a transport drop is observable without polling the supervisor directly. - DiscoverAsync now refreshes the per-platform probe watcher's membership against $WinPlatform / $AppEngine objects after every discovery pass. - OnPumpDataChange routes ScanState changes through the probe watcher in addition to fanning out OnDataChange to ISubscribable consumers. - Server registers GalaxyDriver under "GalaxyMxGateway" alongside the legacy "Galaxy" GalaxyProxyDriver factory so DriverInstance rows can opt in. - Bumped Server.Tests' Microsoft.Extensions.Logging.Abstractions to 10.0.7 to resolve the downgrade pulled in transitively via MxGateway.Client. - Lifecycle factory tests switched to the internal seam-injection ctor so they no longer attempt a real gRPC connect during InitializeAsync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using MxGateway.Contracts.Proto;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests;
|
||||
|
||||
@@ -118,7 +121,12 @@ public sealed class GalaxyDriverFactoryTests
|
||||
[Fact]
|
||||
public async Task DriverLifecycle_InitializeShutdown_ToggleHealth()
|
||||
{
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("galaxy-x", MinimalConfig);
|
||||
// Inject a no-op subscriber seam so InitializeAsync skips BuildProductionRuntimeAsync
|
||||
// (no gw connect attempt). The factory tests only care about the lifecycle health
|
||||
// toggle; real-runtime wire-up is exercised in PR 4.W's BuildProductionRuntime tests.
|
||||
using var driver = new GalaxyDriver(
|
||||
"galaxy-x", BuildOptions(), hierarchySource: null, dataReader: null,
|
||||
dataWriter: null, subscriber: new NoopSubscriber());
|
||||
driver.GetHealth().State.ShouldBe(DriverState.Unknown);
|
||||
|
||||
await driver.InitializeAsync(MinimalConfig, CancellationToken.None);
|
||||
@@ -135,7 +143,9 @@ public sealed class GalaxyDriverFactoryTests
|
||||
[Fact]
|
||||
public async Task ReinitializeAsync_RefreshesHealth()
|
||||
{
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("galaxy-x", MinimalConfig);
|
||||
using var driver = new GalaxyDriver(
|
||||
"galaxy-x", BuildOptions(), hierarchySource: null, dataReader: null,
|
||||
dataWriter: null, subscriber: new NoopSubscriber());
|
||||
await driver.InitializeAsync(MinimalConfig, CancellationToken.None);
|
||||
var firstStamp = driver.GetHealth().LastSuccessfulRead!.Value;
|
||||
|
||||
@@ -147,6 +157,34 @@ public sealed class GalaxyDriverFactoryTests
|
||||
driver.GetHealth().LastSuccessfulRead!.Value.ShouldBeGreaterThan(firstStamp);
|
||||
}
|
||||
|
||||
private static GalaxyDriverOptions BuildOptions() => new(
|
||||
new GalaxyGatewayOptions("https://mxgw.test:5001", "key"),
|
||||
new GalaxyMxAccessOptions("OtOpcUa-A"),
|
||||
new GalaxyRepositoryOptions(),
|
||||
new GalaxyReconnectOptions());
|
||||
|
||||
/// <summary>
|
||||
/// Minimum-surface <see cref="IGalaxySubscriber"/> seam — enough to satisfy
|
||||
/// <see cref="GalaxyDriver.InitializeAsync"/>'s "skip production-runtime build" branch
|
||||
/// without driving any actual subscribe/event-pump traffic.
|
||||
/// </summary>
|
||||
private sealed class NoopSubscriber : IGalaxySubscriber
|
||||
{
|
||||
public Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
|
||||
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken)
|
||||
=> Task.FromResult<IReadOnlyList<SubscribeResult>>([]);
|
||||
|
||||
public Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public async IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_IsIdempotent_AndShutdownAfterDisposeIsHarmless()
|
||||
{
|
||||
@@ -163,4 +201,42 @@ public sealed class GalaxyDriverFactoryTests
|
||||
await Should.ThrowAsync<ObjectDisposedException>(() =>
|
||||
driver.InitializeAsync(MinimalConfig, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DriverImplementsAllPhase4Capabilities()
|
||||
{
|
||||
// PR 4.W contract pin — every capability the in-process Galaxy driver must
|
||||
// expose for parity with GalaxyProxyDriver. If any of these regress, the
|
||||
// Galaxy:Backend flag flip in PR 7.1 will silently lose surface.
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("g", MinimalConfig);
|
||||
driver.ShouldBeAssignableTo<ITagDiscovery>();
|
||||
driver.ShouldBeAssignableTo<IReadable>();
|
||||
driver.ShouldBeAssignableTo<IWritable>();
|
||||
driver.ShouldBeAssignableTo<ISubscribable>();
|
||||
driver.ShouldBeAssignableTo<IRediscoverable>();
|
||||
driver.ShouldBeAssignableTo<IHostConnectivityProbe>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetHostStatuses_AfterInitWithSeam_ReturnsEmptySnapshot()
|
||||
{
|
||||
// PR 4.W wire-up assertion: when InitializeAsync skips the production-runtime
|
||||
// build (seam injected), no transport-forwarder or probe-watcher pushes a
|
||||
// status, so the aggregator snapshot is empty. The forwarder + watcher have
|
||||
// their own unit tests in PR 4.7.
|
||||
using var driver = new GalaxyDriver(
|
||||
"g", BuildOptions(), hierarchySource: null, dataReader: null,
|
||||
dataWriter: null, subscriber: new NoopSubscriber());
|
||||
await driver.InitializeAsync(MinimalConfig, CancellationToken.None);
|
||||
driver.GetHostStatuses().ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DriverType_IsGalaxyMxGateway_NotLegacyGalaxy()
|
||||
{
|
||||
// Must match GalaxyProxyDriver's DriverType ("Galaxy") side-by-side without
|
||||
// collision so DriverInstanceBootstrapper can resolve both factories.
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("g", MinimalConfig);
|
||||
driver.DriverType.ShouldBe("GalaxyMxGateway");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user