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,81 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class OpcUaClientFailoverTests
|
||||
{
|
||||
[Fact]
|
||||
public void ResolveEndpointCandidates_prefers_EndpointUrls_when_provided()
|
||||
{
|
||||
var opts = new OpcUaClientDriverOptions
|
||||
{
|
||||
EndpointUrl = "opc.tcp://fallback:4840",
|
||||
EndpointUrls = ["opc.tcp://primary:4840", "opc.tcp://backup:4841"],
|
||||
};
|
||||
var list = OpcUaClientDriver.ResolveEndpointCandidates(opts);
|
||||
list.Count.ShouldBe(2);
|
||||
list[0].ShouldBe("opc.tcp://primary:4840");
|
||||
list[1].ShouldBe("opc.tcp://backup:4841");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveEndpointCandidates_falls_back_to_single_EndpointUrl_when_list_empty()
|
||||
{
|
||||
var opts = new OpcUaClientDriverOptions { EndpointUrl = "opc.tcp://only:4840" };
|
||||
var list = OpcUaClientDriver.ResolveEndpointCandidates(opts);
|
||||
list.Count.ShouldBe(1);
|
||||
list[0].ShouldBe("opc.tcp://only:4840");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveEndpointCandidates_empty_list_treated_as_fallback_to_EndpointUrl()
|
||||
{
|
||||
// Explicit empty list should still fall back to the single-URL shortcut rather than
|
||||
// producing a zero-candidate sweep that would immediately throw with no URLs tried.
|
||||
var opts = new OpcUaClientDriverOptions
|
||||
{
|
||||
EndpointUrl = "opc.tcp://single:4840",
|
||||
EndpointUrls = [],
|
||||
};
|
||||
OpcUaClientDriver.ResolveEndpointCandidates(opts).Count.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HostName_uses_first_candidate_before_connect()
|
||||
{
|
||||
var opts = new OpcUaClientDriverOptions
|
||||
{
|
||||
EndpointUrls = ["opc.tcp://primary:4840", "opc.tcp://backup:4841"],
|
||||
};
|
||||
using var drv = new OpcUaClientDriver(opts, "opcua-host");
|
||||
drv.HostName.ShouldBe("opc.tcp://primary:4840",
|
||||
"pre-connect the dashboard should show the first candidate URL so operators can link back");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Initialize_against_all_unreachable_endpoints_throws_AggregateException_listing_each()
|
||||
{
|
||||
// Port 1 + port 2 + port 3 on loopback are all guaranteed closed (TCP RST immediate).
|
||||
// Failover sweep should attempt all three and throw AggregateException naming each URL
|
||||
// so operators see exactly which candidates were tried.
|
||||
var opts = new OpcUaClientDriverOptions
|
||||
{
|
||||
EndpointUrls = ["opc.tcp://127.0.0.1:1", "opc.tcp://127.0.0.1:2", "opc.tcp://127.0.0.1:3"],
|
||||
PerEndpointConnectTimeout = TimeSpan.FromMilliseconds(500),
|
||||
Timeout = TimeSpan.FromMilliseconds(500),
|
||||
AutoAcceptCertificates = true,
|
||||
};
|
||||
using var drv = new OpcUaClientDriver(opts, "opcua-failover");
|
||||
|
||||
var ex = await Should.ThrowAsync<AggregateException>(async () =>
|
||||
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken));
|
||||
|
||||
ex.Message.ShouldContain("127.0.0.1:1");
|
||||
ex.Message.ShouldContain("127.0.0.1:2");
|
||||
ex.Message.ShouldContain("127.0.0.1:3");
|
||||
drv.GetHealth().State.ShouldBe(DriverState.Faulted);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user