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
+
+
+
+
+
+
+
+
+