From 8a8b9ec564d72beda06fa1e35098a46d5be40e32 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 8 Jul 2026 23:16:23 -0400 Subject: [PATCH] test(archreview #10/#13): guard prod DI binds the REAL invoker factory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- ...silienceInvokerFactoryRegistrationTests.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ResilienceInvokerFactoryRegistrationTests.cs diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ResilienceInvokerFactoryRegistrationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ResilienceInvokerFactoryRegistrationTests.cs new file mode 100644 index 00000000..6d6ec6b6 --- /dev/null +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ResilienceInvokerFactoryRegistrationTests.cs @@ -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; + +/// +/// Guards the production DI wiring for the Phase 6.1 resilience dispatch pipeline (arch-review #10 + +/// #13). AddOtOpcUaDriverFactories (the driver-node bootstrap) must bind the real +/// — not the +/// pass-through — and that factory must mint a real 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. +/// +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(); + + factory.ShouldBeOfType( + "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(); + + var invoker = factory.Create("drv-1", "Modbus"); + + invoker.ShouldBeOfType( + "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().ShouldNotBeNull(); + sp.GetService().ShouldNotBeNull(); + sp.GetRequiredService() + .ShouldBeSameAs(sp.GetRequiredService(), "builder must be a singleton"); + } +}