Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

238 lines
8.6 KiB
Python

"""
Tests for AdaptiveRateLimitTracker.
"""
from unittest.mock import patch
from local_deep_research.web_search_engines.rate_limiting.tracker import (
AdaptiveRateLimitTracker,
)
class TestAdaptiveRateLimitTrackerInit:
"""Tests for AdaptiveRateLimitTracker initialization."""
def test_init_with_default_settings(self):
"""Initializes with default settings."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
# Should use defaults when settings are not available
assert tracker.memory_window == 100
assert tracker.exploration_rate == 0.1
assert tracker.learning_rate == 0.3
assert tracker.decay_per_day == 0.95
def test_init_programmatic_mode(self):
"""Initializes in programmatic mode."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert tracker.programmatic_mode is True
def test_init_with_settings_snapshot(self, mock_settings_snapshot):
"""Initializes with settings snapshot."""
tracker = AdaptiveRateLimitTracker(
settings_snapshot=mock_settings_snapshot,
programmatic_mode=True,
)
assert tracker.settings_snapshot == mock_settings_snapshot
def test_init_empty_caches(self):
"""Initializes with empty caches."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert tracker.recent_attempts == {}
assert tracker.current_estimates == {}
class TestAdaptiveRateLimitTrackerProfiles:
"""Tests for rate limiting profiles."""
def test_balanced_profile(self):
"""Balanced profile keeps default values."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
tracker._apply_profile("balanced")
# Values should be unchanged from defaults
assert tracker.exploration_rate == 0.1
assert tracker.learning_rate == 0.3
def test_conservative_profile(self):
"""Conservative profile reduces exploration and learning rates."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
original_exploration = tracker.exploration_rate
original_learning = tracker.learning_rate
tracker._apply_profile("conservative")
# Values should be reduced
assert tracker.exploration_rate < original_exploration
assert tracker.learning_rate < original_learning
def test_aggressive_profile(self):
"""Aggressive profile increases exploration and learning rates."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
original_exploration = tracker.exploration_rate
original_learning = tracker.learning_rate
tracker._apply_profile("aggressive")
# Values should be increased
assert tracker.exploration_rate > original_exploration
assert tracker.learning_rate > original_learning
class TestAdaptiveRateLimitTrackerEnabled:
"""Tests for enabled/disabled state."""
def test_enabled_by_default_in_normal_mode(self):
"""Enabled by default in normal mode."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=False)
assert tracker.enabled is True
def test_disabled_by_default_in_programmatic_mode(self):
"""Disabled by default in programmatic mode."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert tracker.enabled is False
class TestAdaptiveRateLimitTrackerMemory:
"""Tests for in-memory tracking."""
def test_recent_attempts_stored(self):
"""Recent attempts are stored in memory."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
# Manually add some attempts
from collections import deque
tracker.recent_attempts["test_engine"] = deque(maxlen=100)
tracker.recent_attempts["test_engine"].append(
{
"wait_time": 1.0,
"success": True,
"timestamp": 1234567890,
}
)
assert len(tracker.recent_attempts["test_engine"]) == 1
def test_estimates_stored(self):
"""Estimates are stored in memory."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
tracker.current_estimates["test_engine"] = {
"min_wait": 0.5,
"max_wait": 2.0,
"avg_wait": 1.0,
}
assert "test_engine" in tracker.current_estimates
assert tracker.current_estimates["test_engine"]["avg_wait"] == 1.0
class TestAdaptiveRateLimitTrackerDatabaseSkip:
"""Tests for database operation skipping."""
def test_load_estimates_skipped_in_programmatic_mode(self):
"""_load_estimates skips database in programmatic mode."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker._get_db_imports"
):
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
# Should not attempt to get database imports
assert tracker._estimates_loaded is True
def test_load_estimates_deferred_in_non_programmatic_mode(self):
"""_load_estimates defers loading in non-programmatic mode."""
with patch(
"local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
) as mock_get:
from local_deep_research.config.thread_settings import (
NoSettingsContextError,
)
mock_get.side_effect = NoSettingsContextError()
tracker = AdaptiveRateLimitTracker(programmatic_mode=False)
# Non-programmatic defers loading until user context is available
assert tracker._estimates_loaded is False
assert tracker.current_estimates == {}
class TestRateLimitTrackerConstants:
"""Tests for tracker constants and settings."""
def test_memory_window_positive(self):
"""Memory window is positive."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert tracker.memory_window > 0
def test_exploration_rate_valid_range(self):
"""Exploration rate is between 0 and 1."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert 0 <= tracker.exploration_rate <= 1
def test_learning_rate_valid_range(self):
"""Learning rate is between 0 and 1."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert 0 <= tracker.learning_rate <= 1
def test_decay_per_day_valid_range(self):
"""Decay per day is between 0 and 1."""
tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
assert 0 <= tracker.decay_per_day <= 1