fix(transport): stop scanning DataSourceReference for blocker references
DetectBlockersAsync was feeding TemplateAttribute.DataSourceReference
into the identifier scanner alongside script bodies, but that field is
an OPC UA node-address path (e.g. "ns=3;s=Tank.Level") owned by the
device, not script source. The dot delimiter inside the path tripped
the heuristic into flagging the address segment ("Tank", "Sensor",
"TestChildObject", "DevAppEngine") as a missing SharedScript or
ExternalSystem reference -- a 100% false-positive class on any
template catalog with OPC-UA-mapped attributes.
Drop the DataSourceReference scan entirely. Attribute.Value is still
scanned because it can carry a design-time default expression that
calls into runtime APIs. Add a regression test pinning the new behavior.
This commit is contained in:
@@ -251,6 +251,45 @@ public sealed class BundleImporterPreviewTests : IDisposable
|
||||
i.Kind == ConflictKind.Blocker && i.Name == "HelperFn");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_does_not_flag_opcua_tag_paths_in_DataSourceReference_as_blockers()
|
||||
{
|
||||
// Arrange: a template with an attribute whose DataSourceReference is an
|
||||
// OPC UA node-address path -- e.g. "ns=3;s=Tank.Level". The segment
|
||||
// before the dot ("Tank") used to be parsed by the blocker heuristic as
|
||||
// a potential SharedScript reference, even though tag paths live in the
|
||||
// device's address space and are not script-callable.
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<ScadaLinkDbContext>();
|
||||
var t = new Template("Pump") { Description = "tag-path-check" };
|
||||
t.Attributes.Add(new TemplateAttribute("Level")
|
||||
{
|
||||
Value = "0",
|
||||
DataSourceReference = "ns=3;s=Tank.Level",
|
||||
});
|
||||
ctx.Templates.Add(t);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var bundleStream = await ExportTemplatesAsync();
|
||||
var bytes = await StreamToBytes(bundleStream);
|
||||
|
||||
// Act
|
||||
ImportPreview preview;
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var importer = scope.ServiceProvider.GetRequiredService<IBundleImporter>();
|
||||
var session = await importer.LoadAsync(new MemoryStream(bytes), passphrase: null);
|
||||
preview = await importer.PreviewAsync(session.SessionId);
|
||||
}
|
||||
|
||||
// Assert: "Tank" (the device-owned tag-path root segment) must not be
|
||||
// flagged as a missing SharedScript or ExternalSystem reference.
|
||||
Assert.DoesNotContain(preview.Items, i =>
|
||||
i.Kind == ConflictKind.Blocker && i.Name == "Tank");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_does_not_flag_stdlib_or_runtime_member_accesses_as_blockers()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user