using Opc.Ua; using Shouldly; using Xunit; using ZB.MOM.WW.LmxOpcUa.Client.UI.Services; using ZB.MOM.WW.LmxOpcUa.Client.UI.Tests.Fakes; using ZB.MOM.WW.LmxOpcUa.Client.UI.ViewModels; namespace ZB.MOM.WW.LmxOpcUa.Client.UI.Tests; public class ReadWriteViewModelTests { private readonly FakeOpcUaClientService _service; private readonly ReadWriteViewModel _vm; 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); } [Fact] public void ReadCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadCommand.CanExecute(null).ShouldBeFalse(); } [Fact] public void ReadCommand_CannotExecute_WhenNoNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _vm.ReadCommand.CanExecute(null).ShouldBeFalse(); } [Fact] public void ReadCommand_CanExecute_WhenConnectedAndNodeSelected() { _vm.IsConnected = true; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.ReadCommand.CanExecute(null).ShouldBeTrue(); } [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); } [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); } [Fact] public void NullSelection_DoesNotCallService() { _vm.IsConnected = true; _vm.SelectedNodeId = null; _service.ReadCallCount.ShouldBe(0); } [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"); } [Fact] public void WriteCommand_CannotExecute_WhenDisconnected() { _vm.IsConnected = false; _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.WriteCommand.CanExecute(null).ShouldBeFalse(); } [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"); } [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(); } [Fact] public void IsNodeSelected_TracksSelectedNodeId() { _vm.IsNodeSelected.ShouldBeFalse(); _vm.SelectedNodeId = "ns=2;s=SomeNode"; _vm.IsNodeSelected.ShouldBeTrue(); _vm.SelectedNodeId = null; _vm.IsNodeSelected.ShouldBeFalse(); } }