diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverReconnectWireTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverReconnectWireTests.cs
new file mode 100644
index 00000000..1f2413da
--- /dev/null
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverReconnectWireTests.cs
@@ -0,0 +1,80 @@
+using Shouldly;
+using Xunit;
+using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
+using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
+
+namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
+
+///
+/// Reconnect + resume leg of the OpcUaClient driver's wire coverage (Gitea #622): the in-process server is
+/// stopped and a fresh one is booted on the SAME port / application URI / PKI store — a process restart as the
+/// driver sees it, with an empty address space the test then re-creates. The driver must detect the loss,
+/// rebuild a session, and its existing subscription must deliver again with no caller action.
+///
+[Trait("Category", "Integration")]
+public sealed class OpcUaClientDriverReconnectWireTests
+{
+ private static async Task WaitAsync(Func condition, string because, TimeSpan? timeout = null)
+ {
+ var deadline = DateTime.UtcNow + (timeout ?? TimeSpan.FromSeconds(60));
+ while (DateTime.UtcNow < deadline)
+ {
+ if (condition()) return;
+ await Task.Delay(50);
+ }
+ condition().ShouldBeTrue(because);
+ }
+
+ [Fact]
+ public async Task After_a_server_restart_the_driver_reconnects_and_the_subscription_resumes_delivering()
+ {
+ var ct = TestContext.Current.CancellationToken;
+ var rig = await OpcUaClientDriverWireTests.StartRigAsync("drv-reconnect", ct);
+ await using var _rig = rig;
+ OpcUaClientDriverWireTests.Publish(rig, OpcUaClientDriverWireTests.SpeedId, 10.0f);
+
+ await using var drv = OpcUaClientDriverWireTests.NewDriver(
+ rig, "wire-reconnect", reconnectPeriod: TimeSpan.FromSeconds(1));
+ await drv.InitializeAsync("{}", ct);
+
+ var seen = new List();
+ drv.OnDataChange += (_, e) =>
+ {
+ if (e.FullReference == OpcUaClientDriverWireTests.SpeedRef && e.Snapshot.StatusCode == 0u)
+ lock (seen) seen.Add(Convert.ToSingle(e.Snapshot.Value));
+ };
+ var states = new List();
+ drv.OnHostStatusChanged += (_, e) => { lock (states) states.Add(e.NewState); };
+
+ await drv.SubscribeAsync([OpcUaClientDriverWireTests.SpeedRef], TimeSpan.FromMilliseconds(100), ct);
+ await WaitAsync(() => { lock (seen) return seen.Contains(10.0f); }, "initial value must arrive before the outage");
+
+ // Outage: the loss must be DETECTED (host leaves Running) while the server is genuinely down.
+ await rig.RestartAsync(ct, whileDown: async () =>
+ {
+ await WaitAsync(
+ () => { lock (states) return states.Any(s => s != HostState.Running); },
+ "the driver must notice the server is gone", TimeSpan.FromSeconds(30));
+ });
+
+ // The new server is empty: redeploy the nodes and publish a value no earlier delivery could have carried.
+ rig.EnsureRawVariable("Speed", "Float", writable: true);
+ rig.EnsureRawVariable("Counter", "Int32", writable: false);
+ OpcUaClientDriverWireTests.Publish(rig, OpcUaClientDriverWireTests.SpeedId, 77.0f);
+
+ await WaitAsync(() => drv.GetHostStatuses().Single().State == HostState.Running,
+ "the driver must reconnect after the server is back");
+ await WaitAsync(() => { lock (seen) return seen.Contains(77.0f); },
+ "the pre-outage subscription must resume and deliver the post-restart value");
+
+ // Ongoing delivery, not just a one-off initial value.
+ OpcUaClientDriverWireTests.Publish(rig, OpcUaClientDriverWireTests.SpeedId, 88.0f);
+ await WaitAsync(() => { lock (seen) return seen.Contains(88.0f); },
+ "a later change must also reach the resumed subscription");
+
+ // And the data plane works for plain reads on the new session.
+ var back = await drv.ReadAsync([OpcUaClientDriverWireTests.SpeedRef], ct);
+ back[0].StatusCode.ShouldBe(0u);
+ Convert.ToSingle(back[0].Value).ShouldBe(88.0f);
+ }
+}