using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Admin.Components.Pages.Modbus;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
namespace ZB.MOM.WW.OtOpcUa.Admin.Tests;
///
/// #145 Admin UI: smoke coverage for the ModbusOptionsEditor view model. The Blazor
/// component itself is exercised in browser-runtime tests; this fixture pins the default
/// values the form initialises to so a regression that flips an unedited row to a
/// non-default value gets caught.
///
[Trait("Category", "Unit")]
public sealed class ModbusOptionsViewModelTests
{
[Fact]
public void DriversTab_Serialized_Defaults_RoundTrip_Through_Factory()
{
// #147 — the form's SaveAsync serialises ModbusOptionsViewModel to the JSON DTO
// shape ModbusDriverFactoryExtensions consumes. This test pins the round-trip:
// unedited form → JSON → driver instance → options match defaults.
var vm = new ModbusOptionsEditor.ModbusOptionsViewModel();
var json = SerializeForRoundTrip(vm);
var driver = ModbusDriverFactoryExtensions.CreateInstance("modbus-roundtrip", json);
var opts = (ModbusDriverOptions)typeof(ModbusDriver)
.GetField("_options", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
.GetValue(driver)!;
opts.Host.ShouldBe(vm.Host);
opts.Port.ShouldBe(vm.Port);
opts.UnitId.ShouldBe(vm.UnitId);
opts.Family.ShouldBe(vm.Family);
opts.MelsecSubFamily.ShouldBe(vm.MelsecSubFamily);
opts.KeepAlive.Enabled.ShouldBe(vm.KeepAliveEnabled);
opts.MaxRegistersPerRead.ShouldBe((ushort)vm.MaxRegistersPerRead);
opts.MaxCoilsPerRead.ShouldBe((ushort)vm.MaxCoilsPerRead);
opts.MaxReadGap.ShouldBe((ushort)vm.MaxReadGap);
opts.WriteOnChangeOnly.ShouldBe(vm.WriteOnChangeOnly);
}
[Fact]
public void DriversTab_Serializes_Edited_Values_Correctly()
{
// Sanity: changing a few fields in the view model produces a JSON the factory
// accepts and the resulting driver carries the edited values.
var vm = new ModbusOptionsEditor.ModbusOptionsViewModel
{
Host = "10.5.5.5",
Port = 1502,
UnitId = 7,
Family = ModbusFamily.DL205,
MaxReadGap = 12,
WriteOnChangeOnly = true,
};
var json = SerializeForRoundTrip(vm);
var driver = ModbusDriverFactoryExtensions.CreateInstance("modbus-edited", json);
var opts = (ModbusDriverOptions)typeof(ModbusDriver)
.GetField("_options", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
.GetValue(driver)!;
opts.Host.ShouldBe("10.5.5.5");
opts.Port.ShouldBe(1502);
opts.UnitId.ShouldBe((byte)7);
opts.Family.ShouldBe(ModbusFamily.DL205);
opts.MaxReadGap.ShouldBe((ushort)12);
opts.WriteOnChangeOnly.ShouldBeTrue();
}
///
/// Mirror of DriversTab.razor's SerializeModbusOptions — kept here so the test
/// doesn't have to reach through Blazor component plumbing to invoke it. If the
/// component method signature drifts, update both.
///
private static string SerializeForRoundTrip(ModbusOptionsEditor.ModbusOptionsViewModel m) =>
System.Text.Json.JsonSerializer.Serialize(new
{
host = m.Host,
port = m.Port,
unitId = m.UnitId,
family = m.Family.ToString(),
melsecSubFamily = m.MelsecSubFamily.ToString(),
keepAlive = new
{
enabled = m.KeepAliveEnabled,
timeMs = m.KeepAliveTimeSec * 1000,
intervalMs = m.KeepAliveIntervalSec * 1000,
retryCount = m.KeepAliveRetryCount,
},
reconnect = new
{
initialDelayMs = m.ReconnectInitialDelayMs,
maxDelayMs = m.ReconnectMaxDelayMs,
backoffMultiplier = m.ReconnectBackoffMultiplier,
},
maxRegistersPerRead = m.MaxRegistersPerRead,
maxRegistersPerWrite = m.MaxRegistersPerWrite,
maxCoilsPerRead = m.MaxCoilsPerRead,
maxReadGap = m.MaxReadGap,
useFC15ForSingleCoilWrites = m.UseFC15ForSingleCoilWrites,
useFC16ForSingleRegisterWrites = m.UseFC16ForSingleRegisterWrites,
writeOnChangeOnly = m.WriteOnChangeOnly,
tags = Array.Empty