"""代理 API 集成测试 - 测试 /api/proxies/* 所有接口""" import pytest class TestProxiesAPI: """测试代理相关 API""" @pytest.mark.asyncio async def test_get_stats(self, client): """测试 GET /api/proxies/stats""" response = await client.get("/api/proxies/stats") assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert "data" in data assert "total" in data["data"] assert "pending" in data["data"] assert "available" in data["data"] assert "scheduler_running" in data["data"] @pytest.mark.asyncio async def test_list_proxies(self, client): """测试 POST /api/proxies""" request_data = { "page": 1, "page_size": 10, "protocol": None, "min_score": 0, "sort_by": "last_check", "sort_order": "DESC" } response = await client.post("/api/proxies", json=request_data) assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert "list" in data["data"] assert "total" in data["data"] assert "page" in data["data"] @pytest.mark.asyncio async def test_list_proxies_with_protocol_filter(self, client): """测试 POST /api/proxies 带协议过滤""" request_data = { "page": 1, "page_size": 10, "protocol": "http", "min_score": 0, } response = await client.post("/api/proxies", json=request_data) assert response.status_code == 200 data = response.json() assert data["code"] == 200 @pytest.mark.asyncio async def test_list_proxies_invalid_protocol(self, client): """测试 POST /api/proxies 无效协议""" request_data = { "page": 1, "page_size": 10, "protocol": "invalid", } response = await client.post("/api/proxies", json=request_data) assert response.status_code == 422 # 验证错误 @pytest.mark.asyncio async def test_get_random_proxy_empty(self, client): """测试 GET /api/proxies/random - 空数据库""" response = await client.get("/api/proxies/random") # 可能返回 200(有数据) 或 404(无数据) assert response.status_code in [200, 404] @pytest.mark.asyncio async def test_delete_proxy_post_json(self, client, sample_proxy): """测试 POST /api/proxies/delete-one(前端默认路径,兼容 IPv6)""" response = await client.post( "/api/proxies/delete-one", json={"ip": sample_proxy["ip"], "port": sample_proxy["port"]}, ) assert response.status_code == 200 data = response.json() assert data["code"] == 200 @pytest.mark.asyncio async def test_delete_proxy(self, client, sample_proxy): """测试 DELETE /api/proxies/{ip}/{port}""" response = await client.delete(f"/api/proxies/{sample_proxy['ip']}/{sample_proxy['port']}") assert response.status_code == 200 data = response.json() assert data["code"] == 200 @pytest.mark.asyncio async def test_delete_one_ipv6(self, client, db, proxy_repo): """POST delete-one 可删除含冒号的 IP(路径 DELETE 无法可靠表达)""" await proxy_repo.insert_or_update(db, "2001:db8::1", 18080, "http", 40) r = await client.post( "/api/proxies/delete-one", json={"ip": "2001:db8::1", "port": 18080}, ) assert r.status_code == 200 assert r.json()["code"] == 200 left = await proxy_repo.get_by_ip_port(db, "2001:db8::1", 18080) assert left is None @pytest.mark.asyncio async def test_delete_nonexistent_proxy(self, client): """测试 DELETE /api/proxies/{ip}/{port} - 不存在的代理""" response = await client.delete("/api/proxies/999.999.999.999/99999") # 即使代理不存在也返回成功(幂等删除) assert response.status_code == 200 @pytest.mark.asyncio async def test_batch_delete_proxies(self, client): """测试 POST /api/proxies/batch-delete""" request_data = { "proxies": [ {"ip": "192.168.1.1", "port": 8080}, {"ip": "192.168.1.2", "port": 8081}, ] } response = await client.post("/api/proxies/batch-delete", json=request_data) assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert "deleted_count" in data["data"] @pytest.mark.asyncio async def test_batch_delete_too_many(self, client): """测试 POST /api/proxies/batch-delete - 超过限制""" request_data = { "proxies": [{"ip": f"192.168.1.{i}", "port": 8080} for i in range(1001)] } response = await client.post("/api/proxies/batch-delete", json=request_data) assert response.status_code == 422 # 验证错误 @pytest.mark.asyncio async def test_clean_invalid_proxies(self, client): """测试 DELETE /api/proxies/clean-invalid""" response = await client.delete("/api/proxies/clean-invalid") assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert "deleted_count" in data["data"] @pytest.mark.asyncio async def test_export_proxies_csv(self, client): """测试 GET /api/proxies/export/csv""" response = await client.get("/api/proxies/export/csv") assert response.status_code == 200 assert response.headers["content-type"].startswith("text/csv") @pytest.mark.asyncio async def test_export_proxies_txt(self, client): """测试 GET /api/proxies/export/txt""" response = await client.get("/api/proxies/export/txt") assert response.status_code == 200 assert response.headers["content-type"].startswith("text/plain") @pytest.mark.asyncio async def test_export_proxies_json(self, client): """测试 GET /api/proxies/export/json""" response = await client.get("/api/proxies/export/json") assert response.status_code == 200 assert response.headers["content-type"] == "application/json" @pytest.mark.asyncio async def test_export_proxies_json_empty_database(self, client): """测试 GET /api/proxies/export/json - 空数据库""" response = await client.get("/api/proxies/export/json") assert response.status_code == 200 assert response.headers["content-type"] == "application/json" # 空数据库应返回空列表 JSON assert response.content.strip() == b"[]" @pytest.mark.asyncio async def test_export_proxies_invalid_format(self, client): """测试 GET /api/proxies/export/invalid - 无效格式""" response = await client.get("/api/proxies/export/invalid") assert response.status_code == 400 # 错误响应