feat(historian): HistoryRead override (Raw/Processed/AtTime) over IHistorianDataSource
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.Historian;
|
||||
|
||||
/// <summary>
|
||||
/// The default no-op historian source. Every read returns an empty result with a null
|
||||
/// continuation point; <see cref="NullHistorianDataSource.GetHealthSnapshot"/> reports a fully
|
||||
/// disabled shape; and <see cref="NullHistorianDataSource.Instance"/> is the shared singleton.
|
||||
/// </summary>
|
||||
public sealed class NullHistorianDataSourceTests
|
||||
{
|
||||
private static CancellationToken Ct => TestContext.Current.CancellationToken;
|
||||
|
||||
private static readonly NullHistorianDataSource Source = NullHistorianDataSource.Instance;
|
||||
|
||||
/// <summary><see cref="NullHistorianDataSource.Instance"/> is a single shared instance.</summary>
|
||||
[Fact]
|
||||
public void Instance_is_a_singleton()
|
||||
{
|
||||
NullHistorianDataSource.Instance.ShouldBeSameAs(NullHistorianDataSource.Instance);
|
||||
}
|
||||
|
||||
/// <summary>ReadRawAsync returns no samples and a null continuation point.</summary>
|
||||
[Fact]
|
||||
public async Task ReadRawAsync_returns_empty()
|
||||
{
|
||||
var result = await Source.ReadRawAsync(
|
||||
"WW.Tag", DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxValuesPerNode: 100, Ct);
|
||||
|
||||
result.Samples.ShouldBeEmpty();
|
||||
result.ContinuationPoint.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>ReadProcessedAsync returns no samples and a null continuation point.</summary>
|
||||
[Fact]
|
||||
public async Task ReadProcessedAsync_returns_empty()
|
||||
{
|
||||
var result = await Source.ReadProcessedAsync(
|
||||
"WW.Tag", DateTime.UtcNow.AddHours(-1), DateTime.UtcNow,
|
||||
TimeSpan.FromSeconds(10), HistoryAggregateType.Average, Ct);
|
||||
|
||||
result.Samples.ShouldBeEmpty();
|
||||
result.ContinuationPoint.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>ReadAtTimeAsync returns no samples and a null continuation point.</summary>
|
||||
[Fact]
|
||||
public async Task ReadAtTimeAsync_returns_empty()
|
||||
{
|
||||
var result = await Source.ReadAtTimeAsync(
|
||||
"WW.Tag", new[] { DateTime.UtcNow }, Ct);
|
||||
|
||||
result.Samples.ShouldBeEmpty();
|
||||
result.ContinuationPoint.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>ReadEventsAsync returns no events and a null continuation point.</summary>
|
||||
[Fact]
|
||||
public async Task ReadEventsAsync_returns_empty()
|
||||
{
|
||||
var result = await Source.ReadEventsAsync(
|
||||
sourceName: null, DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxEvents: 0, Ct);
|
||||
|
||||
result.Events.ShouldBeEmpty();
|
||||
result.ContinuationPoint.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>GetHealthSnapshot reports a fully-disabled (zeroed, disconnected, no-node) shape.</summary>
|
||||
[Fact]
|
||||
public void GetHealthSnapshot_is_disabled()
|
||||
{
|
||||
var snapshot = Source.GetHealthSnapshot();
|
||||
|
||||
snapshot.TotalQueries.ShouldBe(0);
|
||||
snapshot.TotalSuccesses.ShouldBe(0);
|
||||
snapshot.TotalFailures.ShouldBe(0);
|
||||
snapshot.ConsecutiveFailures.ShouldBe(0);
|
||||
snapshot.LastSuccessTime.ShouldBeNull();
|
||||
snapshot.LastFailureTime.ShouldBeNull();
|
||||
snapshot.LastError.ShouldBeNull();
|
||||
snapshot.ProcessConnectionOpen.ShouldBeFalse();
|
||||
snapshot.EventConnectionOpen.ShouldBeFalse();
|
||||
snapshot.ActiveProcessNode.ShouldBeNull();
|
||||
snapshot.ActiveEventNode.ShouldBeNull();
|
||||
snapshot.Nodes.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Dispose is a safe no-op (idempotent).</summary>
|
||||
[Fact]
|
||||
public void Dispose_is_a_noop()
|
||||
{
|
||||
Should.NotThrow(() =>
|
||||
{
|
||||
Source.Dispose();
|
||||
Source.Dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user