peijun1's picture
Deploy AI Studio Proxy API to Hugging Face Spaces
a5784e9
Raw
History Blame Contribute Delete
5.95 kB
"""
Tests for Proxy Configuration API Router
Covers: GET/POST /api/proxy/config, POST /api/proxy/test
"""
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from api_utils.routers.proxy import (
ProxyConfig,
ProxyTestRequest,
ProxyTestResult,
_load_config,
_save_config,
router,
)
@pytest.fixture
def app() -> FastAPI:
"""Create test FastAPI app with proxy router."""
app = FastAPI()
app.include_router(router)
return app
@pytest.fixture
def client(app: FastAPI) -> TestClient:
"""Create test client."""
return TestClient(app)
@pytest.fixture
def mock_config_file(tmp_path: Path) -> Path:
"""Create a temporary config file path."""
return tmp_path / "proxy_config.json"
class TestProxyConfig:
"""Tests for proxy configuration endpoints."""
def test_proxy_config_model_validation(self) -> None:
"""Test ProxyConfig validation."""
config = ProxyConfig(enabled=True, address="http://127.0.0.1:7890")
assert config.enabled is True
assert config.address == "http://127.0.0.1:7890"
def test_proxy_config_invalid_address(self) -> None:
"""Test ProxyConfig rejects invalid addresses."""
with pytest.raises(ValueError, match="http://"):
ProxyConfig(enabled=True, address="invalid")
def test_proxy_config_empty_address_allowed(self) -> None:
"""Test ProxyConfig allows empty address."""
config = ProxyConfig(enabled=False, address="")
assert config.address == ""
def test_get_proxy_config(self, client: TestClient) -> None:
"""Test GET /api/proxy/config returns config."""
with patch(
"api_utils.routers.proxy._load_config",
return_value=ProxyConfig(enabled=True, address="http://test:8080"),
):
response = client.get("/api/proxy/config")
assert response.status_code == 200
data = response.json()
assert data["enabled"] is True
assert data["address"] == "http://test:8080"
def test_update_proxy_config(self, client: TestClient) -> None:
"""Test POST /api/proxy/config updates config."""
with patch("api_utils.routers.proxy._save_config") as mock_save:
response = client.post(
"/api/proxy/config",
json={"enabled": True, "address": "http://new:8080"},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["config"]["enabled"] is True
mock_save.assert_called_once()
def test_update_proxy_config_validation_error(self, client: TestClient) -> None:
"""Test POST /api/proxy/config rejects invalid data."""
response = client.post(
"/api/proxy/config",
json={"enabled": True, "address": "invalid"},
)
assert response.status_code == 422 # Validation error
class TestProxyConnectivityTest:
"""Tests for proxy connectivity test endpoint."""
def test_proxy_test_request_model(self) -> None:
"""Test ProxyTestRequest model."""
req = ProxyTestRequest(address="http://proxy:8080")
assert req.address == "http://proxy:8080"
assert req.test_url == "http://httpbin.org/get"
def test_proxy_test_result_model(self) -> None:
"""Test ProxyTestResult model."""
result = ProxyTestResult(success=True, message="OK", latency_ms=123.45)
assert result.success is True
assert result.latency_ms == 123.45
def test_proxy_test_empty_address(self, client: TestClient) -> None:
"""Test POST /api/proxy/test with empty address returns error."""
response = client.post("/api/proxy/test", json={"address": ""})
assert response.status_code == 400
data = response.json()
assert data["success"] is False
@pytest.mark.asyncio
async def test_proxy_test_success(self, client: TestClient) -> None:
"""Test POST /api/proxy/test with successful connection."""
mock_response = MagicMock()
mock_response.status_code = 200
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
response = client.post(
"/api/proxy/test",
json={"address": "http://proxy:8080", "test_url": "http://test.com"},
)
# Note: Due to async context, this test may need adjustment
# The actual response depends on httpx behavior
assert response.status_code in (200, 500) # Either success or error
class TestConfigPersistence:
"""Tests for config file operations."""
def test_load_config_default(self, tmp_path: Path) -> None:
"""Test _load_config returns default when file missing."""
with patch(
"api_utils.routers.proxy._PROXY_CONFIG_FILE", tmp_path / "nonexistent.json"
):
config = _load_config()
assert config.enabled is False
assert config.address == "http://127.0.0.1:7890"
def test_save_and_load_config(self, tmp_path: Path) -> None:
"""Test config can be saved and loaded."""
config_file = tmp_path / "proxy_config.json"
with patch("api_utils.routers.proxy._PROXY_CONFIG_FILE", config_file):
original = ProxyConfig(enabled=True, address="http://saved:9999")
_save_config(original)
loaded = _load_config()
assert loaded.enabled is True
assert loaded.address == "http://saved:9999"