diff --git a/ZB.MOM.WW.OtOpcUa.slnx b/ZB.MOM.WW.OtOpcUa.slnx index b07550cc..9b882fad 100644 --- a/ZB.MOM.WW.OtOpcUa.slnx +++ b/ZB.MOM.WW.OtOpcUa.slnx @@ -101,6 +101,7 @@ + diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs new file mode 100644 index 00000000..f1e1b8ab --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs @@ -0,0 +1,53 @@ +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.Commons.Browsing; +using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser; + +namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests; + +/// +/// End-to-end test against opc-plc Docker fixture. Validates that NodeId strings returned +/// by RootAsync/ExpandAsync round-trip cleanly through ExpandAsync — the regression test +/// for namespace-stable address encoding. +/// +[Trait("Category", "Integration"), Trait("Fixture", "opc-plc")] +public class BrowseRoundTripTests +{ + [Fact] + public async Task Three_level_expand_round_trips_resolve_back() + { + var endpoint = Environment.GetEnvironmentVariable("OPCUA_SIM_ENDPOINT") + ?? "opc.tcp://10.100.0.35:50000"; + // Numeric ordinals because the production browser uses default System.Text.Json + // without a JsonStringEnumConverter (SecurityPolicy.None=0, SecurityMode.None=0, + // AuthType.Anonymous=0). + var json = $$""" + { + "EndpointUrl":"{{endpoint}}", + "SecurityPolicy":0,"SecurityMode":0,"AuthType":0, + "SessionTimeout":"00:01:00","Timeout":"00:00:10","PerEndpointConnectTimeout":"00:00:10" + } + """; + + var browser = new OpcUaClientDriverBrowser(); + await using var session = await browser.OpenAsync(json, TestContext.Current.CancellationToken); + + var roots = await session.RootAsync(TestContext.Current.CancellationToken); + var current = roots.FirstOrDefault(n => n.HasChildrenHint); + current.ShouldNotBeNull("expected at least one Folder under ObjectsFolder on opc-plc"); + + // Drill down up to 2 more levels by alternately expanding the first Folder + for (var depth = 0; depth < 2; depth++) + { + var next = await session.ExpandAsync(current!.NodeId, TestContext.Current.CancellationToken); + var step = next.FirstOrDefault(n => n.HasChildrenHint); + if (step is null) break; + current = step; + } + + // Round-trip the leaf-most reachable folder string — must resolve back through the + // namespace map, prove the nsu= encoding survives. + var children = await session.ExpandAsync(current!.NodeId, TestContext.Current.CancellationToken); + children.ShouldNotBeNull(); + } +} diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests.csproj b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests.csproj new file mode 100644 index 00000000..dee7cb7d --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests.csproj @@ -0,0 +1,27 @@ + + + + net10.0 + enable + enable + false + true + ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + +