feat(historian): typed HistorianValue contracts, IHistorianDataUpdater, IHistorianBackendProvider (no behaviour change)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.Historian;
|
||||
|
||||
public sealed class HistorianValueTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(1.5d, HistorianValueKind.Double)]
|
||||
[InlineData(1.5f, HistorianValueKind.Double)]
|
||||
[InlineData(7, HistorianValueKind.Int64)]
|
||||
[InlineData((short)7, HistorianValueKind.Int64)]
|
||||
[InlineData((byte)7, HistorianValueKind.Int64)]
|
||||
[InlineData(7u, HistorianValueKind.Int64)]
|
||||
[InlineData(7L, HistorianValueKind.Int64)]
|
||||
[InlineData(true, HistorianValueKind.Boolean)]
|
||||
[InlineData("x", HistorianValueKind.String)]
|
||||
public void Maps_clr_values_to_kinds(object input, HistorianValueKind expected)
|
||||
{
|
||||
HistorianValue.TryFromObject(input, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decimal_maps_to_double()
|
||||
{
|
||||
HistorianValue.TryFromObject(2.5m, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(HistorianValueKind.Double);
|
||||
v.Number.ShouldBe(2.5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ulong_above_long_max_maps_to_double()
|
||||
{
|
||||
HistorianValue.TryFromObject(ulong.MaxValue, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(HistorianValueKind.Double);
|
||||
v.Number.ShouldBe((double)ulong.MaxValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ulong_within_long_range_maps_to_int64()
|
||||
{
|
||||
HistorianValue.TryFromObject((ulong)5, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(HistorianValueKind.Int64);
|
||||
v.Integer.ShouldBe(5);
|
||||
HistorianValue.TryFromObject((ulong)long.MaxValue, out var edge).ShouldBeTrue();
|
||||
edge.Kind.ShouldBe(HistorianValueKind.Int64);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_maps_to_empty()
|
||||
{
|
||||
HistorianValue.TryFromObject(null, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(HistorianValueKind.Empty);
|
||||
v.ToObject().ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Arrays_and_unknown_objects_are_not_historizable()
|
||||
{
|
||||
HistorianValue.TryFromObject(new[] { 1, 2 }, out _).ShouldBeFalse();
|
||||
HistorianValue.TryFromObject(new object(), out _).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Local_datetime_is_stored_as_utc_ticks()
|
||||
{
|
||||
var local = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Local);
|
||||
HistorianValue.TryFromObject(local, out var v).ShouldBeTrue();
|
||||
v.Kind.ShouldBe(HistorianValueKind.DateTime);
|
||||
v.Integer.ShouldBe(local.ToUniversalTime().Ticks);
|
||||
var back = (DateTime)v.ToObject()!;
|
||||
back.Kind.ShouldBe(DateTimeKind.Utc);
|
||||
back.Ticks.ShouldBe(local.ToUniversalTime().Ticks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Boolean_carries_integer_and_number()
|
||||
{
|
||||
var v = HistorianValue.FromBoolean(true);
|
||||
v.Integer.ShouldBe(1);
|
||||
v.Number.ShouldBe(1d);
|
||||
v.TryGetDouble(out var d).ShouldBeTrue();
|
||||
d.ShouldBe(1d);
|
||||
HistorianValue.FromBoolean(false).Integer.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDouble_is_true_only_for_double_int64_boolean()
|
||||
{
|
||||
HistorianValue.FromDouble(1.5).TryGetDouble(out _).ShouldBeTrue();
|
||||
HistorianValue.FromInt64(3).TryGetDouble(out var i).ShouldBeTrue();
|
||||
i.ShouldBe(3d);
|
||||
HistorianValue.FromBoolean(true).TryGetDouble(out _).ShouldBeTrue();
|
||||
HistorianValue.FromString("a").TryGetDouble(out _).ShouldBeFalse();
|
||||
HistorianValue.FromDateTime(DateTime.UtcNow).TryGetDouble(out _).ShouldBeFalse();
|
||||
HistorianValue.Empty.TryGetDouble(out _).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToObject_round_trips_each_kind()
|
||||
{
|
||||
HistorianValue.FromDouble(1.5).ToObject().ShouldBe(1.5);
|
||||
HistorianValue.FromInt64(9).ToObject().ShouldBe(9L);
|
||||
HistorianValue.FromBoolean(true).ToObject().ShouldBe(true);
|
||||
HistorianValue.FromBoolean(false).ToObject().ShouldBe(false);
|
||||
HistorianValue.FromString("a").ToObject().ShouldBe("a");
|
||||
var utc = new DateTime(2026, 5, 6, 7, 8, 9, DateTimeKind.Utc);
|
||||
var dt = (DateTime)HistorianValue.FromDateTime(utc).ToObject()!;
|
||||
dt.ShouldBe(utc);
|
||||
dt.Kind.ShouldBe(DateTimeKind.Utc);
|
||||
HistorianValue.Empty.ToObject().ShouldBeNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user