"""Tests for Nous fallback local-availability suppression. Blocker if Nous token material is missing locally: the fallback chain should not repeatedly attempt Nous resolution; it must skip and continue to the next provider. """ from __future__ import annotations from unittest.mock import patch from run_agent import AIAgent def _make_agent(fallback_model=None): with ( patch("run_agent.get_tool_definitions", return_value=[]), patch("run_agent.check_toolset_requirements", return_value={}), patch("run_agent.OpenAI"), ): agent = AIAgent( api_key="test-key", provider="openai-codex", base_url="https://chatgpt.com/backend-api/codex", quiet_mode=True, skip_context_files=True, skip_memory=True, fallback_model=fallback_model, ) agent.client = None return agent def _mock_client(base_url="https://chatgpt.com/backend-api/codex", api_key="fb-key"): mock = type("Client", (), {})() mock.base_url = base_url mock.api_key = api_key mock.chat = type("Chat", (), {})() mock.chat.completions = type("Completions", (), {})() mock.chat.completions.create = lambda *args, **kwargs: None return mock class TestNousFallbackLocalAvailability: def test_missing_nous_token_is_skipped_once(self): """Nous fallback is skipped when no access/refresh token is stored.""" agent = _make_agent( fallback_model=[ {"provider": "nous", "model": "anthropic/claude-sonnet-4.6"}, {"provider": "openai-codex", "model": "gpt-5.5"}, ] ) with patch( "hermes_cli.auth.get_provider_auth_state", return_value={}, ), patch( "agent.auxiliary_client.resolve_provider_client", return_value=(_mock_client(api_key="fb"), "gpt-5.5"), ): activated = agent._try_activate_fallback(None) assert activated is True assert agent.model == "gpt-5.5" def test_nous_unavailable_not_retried_in_same_session(self): """After Nous is skipped once, subsequent activations continue further.""" agent = _make_agent( fallback_model=[ {"provider": "nous", "model": "anthropic/claude-sonnet-4.6"}, {"provider": "openai-codex", "model": "gpt-5.5"}, ] ) with patch( "hermes_cli.auth.get_provider_auth_state", return_value={}, ): agent._try_activate_fallback(None) key = ( "nous", "anthropic/claude-sonnet-4.6", "", ) assert key in getattr(agent, "_unavailable_fallback_keys", set()) def test_present_nous_token_allows_activation(self): """Nous is considered when token material exists.""" agent = _make_agent( fallback_model=[ {"provider": "nous", "model": "anthropic/claude-sonnet-4.6"}, {"provider": "openai-codex", "model": "gpt-5.5"}, ] ) with patch( "hermes_cli.auth.get_provider_auth_state", return_value={"access_token": "abc", "refresh_token": "xyz"}, ), patch( "agent.auxiliary_client.resolve_provider_client", return_value=(_mock_client(api_key="fb"), "anthropic/claude-sonnet-4.6"), ): activated = agent._try_activate_fallback(None) assert activated is True assert agent.provider == "nous"