Spaces:
Paused
Paused
| import os | |
| import sys | |
| import time | |
| from datetime import datetime, timedelta | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| # Add project root to the Python path to resolve module imports | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) | |
| import pytest | |
| from browser_utils.auth_rotation import perform_auth_rotation | |
| from config.global_state import GlobalState | |
| # Mark the entire module as async | |
| pytestmark = pytest.mark.asyncio | |
| def setup_teardown(): | |
| """Set up and tear down global state for each test.""" | |
| from browser_utils import auth_rotation | |
| GlobalState.AUTH_ROTATION_LOCK.set() | |
| GlobalState.DEPLOYMENT_EMERGENCY_MODE = False | |
| GlobalState.reset_quota_status() | |
| auth_rotation._ROTATION_TIMESTAMPS.clear() | |
| yield | |
| GlobalState.reset_quota_status() | |
| GlobalState.AUTH_ROTATION_LOCK.set() | |
| auth_rotation._ROTATION_TIMESTAMPS.clear() | |
| async def test_perform_auth_rotation_waits_for_cooldown(): | |
| """ | |
| Test that perform_auth_rotation waits for the correct duration when all profiles are on cooldown. | |
| """ | |
| cooldown_duration = 2 | |
| start_time = time.time() | |
| # Set up cooldown profiles | |
| mock_cooldown_profiles = { | |
| "/path/to/profile1.json": { | |
| "global": (datetime.now() + timedelta(seconds=10)).timestamp() | |
| }, | |
| "/path/to/profile2.json": { | |
| "global": ( | |
| datetime.now() + timedelta(seconds=cooldown_duration) | |
| ).timestamp() | |
| }, | |
| } | |
| # Track calls to _get_next_profile | |
| call_count = 0 | |
| def mock_get_next_profile(*args, **kwargs): | |
| nonlocal call_count | |
| call_count += 1 | |
| if call_count == 1: | |
| # First call: no profiles available | |
| return None | |
| else: | |
| # Subsequent calls: profile becomes available after cooldown | |
| return "/path/to/profile2.json" | |
| # Set up mocks | |
| mock_canary = AsyncMock(return_value=True) | |
| mock_page = MagicMock() | |
| mock_page.is_closed.return_value = False | |
| mock_context = MagicMock() | |
| mock_context.clear_cookies = AsyncMock() | |
| mock_context.add_cookies = AsyncMock() | |
| mock_page.context = mock_context | |
| # Mock other dependencies | |
| with ( | |
| patch( | |
| "browser_utils.auth_rotation._get_next_profile", | |
| side_effect=mock_get_next_profile, | |
| ), | |
| patch("browser_utils.auth_rotation._COOLDOWN_PROFILES", mock_cooldown_profiles), | |
| patch("browser_utils.auth_rotation._perform_canary_test", mock_canary), | |
| patch("browser_utils.auth_rotation.save_cooldown_profiles"), | |
| patch( | |
| "browser_utils.auth_rotation.GlobalState.last_error_type", "QUOTA_EXCEEDED" | |
| ), | |
| patch("browser_utils.auth_rotation.state.page_instance", mock_page), | |
| patch( | |
| "browser_utils.auth_rotation.state.current_auth_profile_path", | |
| "/path/to/old_profile.json", | |
| ), | |
| patch("browser_utils.auth_rotation.QUOTA_EXCEEDED_COOLDOWN_SECONDS", 30), | |
| patch("browser_utils.auth_rotation.RATE_LIMIT_COOLDOWN_SECONDS", 10), | |
| patch("builtins.open") as mock_open, | |
| patch("os.path.exists", return_value=True), | |
| ): | |
| # Configure file opening to return valid profile data | |
| mock_file = MagicMock() | |
| mock_file.read.return_value = '{"cookies": []}' | |
| mock_open.return_value.__enter__.return_value = mock_file | |
| # Run the function | |
| result = await perform_auth_rotation() | |
| # Verify the result | |
| end_time = time.time() | |
| execution_time = end_time - start_time | |
| # Assertions | |
| assert result is True, "perform_auth_rotation should return True after waiting" | |
| assert execution_time >= cooldown_duration, ( | |
| f"Expected wait time >= {cooldown_duration}s, got {execution_time:.2f}s" | |
| ) | |
| assert call_count >= 2, ( | |
| f"Expected at least 2 calls to _get_next_profile, got {call_count}" | |
| ) | |
| # Verify that the mock was called and waiting occurred | |
| assert mock_canary.called, "Canary test should have been called" | |