Files
LangBot/tests/unit_tests/utils/test_proxy.py
huanghuoguoguo 3ba727f0e4 test: add 105 new unit tests for untested core functionality
Add comprehensive tests for B-class issues (core functionality untested):

Pipeline:
- test_pool.py: QueryPool ID generation, caching, async context (12 tests)
- test_ratelimit.py: Fixed timing-sensitive test tolerance
- test_pipelinemgr.py: Use real Pydantic StageProcessResult instead of Mock

Utils:
- test_version.py: Version comparison functions (20 tests)
- test_logcache.py: Log page management and retrieval (18 tests)
- test_httpclient.py: HTTP session pool management (10 tests)
- test_proxy.py: Proxy configuration from env and config (10 tests)
- test_image.py: URL parsing and base64 extraction (12 tests)
- test_pkgmgr.py: Pip command generation (8 tests)

Discover:
- test_engine.py: I18nString, Metadata, Component manifest (15 tests)

Test count: 1193 → 1298 (+105 tests)

Note: Some B-class issues cannot be tested due to circular import bugs
filed as GitHub issues #2175 (pipeline) and #2176 (persistence).
2026-05-16 10:13:15 +08:00

168 lines
5.8 KiB
Python

"""
Unit tests for ProxyManager.
Tests proxy configuration from environment and config.
"""
from __future__ import annotations
import pytest
import os
from unittest.mock import Mock, patch
from langbot.pkg.utils.proxy import ProxyManager
pytestmark = pytest.mark.asyncio
class TestProxyManager:
"""Tests for ProxyManager class."""
def _create_mock_app(self, proxy_config: dict = None):
"""Create mock app with proxy config."""
mock_app = Mock()
mock_app.instance_config = Mock()
mock_app.instance_config.data = {'proxy': proxy_config or {}}
return mock_app
def test_init_creates_empty_proxies(self):
"""ProxyManager initializes with empty forward_proxies."""
mock_app = self._create_mock_app()
pm = ProxyManager(mock_app)
assert pm.forward_proxies == {}
async def test_initialize_reads_env_variables(self):
"""initialize reads HTTP_PROXY from environment."""
mock_app = self._create_mock_app()
with patch.dict(os.environ, {'HTTP_PROXY': 'http://env-proxy:8080', 'HTTPS_PROXY': 'https://env-proxy:8443'}):
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] == 'http://env-proxy:8080'
assert pm.forward_proxies['https://'] == 'https://env-proxy:8443'
async def test_initialize_reads_lower_case_env(self):
"""initialize reads lower case http_proxy from environment."""
mock_app = self._create_mock_app()
with patch.dict(os.environ, {'http_proxy': 'http://lower-proxy:8080'}, clear=True):
# Clear HTTP_PROXY to test fallback
if 'HTTP_PROXY' in os.environ:
del os.environ['HTTP_PROXY']
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] == 'http://lower-proxy:8080'
async def test_initialize_config_overrides_env(self):
"""Config proxy overrides environment variables."""
mock_app = self._create_mock_app(proxy_config={
'http': 'http://config-proxy:8080',
'https': 'https://config-proxy:8443',
})
with patch.dict(os.environ, {'HTTP_PROXY': 'http://env-proxy:8080'}):
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] == 'http://config-proxy:8080'
assert pm.forward_proxies['https://'] == 'https://config-proxy:8443'
async def test_initialize_sets_env_variables(self):
"""initialize sets proxy to environment variables."""
mock_app = self._create_mock_app(proxy_config={
'http': 'http://test-proxy:8080',
'https': 'https://test-proxy:8443',
})
pm = ProxyManager(mock_app)
await pm.initialize()
assert os.environ.get('HTTP_PROXY') == 'http://test-proxy:8080'
assert os.environ.get('HTTPS_PROXY') == 'https://test-proxy:8443'
async def test_initialize_handles_empty_config(self):
"""initialize handles empty proxy config."""
mock_app = self._create_mock_app(proxy_config={})
with patch.dict(os.environ, clear=True):
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] is None
assert pm.forward_proxies['https://'] is None
async def test_initialize_handles_no_env_no_config(self):
"""initialize handles no env and no config."""
mock_app = self._create_mock_app(proxy_config={})
# Clear proxy env vars
env_backup = {}
for key in ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy']:
env_backup[key] = os.environ.get(key)
if key in os.environ:
del os.environ[key]
try:
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] is None
assert pm.forward_proxies['https://'] is None
finally:
# Restore env
for key, value in env_backup.items():
if value is not None:
os.environ[key] = value
def test_get_forward_proxies_returns_copy(self):
"""get_forward_proxies returns a copy of the dict."""
mock_app = self._create_mock_app()
pm = ProxyManager(mock_app)
pm.forward_proxies = {'http://': 'http://test:8080'}
result = pm.get_forward_proxies()
assert result == pm.forward_proxies
assert result is not pm.forward_proxies # Different object
def test_get_forward_proxies_modification_safe(self):
"""Modifying returned dict doesn't affect internal state."""
mock_app = self._create_mock_app()
pm = ProxyManager(mock_app)
pm.forward_proxies = {'http://': 'http://test:8080'}
result = pm.get_forward_proxies()
result['http://'] = 'http://modified:9999'
assert pm.forward_proxies['http://'] == 'http://test:8080'
async def test_initialize_http_only_config(self):
"""initialize handles http-only config."""
mock_app = self._create_mock_app(proxy_config={
'http': 'http://http-only:8080',
})
# Clear any existing proxy env vars
env_backup = {}
for key in ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy']:
env_backup[key] = os.environ.get(key)
if key in os.environ:
del os.environ[key]
try:
pm = ProxyManager(mock_app)
await pm.initialize()
assert pm.forward_proxies['http://'] == 'http://http-only:8080'
assert pm.forward_proxies['https://'] is None
finally:
# Restore env
for key, value in env_backup.items():
if value is not None:
os.environ[key] = value