AIstudioProxyAPI / tests /test_smart_rotation_logic.py
peijun1's picture
Deploy AI Studio Proxy API to Hugging Face Spaces
a5784e9
Raw
History Blame Contribute Delete
7.98 kB
import os
import time
import unittest
from datetime import datetime, timedelta
from unittest.mock import patch
# Import the module to test
from browser_utils import auth_rotation
class TestSmartRotationLogic(unittest.TestCase):
def setUp(self):
# Reset cooldown profiles before each test
# We need to patch the module-level variable used in the target module
self.original_cooldowns = auth_rotation._COOLDOWN_PROFILES
auth_rotation._COOLDOWN_PROFILES = {}
def tearDown(self):
auth_rotation._COOLDOWN_PROFILES = self.original_cooldowns
@patch('browser_utils.auth_rotation.glob.glob')
@patch('browser_utils.auth_rotation.os.path.exists')
@patch('browser_utils.auth_rotation.get_profile_usage')
def test_efficiency_preference(self, mock_usage, mock_exists, mock_glob):
"""
Scenario 1: The "Efficiency" Test
Profile_Partial: Cooldown on "Gemini 2.5", Valid for "Gemini 3".
Profile_Fresh: Valid for ALL.
Action: Request "Gemini 3".
Expectation: Profile_Partial MUST be selected.
"""
print("\n--- Test 1: Efficiency Preference ---")
# Setup Files - Use abspath to ensure consistency with os.path.abspath usage in actual code
profile_partial = os.path.abspath("/abs/path/profile_partial.json")
profile_fresh = os.path.abspath("/abs/path/profile_fresh.json")
# Important: Order in glob doesn't matter for the test logic, but we provide them
mock_glob.return_value = [profile_partial, profile_fresh]
mock_exists.return_value = True
# Setup Usage
# Crucial: Profile_Fresh has LOWER usage.
# Under OLD logic, Fresh wins.
# Under NEW logic, Partial (Efficiency Score 1) wins over Fresh (Efficiency Score 0).
mock_usage.side_effect = lambda p: 2000 if p == profile_partial else 1000
# Setup Cooldowns
# Partial is in cooldown for gemini-2.5-pro
future_time = datetime.now() + timedelta(hours=1)
# We modify the module's variable directly as the code uses it directly
auth_rotation._COOLDOWN_PROFILES[profile_partial] = {
"gemini-2.5-pro": future_time
}
# Action: Request Gemini 3
target_model = "gemini-3-pro-preview"
selected = auth_rotation._find_best_profile_in_dirs(["/dummy/dir"], target_model)
print(f"Selected: {selected}")
print(f"Expected: {profile_partial} (because it recycles partially exhausted profile)")
# Assertion
self.assertEqual(selected, profile_partial, "Should prefer partially exhausted profile over fresh one, even if fresh has lower usage")
@patch('browser_utils.auth_rotation.glob.glob')
@patch('browser_utils.auth_rotation.os.path.exists')
@patch('browser_utils.auth_rotation.get_profile_usage')
def test_safety_exclusion(self, mock_usage, mock_exists, mock_glob):
"""
Scenario 2: The "Safety" Test
Profile_Partial: Cooldown on "Gemini 2.5".
Profile_Fresh: Valid for ALL.
Action: Request "Gemini 2.5".
Expectation: Profile_Fresh MUST be selected.
"""
print("\n--- Test 2: Safety Exclusion ---")
profile_partial = os.path.abspath("/abs/path/profile_partial.json")
profile_fresh = os.path.abspath("/abs/path/profile_fresh.json")
mock_glob.return_value = [profile_partial, profile_fresh]
mock_exists.return_value = True
mock_usage.side_effect = lambda p: 100 # Usage equal
# Setup Cooldowns
future_time = datetime.now() + timedelta(hours=1)
auth_rotation._COOLDOWN_PROFILES[profile_partial] = {
"gemini-2.5-pro": future_time
}
# Action: Request Gemini 2.5
target_model = "gemini-2.5-pro"
selected = auth_rotation._find_best_profile_in_dirs(["/dummy/dir"], target_model)
print(f"Selected: {selected}")
print(f"Expected: {profile_fresh}")
self.assertEqual(selected, profile_fresh, "Should exclude profile in cooldown for target model")
@patch('browser_utils.auth_rotation.glob.glob')
@patch('browser_utils.auth_rotation.os.path.exists')
@patch('browser_utils.auth_rotation.get_profile_usage')
def test_wear_leveling(self, mock_usage, mock_exists, mock_glob):
"""
Scenario 3: The "Wear Leveling" Test
Profile_A: Valid, Usage 1000.
Profile_B: Valid, Usage 2000.
Action: Request any.
Expectation: Profile_A MUST be selected.
"""
print("\n--- Test 3: Wear Leveling ---")
profile_a = os.path.abspath("/abs/path/profile_a.json")
profile_b = os.path.abspath("/abs/path/profile_b.json")
mock_glob.return_value = [profile_a, profile_b]
mock_exists.return_value = True
# Setup Usage
mock_usage.side_effect = lambda p: 1000 if p == profile_a else 2000
# No Cooldowns -> Equal Efficiency Score (0)
auth_rotation._COOLDOWN_PROFILES = {}
target_model = "gemini-3-pro-preview"
selected = auth_rotation._find_best_profile_in_dirs(["/dummy/dir"], target_model)
print(f"Selected: {selected}")
print(f"Expected: {profile_a}")
self.assertEqual(selected, profile_a, "Should prefer lower usage when efficiency scores are equal")
@patch('browser_utils.auth_rotation.glob.glob')
@patch('browser_utils.auth_rotation.os.path.exists')
@patch('browser_utils.auth_rotation.get_profile_usage')
def test_legacy_json_compatibility(self, mock_usage, mock_exists, mock_glob):
"""
Scenario 4: Compatibility Test with User's JSON Structure.
Verifies that the logic handles mixed types (datetime objects from JSON load vs floats from runtime)
and absolute paths correctly.
"""
print("\n--- Test 4: Legacy JSON Compatibility ---")
# 1. Setup paths exactly as they might appear on Windows (escaped in code, but clean strings in memory)
# Using a raw string for the path to simulate the absolute path key
profile_path = os.path.abspath("auth_profiles/saved/rosavival002.json")
# 2. Setup Cooldowns mimicking load_cooldown_profiles output (datetime objects)
# User provided: "gemini-2.5-pro": "2025-12-01T01:52:00.112199"
# Since this is in the future, it should count as an active cooldown.
future_iso = datetime.now() + timedelta(days=365) # Ensure it's in the future
auth_rotation._COOLDOWN_PROFILES = {
profile_path: {
"gemini-2.5-pro": future_iso, # Object type: datetime (simulating loaded JSON)
"gemini-runtime-added": time.time() + 9999 # Object type: float (simulating runtime add)
}
}
# 3. Setup Files
mock_glob.return_value = [profile_path]
mock_exists.return_value = True
mock_usage.return_value = 500
# 4. Action: Request a DIFFERENT model (gemini-3)
# The profile is in cooldown for gemini-2.5 (datetime) and gemini-runtime (float).
# Both are "Other" models.
# Both are in the future.
# Efficiency Score should be 2.
target_model = "gemini-3-pro-preview"
# We need to spy on the internal priority calculation to verify it didn't crash
# and calculated score > 0.
# Since we can't easily spy on the inner function call result without more complex mocking,
# we'll rely on the function returning successfully.
try:
selected = auth_rotation._find_best_profile_in_dirs(["/dummy/dir"], target_model)
print(f"Selected: {selected}")
self.assertEqual(selected, profile_path, "Should successfully select profile using legacy/mixed data types")
except Exception as e:
self.fail(f"Compatibility test failed with error: {e}")
if __name__ == '__main__':
unittest.main()