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,