From 9868d9f63f3c537a8535341ff084f06e5389796b Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:25:47 +0530 Subject: [PATCH] test(auth): fold the edge-block refresh case into the 503 matrix and pin the deadline clamp --- tests/hermes_cli/test_auth_nous_provider.py | 75 ++++++------------- .../test_device_poll_waf_backoff.py | 10 ++- 2 files changed, 29 insertions(+), 56 deletions(-) diff --git a/tests/hermes_cli/test_auth_nous_provider.py b/tests/hermes_cli/test_auth_nous_provider.py index 17fa8162f8..51d0c1f7cd 100644 --- a/tests/hermes_cli/test_auth_nous_provider.py +++ b/tests/hermes_cli/test_auth_nous_provider.py @@ -748,58 +748,21 @@ def test_refresh_token_exchange_error_classification( assert exc_info.value.retry_after == 30.0 -@pytest.mark.parametrize("status_code", [403, 429]) -def test_runtime_refresh_edge_block_preserves_nous_oauth_credentials( - tmp_path, monkeypatch, status_code +@pytest.mark.parametrize( + ("status_code", "headers", "json_body", "expected_code"), + [ + (503, {}, {}, "temporarily_unavailable"), + (403, {"x-vercel-mitigated": "deny"}, None, "upstream_blocked"), + (429, {"x-vercel-mitigated": "challenge"}, None, "upstream_blocked"), + ], + ids=["portal-503", "edge-deny-403", "edge-challenge-429"], +) +def test_runtime_refresh_503_preserves_nous_oauth_credentials( + tmp_path, monkeypatch, status_code, headers, json_body, expected_code ): - """A Vercel Security Checkpoint deny/challenge on the token endpoint must not quarantine the - still-valid refresh token or tell the user to re-login (#120602).""" - import hermes_cli.auth as auth_mod - import hermes_cli.auth_nous as auth_nous - - hermes_home = tmp_path / "hermes" - access_token = _invoke_jwt(seconds=3600) - refresh_token = "refresh-still-valid" - _setup_nous_auth( - hermes_home, access_token=access_token, refresh_token=refresh_token, - expires_at=_future_iso(3600), expires_in=3600) - monkeypatch.setenv("HERMES_HOME", str(hermes_home)) - - class _FakeResponse: - headers = {"x-vercel-mitigated": "deny" if status_code == 403 else "challenge"} - - def __init__(self): - self.status_code = status_code - - def json(self): - raise ValueError("Forbidden is not JSON") - - class _FakeClient: - def __enter__(self): - return self - - def __exit__(self, *args): - return False - - def post(self, *args, **kwargs): - return _FakeResponse() - - monkeypatch.setattr(auth_nous, "_nous_http_client", lambda *args: _FakeClient()) - - with pytest.raises(AuthError) as exc_info: - auth_mod.resolve_nous_runtime_credentials(force_refresh=True) - - state = auth_mod.get_provider_auth_state("nous") - assert state["access_token"] == access_token - assert state["refresh_token"] == refresh_token - assert "last_auth_error" not in state - assert exc_info.value.code == "upstream_blocked" - assert exc_info.value.relogin_required is False - assert exc_info.value.retryable is True - - -def test_runtime_refresh_503_preserves_nous_oauth_credentials(tmp_path, monkeypatch): - """The real runtime resolver must not quarantine credentials during a Portal outage (#120976).""" + """The real runtime resolver must not quarantine a still-valid refresh token or demand a + re-login during a Portal outage (#120976) or a Vercel Security Checkpoint deny/challenge on + the token endpoint (#120602).""" import hermes_cli.auth as auth_mod import hermes_cli.auth_nous as auth_nous @@ -816,10 +779,14 @@ def test_runtime_refresh_503_preserves_nous_oauth_credentials(tmp_path, monkeypa monkeypatch.setenv("HERMES_HOME", str(hermes_home)) class _FakeResponse: - status_code = 503 + def __init__(self): + self.status_code = status_code + self.headers = headers def json(self): - return {} + if json_body is None: + raise ValueError("edge block page is not JSON") + return json_body class _FakeClient: def __enter__(self): @@ -840,7 +807,7 @@ def test_runtime_refresh_503_preserves_nous_oauth_credentials(tmp_path, monkeypa assert state["access_token"] == access_token assert state["refresh_token"] == refresh_token assert "last_auth_error" not in state - assert exc_info.value.code == "temporarily_unavailable" + assert exc_info.value.code == expected_code assert exc_info.value.relogin_required is False assert exc_info.value.retryable is True diff --git a/tests/hermes_cli/test_device_poll_waf_backoff.py b/tests/hermes_cli/test_device_poll_waf_backoff.py index 83b49f62ad..f86ebaf29c 100644 --- a/tests/hermes_cli/test_device_poll_waf_backoff.py +++ b/tests/hermes_cli/test_device_poll_waf_backoff.py @@ -81,6 +81,12 @@ def test_persistent_block_ends_at_deadline_without_oversleeping(monkeypatch): sleeps = _fake_clock(monkeypatch) with pytest.raises(TimeoutError, match="device code expired"): - _poll(lambda: _non_json(429, headers={"retry-after": "3600"}), expires_in=300) + # 290 is not a multiple of the 60s backoff cap, so only the deadline clamp keeps the + # last sleep from overshooting the device-code expiry. + _poll(lambda: _non_json(429, headers={"retry-after": "3600"}), expires_in=290) - assert sum(sleeps) <= 300 + assert sum(sleeps) <= 290 + + # A 403 without x-vercel-mitigated is a real rejection, not edge mitigation: fail fast. + with pytest.raises(httpx.HTTPStatusError): + _poll(_post_returning(_non_json(403)))