AIstudioProxyAPI / tests /api_utils /test_auth_manager.py
peijun1's picture
Deploy AI Studio Proxy API to Hugging Face Spaces
a5784e9
Raw
History Blame Contribute Delete
5.4 kB
import os
from unittest.mock import AsyncMock, patch
import pytest
from api_utils.auth_manager import AuthManager, auth_manager
# --- Fixtures ---
@pytest.fixture
def manager():
return AuthManager()
@pytest.fixture
def mock_saved_auth_dir(tmp_path):
"""Mock the SAVED_AUTH_DIR constant."""
with patch("api_utils.auth_manager.SAVED_AUTH_DIR", str(tmp_path)):
yield tmp_path
# --- Tests ---
def test_init_default():
"""Test initialization without environment variable."""
with patch.dict(os.environ, {}, clear=True):
am = AuthManager()
assert am.current_profile is None
assert am.failed_profiles == set()
def test_init_with_env_var():
"""Test initialization with ACTIVE_AUTH_JSON_PATH."""
with patch.dict(os.environ, {"ACTIVE_AUTH_JSON_PATH": "/path/to/auth.json"}):
am = AuthManager()
assert am.current_profile == "/path/to/auth.json"
@pytest.mark.asyncio
async def test_get_available_profiles_no_dir(manager):
"""Test get_available_profiles when directory doesn't exist."""
with patch("os.path.exists", return_value=False):
profiles = await manager.get_available_profiles()
assert profiles == []
@pytest.mark.asyncio
async def test_get_available_profiles_success(manager, mock_saved_auth_dir):
"""Test get_available_profiles with existing files."""
# Create dummy auth files
(mock_saved_auth_dir / "auth1.json").touch()
(mock_saved_auth_dir / "auth2.json").touch()
with patch("os.path.exists", return_value=True):
profiles = await manager.get_available_profiles()
assert len(profiles) == 2
assert any("auth1.json" in p for p in profiles)
assert any("auth2.json" in p for p in profiles)
# Check sorting
assert profiles == sorted(profiles)
@pytest.mark.asyncio
async def test_get_next_profile_success(manager):
"""Test getting next profile successfully."""
mock_profiles = ["/dir/auth1.json", "/dir/auth2.json"]
with patch.object(
manager, "get_available_profiles", new_callable=AsyncMock
) as mock_get:
mock_get.return_value = mock_profiles
# First call
next_p = await manager.get_next_profile()
assert next_p == "/dir/auth1.json"
assert manager.current_profile == "/dir/auth1.json"
@pytest.mark.asyncio
async def test_get_next_profile_skips_failed(manager):
"""Test that failed profiles are skipped."""
mock_profiles = ["/dir/auth1.json", "/dir/auth2.json", "/dir/auth3.json"]
manager.failed_profiles.add("/dir/auth1.json")
with patch.object(
manager, "get_available_profiles", new_callable=AsyncMock
) as mock_get:
mock_get.return_value = mock_profiles
next_p = await manager.get_next_profile()
assert next_p == "/dir/auth2.json"
# Mark auth2 as failed and try again
manager.mark_profile_failed() # marks current (auth2)
next_p_2 = await manager.get_next_profile()
assert next_p_2 == "/dir/auth3.json"
@pytest.mark.asyncio
async def test_get_next_profile_skips_current(manager):
"""Test that current profile is skipped even if not failed (to ensure rotation if needed, or just behavior check)."""
# Actually logic says: os.path.basename(p) != current_basename
# So if we call get_next_profile, it should give us a *different* one if available.
mock_profiles = ["/dir/auth1.json", "/dir/auth2.json"]
manager.current_profile = "/dir/auth1.json"
with patch.object(
manager, "get_available_profiles", new_callable=AsyncMock
) as mock_get:
mock_get.return_value = mock_profiles
next_p = await manager.get_next_profile()
assert next_p == "/dir/auth2.json"
@pytest.mark.asyncio
async def test_get_next_profile_exhausted(manager):
"""Test raising RuntimeError when no profiles available."""
with patch.object(
manager, "get_available_profiles", new_callable=AsyncMock
) as mock_get:
mock_get.return_value = []
with pytest.raises(RuntimeError, match="All authentication profiles exhausted"):
await manager.get_next_profile()
@pytest.mark.asyncio
async def test_get_next_profile_all_failed(manager):
"""Test raising RuntimeError when all profiles failed."""
mock_profiles = ["/dir/auth1.json"]
manager.failed_profiles.add("/dir/auth1.json")
with patch.object(
manager, "get_available_profiles", new_callable=AsyncMock
) as mock_get:
mock_get.return_value = mock_profiles
with pytest.raises(RuntimeError, match="All authentication profiles exhausted"):
await manager.get_next_profile()
def test_mark_profile_failed(manager):
"""Test marking profile as failed."""
# 1. With explicit path
manager.mark_profile_failed("/dir/auth1.json")
assert "/dir/auth1.json" in manager.failed_profiles
# 2. With current profile
manager.current_profile = "/dir/auth2.json"
manager.mark_profile_failed()
assert "/dir/auth2.json" in manager.failed_profiles
# 3. No profile active and no arg
manager.current_profile = None
manager.mark_profile_failed() # Should log warning but not crash
# (Assert log if needed, but no crash is enough for basic coverage)
def test_global_instance():
"""Ensure global instance exists."""
assert isinstance(auth_manager, AuthManager)