From 27f4dba5ceef6e93597d8767d3f745bd9ecbdd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=B3=A5=E8=B1=86?= <1243352777@qq.com> Date: Sun, 26 Apr 2026 14:54:48 +0800 Subject: [PATCH] test: add unit tests for credential pool env fallback --- .../test_credential_pool_env_fallback.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tests/tools/test_credential_pool_env_fallback.py diff --git a/tests/tools/test_credential_pool_env_fallback.py b/tests/tools/test_credential_pool_env_fallback.py new file mode 100644 index 0000000000..bd88a0de99 --- /dev/null +++ b/tests/tools/test_credential_pool_env_fallback.py @@ -0,0 +1,110 @@ +"""Tests for credential_pool .env fallback and auth credential pool lookup.""" + +import os +import pytest +from unittest.mock import patch, MagicMock + + +def _make_pconfig(env_vars=None): + """Create a minimal ProviderConfig for testing.""" + from hermes_cli.auth import ProviderConfig + return ProviderConfig( + id="openai", + name="OpenAI", + auth_type="api_key", + api_key_env_vars=tuple(env_vars or ["OPENAI_API_KEY"]), + ) + + +class TestCredentialPoolEnvFallback: + """Verify _seed_from_env resolves keys from both os.environ and .env file.""" + + def test_os_environ_still_works(self): + """Existing os.environ resolution must not break. + _seed_from_env only collects env var names, does not return found=True + for existing keys — that is _resolve's job. Just verify no crash.""" + from agent.credential_pool import _seed_from_env + # Should not raise + found, entries = _seed_from_env("openai", []) + + def test_get_env_value_import_does_not_crash(self): + """Importing get_env_value from hermes_cli.config should not raise.""" + try: + from hermes_cli.config import get_env_value + assert callable(get_env_value) + except ImportError: + pytest.skip("hermes_cli.config not available in test environment") + + +class TestAuthCredentialPoolFallback: + """Verify auth.py falls back to credential pool when env vars are empty.""" + + def _clear_api_keys(self): + """Temporarily clear API key env vars, return backup dict.""" + backup = {} + for key in ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "OPENROUTER_API_KEY", + "ZAI_API_KEY", "DEEPSEEK_API_KEY"]: + if key in os.environ: + backup[key] = os.environ.pop(key) + return backup + + def test_credential_pool_fallback_structure(self): + """When no env var is set, auth should try credential pool.""" + from hermes_cli.auth import _resolve_api_key_provider_secret + + mock_entry = MagicMock() + mock_entry.access_token = "test-pool-key-12345" + mock_entry.runtime_api_key = "" + + mock_pool = MagicMock() + mock_pool.has_credentials.return_value = True + mock_pool.peek.return_value = mock_entry + + backup = self._clear_api_keys() + try: + with patch("agent.credential_pool.load_pool", return_value=mock_pool): + key, source = _resolve_api_key_provider_secret( + provider_id="openai", + pconfig=_make_pconfig(), + ) + assert "test-pool-key-12345" in key + assert "credential_pool" in source + finally: + os.environ.update(backup) + + def test_credential_pool_empty_returns_empty(self): + """When pool is empty, return empty string.""" + from hermes_cli.auth import _resolve_api_key_provider_secret + + mock_pool = MagicMock() + mock_pool.has_credentials.return_value = False + + backup = self._clear_api_keys() + try: + with patch("agent.credential_pool.load_pool", return_value=mock_pool): + key, source = _resolve_api_key_provider_secret( + provider_id="openai", + pconfig=_make_pconfig(), + ) + assert key == "" + finally: + os.environ.update(backup) + + def test_env_var_takes_priority_over_pool(self): + """Env vars should be checked before credential pool.""" + from hermes_cli.auth import _resolve_api_key_provider_secret + + mock_pool = MagicMock() + mock_pool.has_credentials.return_value = True + + with patch.dict(os.environ, {"OPENAI_API_KEY": "sk-env-key-first-abc123"}): + with patch("agent.credential_pool.load_pool", return_value=mock_pool): + key, source = _resolve_api_key_provider_secret( + provider_id="openai", + pconfig=_make_pconfig(), + ) + assert key == "sk-env-key-first-abc123" + # Source is the env var name itself (e.g. "OPENAI_API_KEY") + assert "OPENAI_API_KEY" in source + # Pool peek should NOT have been called — env var found first + mock_pool.peek.assert_not_called()