using Opc.Ua; using Shouldly; using Xunit; 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 ReadWriteViewModelTests { private readonly FakeOpcUaClientService _service; private readonly ReadWriteViewModel _vm; /// Initializes a new instance of the ReadWriteViewModelTests class. public ReadWriteViewModelTests() { _service = new FakeOpcUaClientService { ReadResult = new DataValue(new Variant("TestValue"), StatusCodes.Good, DateTime.UtcNow, DateTime.UtcNow) }; var dispatcher = new SynchronousUiDispatcher(); _vm = new ReadWriteViewModel(_service, dispatcher); } /// Verifies that the read command cannot execute when disconnected. [Fact] public void ReadCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadCommand.CanExecute(null).ShouldBeFalse(); } /// Verifies that the read command cannot execute when no node is selected. [Fact] public void ReadCommand_CannotExecute_WhenNoNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _vm.ReadCommand.CanExecute(null).ShouldBeFalse(); } /// Verifies that the read command can execute when connected and a node is selected. [Fact] public void ReadCommand_CanExecute_WhenConnectedAndNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadCommand.CanExecute(null).ShouldBeTrue(); } /// Verifies that the read command updates value and status. [Fact] public async Task ReadCommand_UpdatesValueAndStatus() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; // auto-read fires on selection change, so reset count var countBefore = _service.ReadCallCount; await _vm.ReadCommand.ExecuteAsync(null); _vm.CurrentValue.ShouldBe("TestValue"); _vm.CurrentStatus.ShouldNotBeNull(); _vm.SourceTimestamp.ShouldNotBeNull(); _vm.ServerTimestamp.ShouldNotBeNull(); (_service.ReadCallCount - countBefore).ShouldBe(1); } /// Verifies that auto-read fires on selection change when connected. [Fact] public void AutoRead_OnSelectionChange_WhenConnected() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; // The auto-read fires asynchronously; give it a moment // In synchronous dispatcher it should fire immediately _service.ReadCallCount.ShouldBeGreaterThanOrEqualTo(1); } /// Verifies that null selection does not call the service. [Fact] public void NullSelection_DoesNotCallService() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _service.ReadCallCount.ShouldBe(0); } /// Verifies that the write command updates write status. [Fact] public async Task WriteCommand_UpdatesWriteStatus() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.WriteValue = "NewValue"; // Reset read count from auto-read var readCountBefore = _service.ReadCallCount; await _vm.WriteCommand.ExecuteAsync(null); _vm.WriteStatus.ShouldNotBeNull(); _service.WriteCallCount.ShouldBe(1); _service.LastWriteValue.ShouldBe("NewValue"); } /// Verifies that the write command cannot execute when disconnected. [Fact] public void WriteCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.WriteCommand.CanExecute(null).ShouldBeFalse(); } /// Verifies that read command error sets error status. [Fact] public async Task ReadCommand_Error_SetsErrorStatus() { _service.ReadException = new Exception("Read failed"); _vm.IsConnected = true; // We need to set SelectedNodeId and manually trigger read // because auto-read catches the exception too _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.CurrentStatus.ShouldContain("Error"); } /// Verifies that clear resets all properties. [Fact] public void Clear_ResetsAllProperties() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.WriteValue = "test"; _vm.WriteStatus = "Good"; _vm.Clear(); _vm.SelectedNodeId.ShouldBeNull(); _vm.CurrentValue.ShouldBeNull(); _vm.CurrentStatus.ShouldBeNull(); _vm.SourceTimestamp.ShouldBeNull(); _vm.ServerTimestamp.ShouldBeNull(); _vm.WriteValue.ShouldBeNull(); _vm.WriteStatus.ShouldBeNull(); } /// Verifies that IsNodeSelected tracks the selected node ID. [Fact] public void IsNodeSelected_TracksSelectedNodeId() { _vm.IsNodeSelected.ShouldBeFalse(); _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.IsNodeSelected.ShouldBeTrue(); _vm.SelectedNodeId = null; _vm.IsNodeSelected.ShouldBeFalse(); } }