Spaces:
Paused
Paused
| """ | |
| 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, | |
| ) | |
| def app() -> FastAPI: | |
| """Create test FastAPI app with proxy router.""" | |
| app = FastAPI() | |
| app.include_router(router) | |
| return app | |
| def client(app: FastAPI) -> TestClient: | |
| """Create test client.""" | |
| return TestClient(app) | |
| 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 | |
| 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" | |