"""设置 API 集成测试 - 测试 /api/settings 接口""" import pytest class TestSettingsAPI: """测试设置相关 API""" @pytest.mark.asyncio async def test_get_settings(self, client): """测试 GET /api/settings""" response = await client.get("/api/settings") assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert "crawl_timeout" not in data["data"] assert "validation_timeout" in data["data"] assert "auto_validate" in data["data"] @pytest.mark.asyncio async def test_get_settings_structure(self, client): """测试 GET /api/settings 返回结构""" response = await client.get("/api/settings") data = response.json()["data"] expected_keys = [ "validation_timeout", "default_concurrency", "min_proxy_score", "proxy_expiry_days", "auto_validate", "auto_validate_after_crawl", "validate_interval_minutes", "validation_targets", ] for key in expected_keys: assert key in data, f"缺少设置项: {key}" @pytest.mark.asyncio async def test_save_settings(self, client): """测试 POST /api/settings""" settings = { "validation_timeout": 15, "default_concurrency": 100, "min_proxy_score": 10, "proxy_expiry_days": 14, "auto_validate": True, "auto_validate_after_crawl": False, "validate_interval_minutes": 60, "validation_targets": [ "http://httpbin.org/ip", ], } response = await client.post("/api/settings", json=settings) assert response.status_code == 200 data = response.json() assert data["code"] == 200 for key, value in settings.items(): assert data["data"][key] == value @pytest.mark.asyncio async def test_save_settings_partial(self, client): """测试 POST /api/settings - 部分更新(实际上会替换所有)""" response = await client.get("/api/settings") current_settings = response.json()["data"] new_settings = current_settings.copy() new_settings["validation_timeout"] = 25 new_settings["auto_validate"] = False response = await client.post("/api/settings", json=new_settings) assert response.status_code == 200 data = response.json() assert data["data"]["validation_timeout"] == 25 assert data["data"]["auto_validate"] is False @pytest.mark.asyncio async def test_save_settings_validation_error(self, client): """测试 POST /api/settings - 验证错误""" invalid_settings = { "validation_timeout": 100, "default_concurrency": 50, "min_proxy_score": 0, "proxy_expiry_days": 7, "auto_validate": True, "validate_interval_minutes": 30, } response = await client.post("/api/settings", json=invalid_settings) assert response.status_code == 422 @pytest.mark.asyncio async def test_save_settings_invalid_type(self, client): """测试 POST /api/settings - 无效类型""" invalid_settings = { "validation_timeout": 10, "default_concurrency": "invalid", "min_proxy_score": 0, "proxy_expiry_days": 7, "auto_validate": True, "validate_interval_minutes": 30, } response = await client.post("/api/settings", json=invalid_settings) assert response.status_code == 422 @pytest.mark.asyncio async def test_save_settings_ignores_deprecated_crawl_timeout(self, client): """旧客户端若仍提交 crawl_timeout,应忽略且保存成功""" response = await client.get("/api/settings") base = response.json()["data"] payload = {**base, "crawl_timeout": 999} response = await client.post("/api/settings", json=payload) assert response.status_code == 200 again = (await client.get("/api/settings")).json()["data"] assert "crawl_timeout" not in again @pytest.mark.asyncio async def test_save_settings_ignores_obsolete_max_retries(self, client): """已移除的 max_retries 键若仍被提交,应忽略。""" response = await client.get("/api/settings") base = response.json()["data"] payload = {**base, "max_retries": 9} response = await client.post("/api/settings", json=payload) assert response.status_code == 200 again = (await client.get("/api/settings")).json()["data"] assert "max_retries" not in again @pytest.mark.asyncio async def test_settings_roundtrip(self, client): """测试设置读写一致性""" import random test_settings = { "validation_timeout": random.randint(5, 30), "default_concurrency": random.randint(20, 100), "min_proxy_score": random.randint(0, 50), "proxy_expiry_days": random.randint(1, 14), "auto_validate": random.choice([True, False]), "validate_interval_minutes": random.randint(10, 120), } response = await client.post("/api/settings", json=test_settings) assert response.status_code == 200 response = await client.get("/api/settings") saved_settings = response.json()["data"] for key, value in test_settings.items(): assert saved_settings[key] == value, f"设置项 {key} 不一致" @pytest.mark.asyncio async def test_settings_roundtrip_with_validation_targets(self, client): """测试设置读写一致性 - 包含数组类型的 validation_targets""" test_settings = { "validation_timeout": 10, "default_concurrency": 50, "min_proxy_score": 0, "proxy_expiry_days": 7, "auto_validate": True, "validate_interval_minutes": 30, "validation_targets": [ "http://example.com/1", "https://example.com/2", ], } response = await client.post("/api/settings", json=test_settings) assert response.status_code == 200 data = response.json() assert data["data"]["validation_targets"] == test_settings["validation_targets"] response = await client.get("/api/settings") saved_settings = response.json()["data"] assert saved_settings["validation_targets"] == test_settings["validation_targets"] @pytest.mark.asyncio async def test_save_settings_empty_validation_targets(self, client): """测试保存空的 validation_targets""" response = await client.get("/api/settings") current_settings = response.json()["data"] new_settings = current_settings.copy() new_settings["validation_targets"] = [] response = await client.post("/api/settings", json=new_settings) assert response.status_code == 200 data = response.json() assert data["data"]["validation_targets"] == [] response = await client.get("/api/settings") saved_settings = response.json()["data"] assert saved_settings["validation_targets"] == []