test(runtime): raw-blob routing test uses a no-FullName protocol blob (genuine #4d case)
This commit is contained in:
+30
-24
@@ -125,19 +125,21 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase
|
||||
recorder.Writes.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>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 <c>FullName</c> key) routes the
|
||||
/// write to its owning child exactly like the Galaxy-style <c>{FullName}</c> blob does, because the
|
||||
/// reverse map is built from the resolved <c>FullName</c> the composer projects, not the raw blob.</summary>
|
||||
/// <summary>A protocol TagConfig blob with no <c>FullName</c> key routes by the equipment NodeId, and
|
||||
/// the forwarded wire-ref is the raw blob verbatim. <c>ExtractTagFullName</c> falls back to the raw
|
||||
/// blob string when no top-level <c>FullName</c> property is present, so the reverse map keys on
|
||||
/// <c>(DriverInstanceId, <raw-blob>)</c> and the driver receives that exact string as its
|
||||
/// <c>WriteRequest.FullReference</c> — not a FullName value extracted from the blob.</summary>
|
||||
[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<DriverHostActor.NodeWriteResult>(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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeds a single-tag Sealed deployment exactly like <see cref="SeedDeploymentWithEquipmentTags"/>,
|
||||
/// except the tag's <c>TagConfig</c> is a RAW protocol-driver blob (Modbus-shaped:
|
||||
/// <c>{FullName, region, address, dataType}</c>) instead of the bare Galaxy-style
|
||||
/// <c>{FullName}</c> blob. The composer keys the reverse map purely on the blob's <c>FullName</c>
|
||||
/// (<c>ExtractTagFullName</c> 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 <c>TagConfig</c> is a genuine protocol-driver
|
||||
/// blob with <strong>no <c>FullName</c> key</strong> (pure Modbus wire config:
|
||||
/// <c>{"region":"HoldingRegister","address":200,"dataType":"UInt16"}</c>). Because
|
||||
/// <c>ExtractTagFullName</c> finds no top-level <c>FullName</c> property, it falls back to
|
||||
/// returning the raw blob string verbatim — that raw string becomes the
|
||||
/// <c>(DriverInstanceId, <raw-blob>)</c> reverse-map key, and the driver receives it as
|
||||
/// <c>WriteRequest.FullReference</c>. Returns both the <see cref="DeploymentId"/> and the exact
|
||||
/// raw blob string so the caller can assert the forwarded wire-ref precisely.
|
||||
/// </summary>
|
||||
private static DeploymentId SeedDeploymentWithRawBlobTag(
|
||||
private static (DeploymentId DeploymentId, string RawBlobString) SeedDeploymentWithRawBlobTag(
|
||||
IDbContextFactory<OtOpcUaConfigDbContext> 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);
|
||||
}
|
||||
|
||||
/// <summary>An <see cref="IDbContextFactory{TContext}"/> whose <c>CreateDbContext</c> always throws,
|
||||
|
||||
Reference in New Issue
Block a user