重构: 迁移后端代码到 app 目录,前端移动到 WebUI,添加完整测试套件
主要变更: - 后端代码从根目录迁移到 app/ 目录 - 前端代码从 frontend/ 重命名为 WebUI/ - 更新所有导入路径以适配新结构 - 提取公共 API 响应函数到 app/api/common.py - 精简验证器服务代码 - 更新启动脚本和文档 测试: - 新增完整测试套件 (tests/) - 单元测试: 模型、仓库层 - 集成测试: 覆盖所有 22+ API 端点 - E2E 测试: 4个完整工作流场景 - 添加 pytest 配置和测试运行脚本
This commit is contained in:
137
tests/integration/test_settings_api.py
Normal file
137
tests/integration/test_settings_api.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""设置 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" 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 = [
|
||||
"crawl_timeout",
|
||||
"validation_timeout",
|
||||
"max_retries",
|
||||
"default_concurrency",
|
||||
"min_proxy_score",
|
||||
"proxy_expiry_days",
|
||||
"auto_validate",
|
||||
"validate_interval_minutes",
|
||||
]
|
||||
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 = {
|
||||
"crawl_timeout": 45,
|
||||
"validation_timeout": 15,
|
||||
"max_retries": 5,
|
||||
"default_concurrency": 100,
|
||||
"min_proxy_score": 10,
|
||||
"proxy_expiry_days": 14,
|
||||
"auto_validate": True,
|
||||
"validate_interval_minutes": 60,
|
||||
}
|
||||
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["crawl_timeout"] = 60
|
||||
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"]["crawl_timeout"] == 60
|
||||
assert data["data"]["auto_validate"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_save_settings_validation_error(self, client):
|
||||
"""测试 POST /api/settings - 验证错误"""
|
||||
# crawl_timeout 必须在 5-120 之间
|
||||
invalid_settings = {
|
||||
"crawl_timeout": 200, # 超出范围
|
||||
"validation_timeout": 10,
|
||||
"max_retries": 3,
|
||||
"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 = {
|
||||
"crawl_timeout": "invalid", # 应该是整数
|
||||
"validation_timeout": 10,
|
||||
"max_retries": 3,
|
||||
"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_settings_roundtrip(self, client):
|
||||
"""测试设置读写一致性"""
|
||||
# 生成随机但有效的设置
|
||||
import random
|
||||
test_settings = {
|
||||
"crawl_timeout": random.randint(10, 60),
|
||||
"validation_timeout": random.randint(5, 30),
|
||||
"max_retries": random.randint(1, 5),
|
||||
"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} 不一致"
|
||||
Reference in New Issue
Block a user