using Opc.Ua; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Client.Shared.Models; using ZB.MOM.WW.OtOpcUa.Client.UI.Services; using ZB.MOM.WW.OtOpcUa.Client.UI.Tests.Fakes; using ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels; namespace ZB.MOM.WW.OtOpcUa.Client.UI.Tests; public class HistoryViewModelTests { private readonly FakeOpcUaClientService _service; private readonly HistoryViewModel _vm; /// Initializes a new instance of the class. public HistoryViewModelTests() { _service = new FakeOpcUaClientService { HistoryRawResult = [ new DataValue(new Variant(10), StatusCodes.Good, DateTime.UtcNow, DateTime.UtcNow), new DataValue(new Variant(20), StatusCodes.Good, DateTime.UtcNow, DateTime.UtcNow) ], HistoryAggregateResult = [ new DataValue(new Variant(15.0), StatusCodes.Good, DateTime.UtcNow, DateTime.UtcNow) ] }; var dispatcher = new SynchronousUiDispatcher(); _vm = new HistoryViewModel(_service, dispatcher); } /// Verifies that the read history command cannot execute when disconnected. [Fact] public void ReadHistoryCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeFalse(); } /// Verifies that the read history command cannot execute when no node is selected. [Fact] public void ReadHistoryCommand_CannotExecute_WhenNoNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeFalse(); } /// Verifies that the read history command can execute when connected and a node is selected. [Fact] public void ReadHistoryCommand_CanExecute_WhenConnectedAndNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeTrue(); } /// Verifies that a raw history read populates results correctly. [Fact] public async Task ReadHistoryCommand_Raw_PopulatesResults() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.SelectedAggregateType = null; // Raw await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.Results.Count.ShouldBe(2); _vm.Results[0].Value.ShouldBe("10"); _vm.Results[1].Value.ShouldBe("20"); _service.HistoryReadRawCallCount.ShouldBe(1); _service.HistoryReadAggregateCallCount.ShouldBe(0); } /// Verifies that an aggregate history read populates results correctly. [Fact] public async Task ReadHistoryCommand_Aggregate_PopulatesResults() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.SelectedAggregateType = AggregateType.Average; await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.Results.Count.ShouldBe(1); _vm.Results[0].Value.ShouldBe("15"); _service.HistoryReadAggregateCallCount.ShouldBe(1); _service.LastAggregateType.ShouldBe(AggregateType.Average); _service.HistoryReadRawCallCount.ShouldBe(0); } /// Verifies that the read history command clears previous results before loading new ones. [Fact] public async Task ReadHistoryCommand_ClearsResultsBefore() { _vm.Results.Add(new HistoryValueViewModel("old", "Good", "t1", "t2")); _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.Results.ShouldNotContain(r => r.Value == "old"); } /// Verifies that the loading state is false after the read history command completes. [Fact] public async Task ReadHistoryCommand_IsLoading_FalseAfterComplete() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.IsLoading.ShouldBeFalse(); } /// Verifies that default values are initialized correctly. [Fact] public void DefaultValues_AreCorrect() { _vm.MaxValues.ShouldBe(1000); _vm.IntervalMs.ShouldBe(3600000); _vm.SelectedAggregateType.ShouldBeNull(); _vm.IsAggregateRead.ShouldBeFalse(); } /// Verifies that IsAggregateRead returns true when an aggregate type is selected. [Fact] public void IsAggregateRead_TrueWhenAggregateSelected() { _vm.SelectedAggregateType = AggregateType.Maximum; _vm.IsAggregateRead.ShouldBeTrue(); } /// Verifies that the aggregate types collection contains null for raw reads. [Fact] public void AggregateTypes_ContainsNullForRaw() { _vm.AggregateTypes.ShouldContain((AggregateType?)null); _vm.AggregateTypes.Count.ShouldBe(8); // null + 7 enum values } /// Verifies that the Clear method resets the view model state. [Fact] public void Clear_ResetsState() { _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.Results.Add(new HistoryValueViewModel("v", "s", "t1", "t2")); _vm.Clear(); _vm.Results.ShouldBeEmpty(); _vm.SelectedNodeId.ShouldBeNull(); } /// Verifies that read history command errors are displayed in the results. [Fact] public async Task ReadHistoryCommand_Error_ShowsErrorInResults() { _service.HistoryException = new Exception("History not supported"); _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.Results.Count.ShouldBe(1); _vm.Results[0].Value.ShouldContain("History not supported"); } }