From c6082aa0b9471bca9041c9d11d395c7a31b111f9 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 24 May 2026 01:07:17 -0400 Subject: [PATCH] fix(admin-e2e): register missing DI services so ClusterDetail interactive circuit boots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UnsTabDragDropE2ETests were timing out at the 'UNS Structure' nav-link locator because AdminWebAppFactory never registered AdminHubConnectionFactory / HubTokenService / DataProtection — ClusterDetail.razor's @inject threw at circuit boot, so the page never advanced past the Loading placeholder. 2 → 3 pass after the registrations land. Also documents the Modbus standard-vs- exception_injection coverage matrix in the fixture README + cross-references docs/drivers/AbServer-Test-Fixture.md from each Emulate test so a developer landing on a skipped test has a direct doc pointer. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Emulate/AbCipEmulateAlmdTests.cs | 4 ++++ .../Emulate/AbCipEmulateUdtReadTests.cs | 4 ++++ .../Docker/README.md | 17 +++++++++++++++++ .../AdminWebAppFactory.cs | 10 ++++++++++ 4 files changed, 35 insertions(+) diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs index 0624b88..c93c998 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs @@ -29,6 +29,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate; /// Runs only when AB_SERVER_PROFILE=emulate. ab_server has no ALMD /// instruction + no alarm subsystem, so this tier-gated class couldn't produce a /// meaningful result against the default simulator. +/// The Emulate tier is hardware-gated (Rockwell per-seat license, Windows-only, +/// conflicts with Docker Desktop's WSL 2 backend) so a permanent skip in CI is expected +/// — see docs/drivers/AbServer-Test-Fixture.md §"Logix Emulate golden-box tier" +/// for the full rationale + the gap matrix this test closes. /// [Collection("AbServerEmulate")] [Trait("Category", "Integration")] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs index 3386629..9382579 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs @@ -27,6 +27,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate; /// Runs only when AB_SERVER_PROFILE=emulate. With ab_server /// (the default), skips cleanly — ab_server lacks UDT / Template Object emulation /// so this wire-level test couldn't pass against it regardless. +/// The Emulate tier is hardware-gated (Rockwell per-seat license, Windows-only, +/// conflicts with Docker Desktop's WSL 2 backend) so a permanent skip in CI is expected +/// — see docs/drivers/AbServer-Test-Fixture.md §"Logix Emulate golden-box tier" +/// for the full rationale + the gap matrix this test closes. /// [Collection("AbServerEmulate")] [Trait("Category", "Integration")] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md index dca6e16..0986415 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md @@ -48,6 +48,23 @@ service + starting another. The integration tests discriminate by a separate `MODBUS_SIM_PROFILE` env var so they skip correctly when the wrong profile is live. +### Profile coverage matrix + +The two general-purpose profiles cover disjoint test sets. A full pass +of the integration suite requires running both — serially on a single +docker host (the `:5020` collision), or in parallel on two hosts. + +| Job | Bring up | Env to set | Expected outcome | +|---|---|---|---| +| `modbus-standard` | `lmxopcua-fix up modbus standard` | unset `MODBUS_SIM_PROFILE` (or set to `standard`) | Standard round-trip + AddressingGrammar suites pass; `ExceptionInjectionTests` (32 rows) skip with `MODBUS_SIM_PROFILE != exception_injection`. | +| `modbus-exception` | `lmxopcua-fix up modbus exception_injection` | `MODBUS_SIM_PROFILE=exception_injection` | `ExceptionInjectionTests` (32 rows) pass against the per-`(fc,address)` rule set; standard-profile suites (round-trip, AddressingGrammar) skip. | + +The DL205 / Mitsubishi / S7-1500 profiles are similar — each gates its +own quirks suite via `MODBUS_SIM_PROFILE=`. Tests that don't +need a specific profile (the basic round-trip set) run under any of +the three pymodbus-based profiles. The `exception_injection` profile +is the only one that runs `exception_injector.py` instead of pymodbus. + ## Endpoint - Default: `localhost:5020` diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs index 6c11859..c158e82 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.OtOpcUa.Admin.Hubs; +using ZB.MOM.WW.OtOpcUa.Admin.Security; using ZB.MOM.WW.OtOpcUa.Configuration; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Configuration.Enums; @@ -101,6 +102,15 @@ public sealed class AdminWebAppFactory : IAsyncDisposable builder.Services.AddScoped(); builder.Services.AddScoped(); + // ClusterDetail.razor injects AdminHubConnectionFactory to drive the live-banner hub + // connection; the factory depends on HubTokenService, which in turn needs Data Protection. + // Without these the InteractiveServer circuit fails to instantiate the component and the + // page never advances past the "Loading…" placeholder — Playwright then times out + // waiting for any tab nav-link to appear. Mirrors Program.cs:35-36. + builder.Services.AddDataProtection(); + builder.Services.AddSingleton(); + builder.Services.AddScoped(); + _app = builder.Build(); _app.UseStaticFiles(); _app.UseRouting();