Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScopeAccessorTests.cs
T

140 lines
4.8 KiB
C#

using ZB.MOM.WW.ScadaBridge.Commons.Types;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;
namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Scripts;
/// <summary>
/// Phase 1 of the script-scope rollout: verify path arithmetic for the new
/// Attributes / Children / Parent accessors. The actor-mediated reads/writes
/// are exercised end-to-end in Phase 2 once flattening carries scope info.
/// </summary>
public class ScopeAccessorTests
{
[Fact]
public void Root_SelfPath_Empty()
{
Assert.Equal("", ScriptScope.Root.SelfPath);
Assert.Null(ScriptScope.Root.ParentPath);
Assert.False(ScriptScope.Root.HasParent);
}
[Fact]
public void CompositionScope_HasParent()
{
var scope = new ScriptScope("TempSensor", "");
Assert.True(scope.HasParent);
Assert.Equal("", scope.ParentPath);
}
[Fact]
public void AttributeAccessor_RootScope_ResolvesBareKey()
{
var acc = new AttributeAccessor(null!, "");
Assert.Equal("Temperature", acc.Resolve("Temperature"));
}
[Fact]
public void AttributeAccessor_ComposedScope_PrependsPath()
{
var acc = new AttributeAccessor(null!, "TempSensor");
Assert.Equal("TempSensor.Temperature", acc.Resolve("Temperature"));
}
[Fact]
public void AttributeAccessor_NestedScope_ChainsPath()
{
var acc = new AttributeAccessor(null!, "Motor.TempSensor");
Assert.Equal("Motor.TempSensor.Temperature", acc.Resolve("Temperature"));
}
[Fact]
public void CompositionAccessor_AttributesShareScope()
{
var comp = new CompositionAccessor(null!, "TempSensor");
Assert.Equal("TempSensor", comp.Path);
Assert.Equal("TempSensor", comp.Attributes.ScopePrefix);
}
[Fact]
public void CompositionAccessor_ResolveScript_PrependsPath()
{
var comp = new CompositionAccessor(null!, "TempSensor");
Assert.Equal("TempSensor.Sample", comp.ResolveScript("Sample"));
}
[Fact]
public void CompositionAccessor_EmptyPath_LeavesScriptNameBare()
{
var comp = new CompositionAccessor(null!, "");
Assert.Equal("Sample", comp.ResolveScript("Sample"));
}
[Fact]
public void ChildrenAccessor_FromRoot_GivesUnpathedChild()
{
var children = new ChildrenAccessor(null!, "");
var temp = children["TempSensor"];
Assert.Equal("TempSensor", temp.Path);
}
[Fact]
public void ChildrenAccessor_FromComposition_PrefixesChild()
{
var children = new ChildrenAccessor(null!, "Motor");
var temp = children["TempSensor"];
Assert.Equal("Motor.TempSensor", temp.Path);
}
// --- AttributeAccessor encoding contract ----------------------------------
//
// AttributeAccessor.this[key].set and SetAsync both route through
// ScriptRuntimeContext.SetAttribute(name, encodedString), which requires
// a live Akka IActorRef; ScriptRuntimeContext has no virtual members and
// its constructor cannot be satisfied without a real ActorSystem, so a
// full-round-trip unit test through the accessor+context is not viable
// without a heavy Akka harness.
//
// Instead we test the encoding decision directly: AttributeAccessor is now
// documented to delegate value serialisation to AttributeValueCodec.Encode.
// These tests verify that contract at the codec level, which is exactly what
// the fix makes the accessor invoke.
[Fact]
public void AttributeValueCodec_Encode_List_ProducesJsonArray()
{
// A List<string> must encode to a JSON array, not the garbage
// "System.Collections.Generic.List`1[System.String]" that .ToString() produced.
var list = new List<string> { "a", "b" };
var encoded = AttributeValueCodec.Encode(list);
Assert.Equal("[\"a\",\"b\"]", encoded);
}
[Fact]
public void AttributeValueCodec_Encode_Scalar_PassesThrough()
{
// A plain string scalar must be returned unchanged (byte-identical to
// the historical value?.ToString() path for strings).
var encoded = AttributeValueCodec.Encode("x");
Assert.Equal("x", encoded);
}
[Fact]
public void AttributeValueCodec_Encode_Null_ReturnsNull()
{
// AttributeAccessor coalesces null → "" at the call site,
// but the codec itself must return null for null input.
Assert.Null(AttributeValueCodec.Encode(null));
}
[Fact]
public void AttributeValueCodec_Encode_IntList_ProducesJsonArray()
{
// Integer list elements encode as native-typed JSON numbers (NJ-1):
// [1,2,3], not the old quoted-element form ["1","2","3"].
var list = new List<int> { 1, 2, 3 };
var encoded = AttributeValueCodec.Encode(list);
Assert.Equal("[1,2,3]", encoded);
}
}