fix(desktop): route global remote profile REST calls (#47011)
* fix(desktop): route global remote profile REST calls * fix(dashboard): scope oauth provider routes by profile * test(tui): isolate notification poller queue
This commit is contained in:
@@ -34,6 +34,13 @@ client = TestClient(app)
|
||||
HEADERS = {"X-Hermes-Session-Token": _SESSION_TOKEN}
|
||||
|
||||
|
||||
def _make_profile_home(tmp_path, monkeypatch, profile="coder"):
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
profile_home = tmp_path / "profiles" / profile
|
||||
profile_home.mkdir(parents=True)
|
||||
return profile_home
|
||||
|
||||
|
||||
def _fake_nous_device_data():
|
||||
return {
|
||||
"device_code": "device-code",
|
||||
@@ -127,6 +134,67 @@ def test_nous_dashboard_device_flow_ignores_legacy_scope_override(monkeypatch):
|
||||
ws._oauth_sessions.pop(result["session_id"], None)
|
||||
|
||||
|
||||
def test_oauth_provider_status_uses_profile_query(tmp_path, monkeypatch):
|
||||
from hermes_cli import web_server as ws
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
profile_home = _make_profile_home(tmp_path, monkeypatch)
|
||||
observed_homes = []
|
||||
|
||||
def fake_status():
|
||||
observed_homes.append(get_hermes_home())
|
||||
return {"logged_in": False, "source": None}
|
||||
|
||||
fake_catalog = ({
|
||||
"id": "fake-oauth",
|
||||
"name": "Fake OAuth",
|
||||
"flow": "pkce",
|
||||
"cli_command": "hermes auth add fake-oauth",
|
||||
"docs_url": "https://example.com",
|
||||
"status_fn": fake_status,
|
||||
},)
|
||||
monkeypatch.setattr(ws, "_OAUTH_PROVIDER_CATALOG", fake_catalog)
|
||||
|
||||
resp = client.get("/api/providers/oauth?profile=coder", headers=HEADERS)
|
||||
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert observed_homes == [profile_home]
|
||||
|
||||
|
||||
def test_oauth_start_stores_profile_for_background_completion(tmp_path, monkeypatch):
|
||||
from hermes_cli import web_server as ws
|
||||
|
||||
_make_profile_home(tmp_path, monkeypatch)
|
||||
fake_user_code_resp = {
|
||||
"user_code": "ABCD-1234",
|
||||
"verification_uri": "https://api.minimax.io/oauth/verify",
|
||||
"expired_in": 600,
|
||||
"interval": 2000,
|
||||
"state": "stub-state",
|
||||
}
|
||||
with patch(
|
||||
"hermes_cli.auth._minimax_request_user_code",
|
||||
return_value=fake_user_code_resp,
|
||||
), patch(
|
||||
"hermes_cli.auth._minimax_pkce_pair",
|
||||
return_value=("verifier-stub", "challenge-stub", "stub-state"),
|
||||
), patch(
|
||||
"hermes_cli.web_server._minimax_poller",
|
||||
return_value=None,
|
||||
):
|
||||
resp = client.post(
|
||||
"/api/providers/oauth/minimax-oauth/start?profile=coder",
|
||||
headers=HEADERS,
|
||||
)
|
||||
|
||||
assert resp.status_code == 200, resp.text
|
||||
session_id = resp.json()["session_id"]
|
||||
try:
|
||||
assert ws._oauth_sessions[session_id]["profile"] == "coder"
|
||||
finally:
|
||||
ws._oauth_sessions.pop(session_id, None)
|
||||
|
||||
|
||||
def test_nous_dashboard_device_flow_does_not_retry_legacy_scope_on_invoke_refusal(monkeypatch):
|
||||
from hermes_cli import auth as auth_mod
|
||||
from hermes_cli import web_server as ws
|
||||
@@ -207,6 +275,71 @@ def test_codex_dashboard_worker_persists_runtime_provider(tmp_path, monkeypatch)
|
||||
ws._oauth_sessions.pop(sid, None)
|
||||
|
||||
|
||||
def test_codex_dashboard_worker_persists_inside_session_profile(tmp_path, monkeypatch):
|
||||
from hermes_cli import auth as auth_mod
|
||||
from hermes_cli import web_server as ws
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
profile_home = _make_profile_home(tmp_path, monkeypatch)
|
||||
|
||||
class _Resp:
|
||||
def __init__(self, status_code, payload):
|
||||
self.status_code = status_code
|
||||
self._payload = payload
|
||||
|
||||
def json(self):
|
||||
return self._payload
|
||||
|
||||
class _Client:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
return False
|
||||
|
||||
def post(self, url, **kwargs):
|
||||
if url.endswith("/deviceauth/usercode"):
|
||||
return _Resp(200, {
|
||||
"device_auth_id": "device-auth-id",
|
||||
"interval": 3,
|
||||
"user_code": "CODEX-1234",
|
||||
})
|
||||
if url.endswith("/deviceauth/token"):
|
||||
return _Resp(200, {
|
||||
"authorization_code": "authorization-code",
|
||||
"code_verifier": "code-verifier",
|
||||
})
|
||||
return _Resp(200, {
|
||||
"access_token": "codex-access",
|
||||
"refresh_token": "codex-refresh",
|
||||
})
|
||||
|
||||
saved_homes = []
|
||||
monkeypatch.setattr(httpx, "Client", _Client)
|
||||
monkeypatch.setattr(ws.time, "sleep", lambda _: None)
|
||||
monkeypatch.setattr(
|
||||
auth_mod,
|
||||
"_save_codex_tokens",
|
||||
lambda tokens: saved_homes.append(get_hermes_home()),
|
||||
)
|
||||
|
||||
sid, _ = ws._new_oauth_session(
|
||||
"openai-codex",
|
||||
"device_code",
|
||||
profile="coder",
|
||||
)
|
||||
try:
|
||||
ws._codex_full_login_worker(sid)
|
||||
|
||||
assert ws._oauth_sessions[sid]["status"] == "approved"
|
||||
assert saved_homes == [profile_home]
|
||||
finally:
|
||||
ws._oauth_sessions.pop(sid, None)
|
||||
|
||||
|
||||
def test_nous_dashboard_poller_preserves_effective_scope_when_token_omits_scope(monkeypatch):
|
||||
from hermes_cli import auth as auth_mod
|
||||
from hermes_cli import web_server as ws
|
||||
|
||||
Reference in New Issue
Block a user