f0700571e8
51 new test cases (34 methods) in Sql.Common.Tests covering AtTimeEvaluator, AggregateEvaluator (hand-built tag-A bucket rows, ProcessedRead literals), BucketPlan, LiveWritePreparer and HistoryUpdatePlanner (incl. resolutionTicks=10 truncate-before-dedup and the superseded-duplicate outcome), plus EventContinuation, HistorianHealthCounters, ClampSeverity and StoredSample.FromStored. Each was falsified by reverting the rule it pins (37 mutations, all red, all restored). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
68 lines
2.8 KiB
C#
68 lines
2.8 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.Tests;
|
|
|
|
/// <summary>EventContinuation, HistorianHealthCounters, ClampSeverity and the StoredSample quality clamp.</summary>
|
|
public sealed class SharedHelperTests
|
|
{
|
|
[Fact]
|
|
public void Event_continuation_is_ticks_pipe_event_id()
|
|
{
|
|
var t = new DateTime(2026, 10, 9, 8, 0, 0, DateTimeKind.Utc).AddTicks(1234567);
|
|
EventContinuation.Encode(t, "abc").ShouldBe($"{t.Ticks.ToString(System.Globalization.CultureInfo.InvariantCulture)}|abc");
|
|
EventContinuation.Encode(new DateTime(42), "id").ShouldBe("42|id");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(-5, 0)]
|
|
[InlineData(700, 700)]
|
|
[InlineData(70_000, 65_535)]
|
|
public void Event_severity_is_clamped_to_ushort(int stored, int expected) =>
|
|
HistoricalEventRows.ClampSeverity(stored).ShouldBe((ushort)expected);
|
|
|
|
[Fact]
|
|
public void Health_counters_open_only_after_a_success_with_no_failure_since()
|
|
{
|
|
var h = new HistorianHealthCounters();
|
|
h.Snapshot().ProcessConnectionOpen.ShouldBeFalse();
|
|
h.RecordSuccess();
|
|
h.Snapshot().ProcessConnectionOpen.ShouldBeTrue();
|
|
h.RecordFailure(new InvalidOperationException("boom"));
|
|
var s = h.Snapshot();
|
|
s.ProcessConnectionOpen.ShouldBeFalse();
|
|
s.EventConnectionOpen.ShouldBeFalse();
|
|
s.ConsecutiveFailures.ShouldBe(1);
|
|
s.TotalQueries.ShouldBe(2);
|
|
s.LastError.ShouldBe("InvalidOperationException: boom");
|
|
h.RecordSuccess();
|
|
h.Snapshot().ConsecutiveFailures.ShouldBe(0);
|
|
h.Snapshot().ProcessConnectionOpen.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void A_cancellation_is_not_a_health_failure()
|
|
{
|
|
var h = new HistorianHealthCounters();
|
|
h.RecordFailure(new OperationCanceledException());
|
|
h.RecordFailure(new TaskCanceledException());
|
|
var s = h.Snapshot();
|
|
s.TotalQueries.ShouldBe(0);
|
|
s.TotalFailures.ShouldBe(0);
|
|
s.LastError.ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void A_stored_empty_sample_with_a_non_bad_status_is_served_BadNoData()
|
|
{
|
|
var t = DateTime.UnixEpoch;
|
|
StoredSample.FromStored(t, HistorianValue.Empty, 0u).Status.ShouldBe(0x809B0000u);
|
|
StoredSample.FromStored(t, HistorianValue.Empty, 0x40000000u).Status.ShouldBe(0x809B0000u);
|
|
StoredSample.FromStored(t, HistorianValue.Empty, 0x80310000u).Status.ShouldBe(0x80310000u);
|
|
StoredSample.FromStored(t, HistorianValue.FromDouble(1), 0x40000000u).Status.ShouldBe(0x40000000u);
|
|
StoredSample.FromStored(t, HistorianValue.Empty, 0x80310000u).IsUsable.ShouldBeFalse();
|
|
StoredSample.FromStored(t, HistorianValue.FromDouble(1), 0x40000000u).IsGood.ShouldBeFalse();
|
|
}
|
|
}
|