From fc01d806452685fccfc71303e2b05de7a18518a7 Mon Sep 17 00:00:00 2001 From: Scott Hanselman Date: Mon, 13 Apr 2026 17:48:56 -0700 Subject: [PATCH] test: cover websocket auto-reconnect regression Add regression coverage showing the reconnect loop starts after unexpected connection failures and remains disabled only when explicitly gated. This locks in the tray/node reconnect hardening around issue #149. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../WebSocketClientBaseTests.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/OpenClaw.Shared.Tests/WebSocketClientBaseTests.cs b/tests/OpenClaw.Shared.Tests/WebSocketClientBaseTests.cs index fbb8b81..05b0441 100644 --- a/tests/OpenClaw.Shared.Tests/WebSocketClientBaseTests.cs +++ b/tests/OpenClaw.Shared.Tests/WebSocketClientBaseTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -17,6 +18,7 @@ public class TestWebSocketClient : WebSocketClientBase public int OnErrorCallCount { get; private set; } public Exception? LastError { get; private set; } public int OnDisposingCallCount { get; private set; } + public bool AutoReconnectEnabled { get; set; } = true; protected override int ReceiveBufferSize => 8192; protected override string ClientRole => "test"; @@ -52,6 +54,8 @@ protected override void OnDisposing() OnDisposingCallCount++; } + protected override bool ShouldAutoReconnect() => AutoReconnectEnabled; + // Expose protected members for testing public void TestRaiseStatusChanged(ConnectionStatus status) => RaiseStatusChanged(status); @@ -232,6 +236,43 @@ public async Task ConnectAsync_RaisesStatusChangedConnecting() Assert.Contains(ConnectionStatus.Connecting, statuses); client.Dispose(); } + + [Fact] + public async Task ConnectAsync_WhenConnectionFails_StartsReconnectLoop() + { + var client = new TestWebSocketClient("ws://127.0.0.1:1", "token", _logger); + var statuses = new List(); + client.StatusChanged += (_, s) => statuses.Add(s); + + await client.ConnectAsync(); + await Task.Delay(150); + + Assert.Contains(ConnectionStatus.Error, statuses); + Assert.True(statuses.Count(s => s == ConnectionStatus.Connecting) >= 2); + Assert.Contains(_logger.Logs, line => line.Contains("reconnecting in 1000ms", StringComparison.OrdinalIgnoreCase)); + + client.Dispose(); + } + + [Fact] + public async Task ConnectAsync_WhenAutoReconnectDisabled_DoesNotStartReconnectLoop() + { + var client = new TestWebSocketClient("ws://127.0.0.1:1", "token", _logger) + { + AutoReconnectEnabled = false + }; + var statuses = new List(); + client.StatusChanged += (_, s) => statuses.Add(s); + + await client.ConnectAsync(); + await Task.Delay(150); + + Assert.Contains(ConnectionStatus.Error, statuses); + Assert.Single(statuses, s => s == ConnectionStatus.Connecting); + Assert.DoesNotContain(_logger.Logs, line => line.Contains("reconnecting in", StringComparison.OrdinalIgnoreCase)); + + client.Dispose(); + } } public class TestLogger : IOpenClawLogger