From 99eea0b4550a1d49a073759f65a6fd5ae23198d5 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 14 Jun 2026 00:26:10 -0400 Subject: [PATCH] test(runtime): raw-blob routing test uses a no-FullName protocol blob (genuine #4d case) --- .../DriverHostActorWriteRoutingTests.cs | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs index 87178416..70c79197 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs @@ -125,19 +125,21 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase recorder.Writes.ShouldBeEmpty(); } - /// The router keys purely on NodeId — the tag's TagConfig blob shape is irrelevant. A tag - /// seeded with a RAW protocol-driver config blob (Modbus-shaped, no FullName key) routes the - /// write to its owning child exactly like the Galaxy-style {FullName} blob does, because the - /// reverse map is built from the resolved FullName the composer projects, not the raw blob. + /// A protocol TagConfig blob with no FullName key routes by the equipment NodeId, and + /// the forwarded wire-ref is the raw blob verbatim. ExtractTagFullName falls back to the raw + /// blob string when no top-level FullName property is present, so the reverse map keys on + /// (DriverInstanceId, <raw-blob>) and the driver receives that exact string as its + /// WriteRequest.FullReference — not a FullName value extracted from the blob. [Fact] public void Primary_routes_write_for_raw_protocol_blob_tag() { var db = NewInMemoryDbFactory(); var recorder = new RecordingDriverFactory("Modbus"); - // Seed the tag with a RAW protocol blob ({region/address/dataType}) instead of {FullName}; the - // composer still resolves a FullName, so the reverse map keys on that and the blob never matters. - var deploymentId = SeedDeploymentWithRawBlobTag(db, RevA, - equip: "eq-2", driver: "drv-2", fullName: "40002", name: "torque"); + // Seed the tag with a genuine protocol blob that has NO FullName key (pure Modbus wire config). + // ExtractTagFullName falls back to returning the raw blob string verbatim, so that string IS the + // wire-ref the reverse map keys on and the driver receives as WriteRequest.FullReference. + var (deploymentId, rawBlobString) = SeedDeploymentWithRawBlobTag(db, RevA, + equip: "eq-2", driver: "drv-2", name: "torque"); var actor = SpawnHostAndApply(db, deploymentId, recorder); @@ -148,11 +150,12 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase var result = asker.ExpectMsg(Timeout); result.Success.ShouldBeTrue(); - // The write was forwarded to the owning child keyed by the resolved FullName, not the blob. + // The forwarded wire-ref is the raw blob string verbatim — ExtractTagFullName fell back because + // there is no top-level "FullName" key in the blob. AwaitAssert(() => { recorder.Writes.Count.ShouldBe(1); - recorder.Writes[0].FullReference.ShouldBe("40002"); + recorder.Writes[0].FullReference.ShouldBe(rawBlobString); recorder.Writes[0].Value.ShouldBe(456.0); }, duration: Timeout); } @@ -256,17 +259,23 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase } /// - /// Seeds a single-tag Sealed deployment exactly like , - /// except the tag's TagConfig is a RAW protocol-driver blob (Modbus-shaped: - /// {FullName, region, address, dataType}) instead of the bare Galaxy-style - /// {FullName} blob. The composer keys the reverse map purely on the blob's FullName - /// (ExtractTagFullName reads only that field), so the extra raw protocol keys alongside it - /// are irrelevant — proving routing is independent of the blob's broader shape. + /// Seeds a single-tag Sealed deployment whose tag's TagConfig is a genuine protocol-driver + /// blob with no FullName key (pure Modbus wire config: + /// {"region":"HoldingRegister","address":200,"dataType":"UInt16"}). Because + /// ExtractTagFullName finds no top-level FullName property, it falls back to + /// returning the raw blob string verbatim — that raw string becomes the + /// (DriverInstanceId, <raw-blob>) reverse-map key, and the driver receives it as + /// WriteRequest.FullReference. Returns both the and the exact + /// raw blob string so the caller can assert the forwarded wire-ref precisely. /// - private static DeploymentId SeedDeploymentWithRawBlobTag( + private static (DeploymentId DeploymentId, string RawBlobString) SeedDeploymentWithRawBlobTag( IDbContextFactory db, RevisionHash rev, - string equip, string driver, string fullName, string name) + string equip, string driver, string name) { + // Serialize the blob with NO FullName field — ExtractTagFullName will fall back to this verbatim. + var rawBlobString = JsonSerializer.Serialize( + new { region = "HoldingRegister", address = 200, dataType = "UInt16" }); + var artifact = JsonSerializer.SerializeToUtf8Bytes(new { Namespaces = new[] @@ -296,11 +305,8 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase Name = name, FolderPath = (string?)null, DataType = "Double", - // RAW protocol-driver TagConfig: FullName alongside the actual Modbus wire fields - // (region/address/dataType), NOT the bare Galaxy {FullName} blob. The composer extracts - // only FullName, proving the extra protocol keys don't change routing. - TagConfig = JsonSerializer.Serialize( - new { FullName = fullName, region = "HoldingRegister", address = 200, dataType = "UInt16" }), + // Pure protocol blob: no FullName key. ExtractTagFullName falls back to raw blob. + TagConfig = rawBlobString, }, }, }); @@ -317,7 +323,7 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase ArtifactBlob = artifact, }); ctx.SaveChanges(); - return id; + return (id, rawBlobString); } /// An whose CreateDbContext always throws,