Group all 69 projects into category subfolders under src/ and tests/ so the Rider Solution Explorer mirrors the module structure. Folders: Core, Server, Drivers (with a nested Driver CLIs subfolder), Client, Tooling. - Move every project folder on disk with git mv (history preserved as renames). - Recompute relative paths in 57 .csproj files: cross-category ProjectReferences, the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external mxaccessgw refs in Driver.Galaxy and its test project. - Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders. - Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL, integration, install). Build green (0 errors); unit tests pass. Docs left for a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
137 lines
6.1 KiB
C#
137 lines
6.1 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// #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.
|
|
/// </summary>
|
|
[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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<object>(),
|
|
});
|
|
|
|
[Fact]
|
|
public void Defaults_Match_DriverOption_Defaults()
|
|
{
|
|
var vm = new ModbusOptionsEditor.ModbusOptionsViewModel();
|
|
var driverDefault = new ModbusDriverOptions();
|
|
|
|
vm.Host.ShouldBe(driverDefault.Host);
|
|
vm.Port.ShouldBe(driverDefault.Port);
|
|
vm.UnitId.ShouldBe(driverDefault.UnitId);
|
|
vm.Family.ShouldBe(driverDefault.Family);
|
|
vm.MelsecSubFamily.ShouldBe(driverDefault.MelsecSubFamily);
|
|
|
|
vm.KeepAliveEnabled.ShouldBe(driverDefault.KeepAlive.Enabled);
|
|
vm.KeepAliveTimeSec.ShouldBe((int)driverDefault.KeepAlive.Time.TotalSeconds);
|
|
vm.KeepAliveIntervalSec.ShouldBe((int)driverDefault.KeepAlive.Interval.TotalSeconds);
|
|
vm.KeepAliveRetryCount.ShouldBe(driverDefault.KeepAlive.RetryCount);
|
|
|
|
vm.ReconnectInitialDelayMs.ShouldBe((int)driverDefault.Reconnect.InitialDelay.TotalMilliseconds);
|
|
vm.ReconnectMaxDelayMs.ShouldBe((int)driverDefault.Reconnect.MaxDelay.TotalMilliseconds);
|
|
vm.ReconnectBackoffMultiplier.ShouldBe(driverDefault.Reconnect.BackoffMultiplier);
|
|
|
|
vm.MaxRegistersPerRead.ShouldBe(driverDefault.MaxRegistersPerRead);
|
|
vm.MaxRegistersPerWrite.ShouldBe(driverDefault.MaxRegistersPerWrite);
|
|
vm.MaxCoilsPerRead.ShouldBe(driverDefault.MaxCoilsPerRead);
|
|
vm.MaxReadGap.ShouldBe(driverDefault.MaxReadGap);
|
|
vm.UseFC15ForSingleCoilWrites.ShouldBe(driverDefault.UseFC15ForSingleCoilWrites);
|
|
vm.UseFC16ForSingleRegisterWrites.ShouldBe(driverDefault.UseFC16ForSingleRegisterWrites);
|
|
vm.WriteOnChangeOnly.ShouldBe(driverDefault.WriteOnChangeOnly);
|
|
}
|
|
}
|