test(archreview #10/#13): guard prod DI binds the REAL invoker factory
AddOtOpcUaDriverFactories (the driver-node bootstrap) must bind the real DriverCapabilityInvokerFactory — not the NullDriverCapabilityInvokerFactory pass-through — and it must mint a real CapabilityInvoker per instance. This is the deterministic DI half of the #10 live gate: a refactor dropping back to the pass-through would make the whole retry/breaker/bulkhead pipeline inert in production while every pass-through-defaulting unit test stays green. 3 tests (real factory bound, real invoker minted, shared singletons), non-fixtured. Host.IntegrationTests.
This commit is contained in:
+68
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
||||
using ZB.MOM.WW.OtOpcUa.Host.Drivers;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Guards the production DI wiring for the Phase 6.1 resilience dispatch pipeline (arch-review #10 +
|
||||
/// #13). <c>AddOtOpcUaDriverFactories</c> (the driver-node bootstrap) must bind the <b>real</b>
|
||||
/// <see cref="DriverCapabilityInvokerFactory"/> — not the <see cref="NullDriverCapabilityInvokerFactory"/>
|
||||
/// pass-through — and that factory must mint a real <see cref="CapabilityInvoker"/> per driver instance.
|
||||
/// This is the DI half of the #10 live gate: without it a refactor could silently drop back to the
|
||||
/// pass-through and the retry/breaker/bulkhead pipeline would go inert in production (the exact
|
||||
/// "built-but-never-wired" failure the whole review targets), while every unit test — which defaults to
|
||||
/// the pass-through — stays green.
|
||||
/// </summary>
|
||||
public sealed class ResilienceInvokerFactoryRegistrationTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddOtOpcUaDriverFactories_binds_the_real_invoker_factory_not_the_pass_through()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddOtOpcUaDriverFactories();
|
||||
|
||||
using var sp = services.BuildServiceProvider();
|
||||
var factory = sp.GetRequiredService<IDriverCapabilityInvokerFactory>();
|
||||
|
||||
factory.ShouldBeOfType<DriverCapabilityInvokerFactory>(
|
||||
"the driver-node bootstrap must bind the real resilience factory — a NullDriverCapabilityInvokerFactory here means the pipeline is inert in production");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_registered_factory_mints_a_real_CapabilityInvoker_per_instance()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddOtOpcUaDriverFactories();
|
||||
|
||||
using var sp = services.BuildServiceProvider();
|
||||
var factory = sp.GetRequiredService<IDriverCapabilityInvokerFactory>();
|
||||
|
||||
var invoker = factory.Create("drv-1", "Modbus");
|
||||
|
||||
invoker.ShouldBeOfType<CapabilityInvoker>(
|
||||
"the real factory must produce a real CapabilityInvoker (not the NullDriverCapabilityInvoker pass-through)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_resilience_singletons_the_factory_depends_on_are_registered()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddOtOpcUaDriverFactories();
|
||||
|
||||
using var sp = services.BuildServiceProvider();
|
||||
|
||||
// The pipeline builder + status tracker are process singletons the factory closes over — the same
|
||||
// instance must be shared so pipelines cache + telemetry aggregate across all driver instances.
|
||||
sp.GetService<DriverResiliencePipelineBuilder>().ShouldNotBeNull();
|
||||
sp.GetService<DriverResilienceStatusTracker>().ShouldNotBeNull();
|
||||
sp.GetRequiredService<DriverResiliencePipelineBuilder>()
|
||||
.ShouldBeSameAs(sp.GetRequiredService<DriverResiliencePipelineBuilder>(), "builder must be a singleton");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user