chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
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>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user