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; 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); } [Fact] public void ReadHistoryCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeFalse(); } [Fact] public void ReadHistoryCommand_CannotExecute_WhenNoNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeFalse(); } [Fact] public void ReadHistoryCommand_CanExecute_WhenConnectedAndNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadHistoryCommand.CanExecute(null).ShouldBeTrue(); } [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); } [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); } [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"); } [Fact] public async Task ReadHistoryCommand_IsLoading_FalseAfterComplete() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; await _vm.ReadHistoryCommand.ExecuteAsync(null); _vm.IsLoading.ShouldBeFalse(); } [Fact] public void DefaultValues_AreCorrect() { _vm.MaxValues.ShouldBe(1000); _vm.IntervalMs.ShouldBe(3600000); _vm.SelectedAggregateType.ShouldBeNull(); _vm.IsAggregateRead.ShouldBeFalse(); } [Fact] public void IsAggregateRead_TrueWhenAggregateSelected() { _vm.SelectedAggregateType = AggregateType.Maximum; _vm.IsAggregateRead.ShouldBeTrue(); } [Fact] public void AggregateTypes_ContainsNullForRaw() { _vm.AggregateTypes.ShouldContain((AggregateType?)null); _vm.AggregateTypes.Count.ShouldBe(8); // null + 7 enum values } [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(); } [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"); } }