feat(auth) normalise the way in which we check whether a user has free/paid access to nous portal so we can expose behaviour and error messages accordingly.

This commit is contained in:
Robin Fernandes
2026-05-28 00:19:31 -07:00
committed by Teknium
parent 0bf9b867cf
commit 406901b27d
32 changed files with 2470 additions and 181 deletions
@@ -9,6 +9,8 @@ from unittest.mock import patch
import pytest
from hermes_cli.nous_account import NousPortalAccountInfo
REPO_ROOT = Path(__file__).resolve().parents[2]
TOOLS_DIR = REPO_ROOT / "tools"
@@ -69,10 +71,17 @@ def _enable_managed_nous_tools(monkeypatch):
The _install_fake_tools_package() helper resets and reimports tool modules,
so a simple monkeypatch on tool_backend_helpers doesn't survive. We patch
the *source* modules that the reimported modules will import from — both
hermes_cli.auth and hermes_cli.models — so the function body returns True.
hermes_cli.nous_account — so the function body returns True.
"""
monkeypatch.setattr("hermes_cli.auth.get_nous_auth_status", lambda: {"logged_in": True})
monkeypatch.setattr("hermes_cli.models.check_nous_free_tier", lambda: False)
monkeypatch.setattr(
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda: NousPortalAccountInfo(
logged_in=True,
source="jwt",
fresh=False,
paid_service_access=True,
),
)
def _install_fake_tools_package():
+11 -2
View File
@@ -5,6 +5,8 @@ from pathlib import Path
import pytest
from hermes_cli.nous_account import NousPortalAccountInfo
TOOLS_DIR = Path(__file__).resolve().parents[2] / "tools"
@@ -48,8 +50,15 @@ def _restore_tool_and_agent_modules():
def _enable_managed_nous_tools(monkeypatch):
"""Patch the source modules so managed_nous_tools_enabled() returns True
even after tool modules are dynamically reloaded."""
monkeypatch.setattr("hermes_cli.auth.get_nous_auth_status", lambda: {"logged_in": True})
monkeypatch.setattr("hermes_cli.models.check_nous_free_tier", lambda: False)
monkeypatch.setattr(
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda: NousPortalAccountInfo(
logged_in=True,
source="jwt",
fresh=False,
paid_service_access=True,
),
)
def _install_fake_tools_package():
+48 -15
View File
@@ -16,10 +16,12 @@ from unittest.mock import patch
import pytest
from hermes_cli.nous_account import NousPaidServiceAccessInfo, NousPortalAccountInfo
from tools.tool_backend_helpers import (
coerce_modal_mode,
has_direct_modal_credentials,
managed_nous_tools_enabled,
nous_tool_gateway_unavailable_message,
normalize_browser_cloud_provider,
normalize_modal_mode,
prefers_gateway,
@@ -40,42 +42,73 @@ class TestManagedNousToolsEnabled:
def test_disabled_when_not_logged_in(self, monkeypatch):
monkeypatch.setattr(
"hermes_cli.auth.get_nous_auth_status",
lambda: {},
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda: NousPortalAccountInfo(logged_in=False, source="none", fresh=False),
)
assert managed_nous_tools_enabled() is False
def test_disabled_for_free_tier(self, monkeypatch):
monkeypatch.setattr(
"hermes_cli.auth.get_nous_auth_status",
lambda: {"logged_in": True},
)
monkeypatch.setattr(
"hermes_cli.models.check_nous_free_tier",
lambda: True,
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda: NousPortalAccountInfo(
logged_in=True,
source="jwt",
fresh=False,
paid_service_access=False,
),
)
assert managed_nous_tools_enabled() is False
def test_enabled_for_paid_subscriber(self, monkeypatch):
monkeypatch.setattr(
"hermes_cli.auth.get_nous_auth_status",
lambda: {"logged_in": True},
)
monkeypatch.setattr(
"hermes_cli.models.check_nous_free_tier",
lambda: False,
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda: NousPortalAccountInfo(
logged_in=True,
source="jwt",
fresh=False,
paid_service_access=True,
),
)
assert managed_nous_tools_enabled() is True
def test_returns_false_on_exception(self, monkeypatch):
"""Should never crash — returns False on any exception."""
monkeypatch.setattr(
"hermes_cli.auth.get_nous_auth_status",
"hermes_cli.nous_account.get_nous_portal_account_info",
_raise_import,
)
assert managed_nous_tools_enabled() is False
class TestNousToolGatewayUnavailableMessage:
def test_uses_entitlement_reason_for_logged_in_user(self, monkeypatch):
monkeypatch.setattr(
"hermes_cli.nous_account.get_nous_portal_account_info",
lambda force_fresh=False: NousPortalAccountInfo(
logged_in=True,
source="account_api",
fresh=True,
paid_service_access=False,
portal_base_url="https://portal.example.test",
paid_service_access_info=NousPaidServiceAccessInfo(
allowed=False,
reason="no_usable_credits",
has_active_subscription=True,
active_subscription_is_paid=True,
subscription_credits_remaining=0,
purchased_credits_remaining=0,
total_usable_credits=0,
),
),
)
message = nous_tool_gateway_unavailable_message("managed image generation")
assert "credits are exhausted" in message
assert "managed image generation" in message
assert "https://portal.example.test/billing" in message
# ---------------------------------------------------------------------------
# normalize_browser_cloud_provider
# ---------------------------------------------------------------------------