test: add terminal auth-failure coverage for PR #206 fix
Tests for the auth-failure backoff and terminal error detection added in PR #206 (fix: stop aggressive retry on auth failures): - HandleRequestError_TerminalAuthError_SetsAuthFailedFlag (theory): token mismatch / origin not allowed / too many failed → _authFailed set - HandleRequestError_TerminalAuthError_RaisesAuthenticationFailedEvent: AuthenticationFailed event fired with message text - HandleRequestError_TerminalAuthError_RaisesErrorStatus: ConnectionStatus.Error raised on terminal auth error - HandleRequestError_TerminalAuthError_OnNonConnectMethod_DoesNotSetAuthFailed: terminal-auth guard is scoped to 'connect' method only - HandleHelloOk_AfterAuthFailed_ClearsAuthFailedFlag: hello-ok response resets the flag so reconnect is re-enabled - HandleRequestError_AllDeviceSignatureModesExhausted_SetsAuthFailed: cycling through all 4 signature modes fires AuthenticationFailed once 660 Shared pass (+8), 20 skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
8283eaa794
commit
7c69a2bea5
@ -288,6 +288,16 @@ public class OpenClawGatewayClientTests
|
||||
_client.StatusChanged += (_, s) => changes.Add(s);
|
||||
return changes;
|
||||
}
|
||||
|
||||
public bool GetAuthFailedFlag() =>
|
||||
GetPrivateField<bool>("_authFailed");
|
||||
|
||||
public List<string> CaptureAuthenticationFailedEvents()
|
||||
{
|
||||
var events = new List<string>();
|
||||
_client.AuthenticationFailed += (_, msg) => events.Add(msg);
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLogger : IOpenClawLogger
|
||||
@ -1626,4 +1636,142 @@ public class OpenClawGatewayClientTests
|
||||
var flags = helper.GetUnsupportedMethodFlags();
|
||||
Assert.True(flags.NodeList);
|
||||
}
|
||||
|
||||
// --- HandleRequestError: terminal auth errors (PR #206 fix) ---
|
||||
|
||||
[Theory]
|
||||
[InlineData("token mismatch")]
|
||||
[InlineData("origin not allowed")]
|
||||
[InlineData("too many failed attempts")]
|
||||
public void HandleRequestError_TerminalAuthError_SetsAuthFailedFlag(string errorMessage)
|
||||
{
|
||||
var helper = new GatewayClientTestHelper();
|
||||
helper.TrackPendingRequest("req-auth-1", "connect");
|
||||
|
||||
helper.ProcessRawMessage($$"""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-auth-1",
|
||||
"ok": false,
|
||||
"error": "{{errorMessage}}"
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.True(helper.GetAuthFailedFlag());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandleRequestError_TerminalAuthError_RaisesAuthenticationFailedEvent()
|
||||
{
|
||||
var helper = new GatewayClientTestHelper();
|
||||
var authEvents = helper.CaptureAuthenticationFailedEvents();
|
||||
helper.TrackPendingRequest("req-auth-2", "connect");
|
||||
|
||||
helper.ProcessRawMessage("""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-auth-2",
|
||||
"ok": false,
|
||||
"error": "token mismatch — reconnect rejected"
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.Single(authEvents);
|
||||
Assert.Contains("token mismatch", authEvents[0], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandleRequestError_TerminalAuthError_RaisesErrorStatus()
|
||||
{
|
||||
var helper = new GatewayClientTestHelper();
|
||||
var statusChanges = helper.CaptureStatusChanges();
|
||||
helper.TrackPendingRequest("req-auth-3", "connect");
|
||||
|
||||
helper.ProcessRawMessage("""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-auth-3",
|
||||
"ok": false,
|
||||
"error": "origin not allowed"
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.Contains(ConnectionStatus.Error, statusChanges);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandleRequestError_TerminalAuthError_OnNonConnectMethod_DoesNotSetAuthFailed()
|
||||
{
|
||||
// Terminal auth check only applies to "connect" method — other methods must not set the flag
|
||||
var helper = new GatewayClientTestHelper();
|
||||
helper.TrackPendingRequest("req-auth-4", "sessions.list");
|
||||
|
||||
helper.ProcessRawMessage("""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-auth-4",
|
||||
"ok": false,
|
||||
"error": "token mismatch"
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.False(helper.GetAuthFailedFlag());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandleHelloOk_AfterAuthFailed_ClearsAuthFailedFlag()
|
||||
{
|
||||
var helper = new GatewayClientTestHelper();
|
||||
|
||||
// First, trigger auth failure
|
||||
helper.TrackPendingRequest("req-auth-5", "connect");
|
||||
helper.ProcessRawMessage("""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-auth-5",
|
||||
"ok": false,
|
||||
"error": "token mismatch"
|
||||
}
|
||||
""");
|
||||
Assert.True(helper.GetAuthFailedFlag());
|
||||
|
||||
// Now receive hello-ok — flag must be cleared
|
||||
helper.ProcessRawMessage("""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-hello-1",
|
||||
"payload": {
|
||||
"type": "hello-ok"
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.False(helper.GetAuthFailedFlag());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandleRequestError_AllDeviceSignatureModesExhausted_SetsAuthFailed()
|
||||
{
|
||||
var logger = new TestLogger();
|
||||
var helper = new GatewayClientTestHelper(logger);
|
||||
var authEvents = helper.CaptureAuthenticationFailedEvents();
|
||||
|
||||
// Cycle through all 4 signature modes by sending 4 successive rejections
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
helper.TrackPendingRequest($"req-sig-exhaust-{i}", "connect");
|
||||
helper.ProcessRawMessage($$"""
|
||||
{
|
||||
"type": "res",
|
||||
"id": "req-sig-exhaust-{{i}}",
|
||||
"ok": false,
|
||||
"error": "device signature invalid"
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
Assert.True(helper.GetAuthFailedFlag());
|
||||
Assert.Single(authEvents);
|
||||
Assert.Contains("device signature", authEvents[0], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user