Round 3 fixes: cancelled polling, aggregator invalid_count, filter state, scheduler atomicity, HTTP exception handler, tests
This commit is contained in:
@@ -45,3 +45,13 @@ class TestHealthAPI:
|
||||
assert isinstance(data["database"], str)
|
||||
assert isinstance(data["scheduler"], str)
|
||||
assert isinstance(data["version"], str)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_404_not_found_unified_format(self, client):
|
||||
"""测试 404 返回统一格式"""
|
||||
response = await client.get("/api/not-exist")
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
assert data["code"] == 404
|
||||
assert "message" in data
|
||||
assert data["data"] is None
|
||||
|
||||
@@ -140,11 +140,11 @@ class TestPluginsAPI:
|
||||
res = await client.get(f"/api/tasks/{task_id}")
|
||||
assert res.status_code == 200
|
||||
task_data = res.json()["data"]
|
||||
if task_data["status"] in ("completed", "failed"):
|
||||
if task_data["status"] in ("completed", "failed", "cancelled"):
|
||||
break
|
||||
|
||||
assert task_data is not None
|
||||
assert task_data["status"] == "completed"
|
||||
assert task_data["status"] in ("completed", "cancelled")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_crawl_nonexistent_plugin(self, client):
|
||||
@@ -170,8 +170,8 @@ class TestPluginsAPI:
|
||||
res = await client.get(f"/api/tasks/{task_id}")
|
||||
assert res.status_code == 200
|
||||
task_data = res.json()["data"]
|
||||
if task_data["status"] in ("completed", "failed"):
|
||||
if task_data["status"] in ("completed", "failed", "cancelled"):
|
||||
break
|
||||
|
||||
assert task_data is not None
|
||||
assert task_data["status"] == "completed"
|
||||
assert task_data["status"] in ("completed", "cancelled")
|
||||
|
||||
@@ -137,6 +137,15 @@ class TestProxiesAPI:
|
||||
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 - 无效格式"""
|
||||
|
||||
@@ -73,6 +73,25 @@ class TestSchedulerAPI:
|
||||
assert data["code"] == 200
|
||||
assert data["data"]["started"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_now_returns_valid_job(self, client):
|
||||
"""测试 POST /api/scheduler/validate-now 返回有效 job_id"""
|
||||
await client.post("/api/scheduler/start")
|
||||
response = await client.post("/api/scheduler/validate-now")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["code"] == 200
|
||||
job_id = data["data"]["job_id"]
|
||||
assert isinstance(job_id, str) and len(job_id) > 0
|
||||
|
||||
# 通过应用状态验证 job 已被提交到 executor
|
||||
from app.api.main import create_app
|
||||
# 使用 client 的 app 实例
|
||||
app = client._transport.app
|
||||
executor = app.state.executor
|
||||
job = executor.get_job(job_id)
|
||||
assert job is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scheduler_full_workflow(self, client):
|
||||
"""测试调度器完整工作流"""
|
||||
|
||||
@@ -135,3 +135,32 @@ class TestSettingsAPI:
|
||||
# 验证一致性
|
||||
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 = {
|
||||
"crawl_timeout": 30,
|
||||
"validation_timeout": 10,
|
||||
"max_retries": 3,
|
||||
"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"]
|
||||
|
||||
Reference in New Issue
Block a user