using System; using System.Collections.Generic; using Opc.Ua; using Shouldly; using Xunit; using ZB.MOM.WW.LmxOpcUa.Host.Historian; namespace ZB.MOM.WW.LmxOpcUa.Tests.Historian { public class HistoryContinuationPointTests { private static List CreateTestValues(int count) { var values = new List(); for (var i = 0; i < count; i++) values.Add(new DataValue { Value = new Variant((double)i), SourceTimestamp = DateTime.UtcNow.AddSeconds(i), StatusCode = StatusCodes.Good }); return values; } [Fact] public void Store_ReturnsNonEmptyContinuationPoint() { var mgr = new HistoryContinuationPointManager(); var values = CreateTestValues(5); var cp = mgr.Store(values); cp.ShouldNotBeNull(); cp.Length.ShouldBe(16); // GUID = 16 bytes } [Fact] public void Retrieve_ValidContinuationPoint_ReturnsStoredValues() { var mgr = new HistoryContinuationPointManager(); var values = CreateTestValues(5); var cp = mgr.Store(values); var retrieved = mgr.Retrieve(cp); retrieved.ShouldNotBeNull(); retrieved!.Count.ShouldBe(5); } [Fact] public void Retrieve_SameContinuationPointTwice_ReturnsNullSecondTime() { var mgr = new HistoryContinuationPointManager(); var values = CreateTestValues(3); var cp = mgr.Store(values); mgr.Retrieve(cp).ShouldNotBeNull(); mgr.Retrieve(cp).ShouldBeNull(); } [Fact] public void Retrieve_InvalidBytes_ReturnsNull() { var mgr = new HistoryContinuationPointManager(); mgr.Retrieve(new byte[] { 1, 2, 3 }).ShouldBeNull(); } [Fact] public void Retrieve_NullBytes_ReturnsNull() { var mgr = new HistoryContinuationPointManager(); mgr.Retrieve(null!).ShouldBeNull(); } [Fact] public void Retrieve_UnknownGuid_ReturnsNull() { var mgr = new HistoryContinuationPointManager(); mgr.Retrieve(Guid.NewGuid().ToByteArray()).ShouldBeNull(); } [Fact] public void Release_RemovesContinuationPoint() { var mgr = new HistoryContinuationPointManager(); var values = CreateTestValues(5); var cp = mgr.Store(values); mgr.Release(cp); mgr.Retrieve(cp).ShouldBeNull(); } [Fact] public void Retrieve_ExpiredContinuationPoint_ReturnsNull() { var mgr = new HistoryContinuationPointManager(TimeSpan.FromMilliseconds(1)); var values = CreateTestValues(5); var cp = mgr.Store(values); System.Threading.Thread.Sleep(50); mgr.Retrieve(cp).ShouldBeNull(); } [Fact] public void Release_PurgesExpiredEntries() { var mgr = new HistoryContinuationPointManager(TimeSpan.FromMilliseconds(1)); var cp1 = mgr.Store(CreateTestValues(3)); var cp2 = mgr.Store(CreateTestValues(5)); System.Threading.Thread.Sleep(50); // Release one — purge should clean both expired entries mgr.Release(cp1); mgr.Retrieve(cp2).ShouldBeNull(); } [Fact] public void MultipleContinuationPoints_IndependentRetrieval() { var mgr = new HistoryContinuationPointManager(); var values1 = CreateTestValues(3); var values2 = CreateTestValues(7); var cp1 = mgr.Store(values1); var cp2 = mgr.Store(values2); var r1 = mgr.Retrieve(cp1); var r2 = mgr.Retrieve(cp2); r1.ShouldNotBeNull(); r1!.Count.ShouldBe(3); r2.ShouldNotBeNull(); r2!.Count.ShouldBe(7); } } }