using System.Text.Json; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Runtime.Drivers; namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers; /// /// Verifies the artifact-decode mirror substitutes the reserved {{equip}} token in a /// VirtualTag script's ctx.GetTag("…") literals with the owning equipment's tag base /// (derived from its child Equipment-namespace tag's FullName) — byte-parity with /// Phase7Composer.Compose's live-edit path, using the same shared /// EquipmentScriptPaths helper and the same equipmentTags-derived base. /// public sealed class DeploymentArtifactEquipTokenTests { [Fact] public void ParseComposition_substitutes_equip_token_in_virtual_tag_expression() { var blob = JsonSerializer.SerializeToUtf8Bytes(new { Namespaces = new[] { new { NamespaceId = "ns-eq", Kind = 0 }, // NamespaceKind.Equipment }, DriverInstances = new[] { new { DriverInstanceId = "drv-modbus", DriverType = "Modbus", DriverConfig = "{}", NamespaceId = "ns-eq" }, }, Tags = new object[] { new { TagId = "tag-source", DriverInstanceId = "drv-modbus", EquipmentId = "TestMachine_001", Name = "Source", FolderPath = (string?)null, DataType = "Float", TagConfig = "{\"FullName\":\"TestMachine_001.Source\"}", }, }, Scripts = new[] { new { ScriptId = "s-equip", SourceCode = "return System.Convert.ToInt32(ctx.GetTag(\"{{equip}}.Source\").Value) > 50;", }, }, VirtualTags = new[] { new { VirtualTagId = "vt-equip", EquipmentId = "TestMachine_001", Name = "OverThreshold", DataType = "Boolean", ScriptId = "s-equip", }, }, }); var c = DeploymentArtifact.ParseComposition(blob); var vt = c.EquipmentVirtualTags.ShouldHaveSingleItem(); vt.Expression.ShouldContain("ctx.GetTag(\"TestMachine_001.Source\")"); vt.Expression.ShouldNotContain("{{equip}}"); vt.DependencyRefs.ShouldBe(new[] { "TestMachine_001.Source" }); } }