ForcePilot/test/api/test_system_api.py
Wenjie Zhang 2a3e18b444 test(tests): 添加全面的API接口测试套件
- 新增认证API测试,覆盖登录、权限验证、用户管理等关键流程
- 新增对话API测试,包含智能体调用、线程管理、工具获取及配置查询
- 新增系统API测试,涵盖系统健康检查、信息查询及配置管理权限控制
- 配置pytest支持异步测试、标记分类及详细输出
- 添加测试环境配置示例文件 .env.test.example
- 新增测试用例README文档,说明测试架构及运行方法
- 优化Makefile,添加格式化差异检查任务 format_diff
- 更新.gitignore,支持忽略测试环境配置文件 .env.test
2025-09-20 22:53:37 +08:00

239 lines
9.6 KiB
Python

"""
系统API测试
测试系统管理、配置管理等相关接口
"""
import pytest
import httpx
class TestSystemAPI:
"""系统API测试类"""
@pytest.mark.asyncio
async def test_health_check(self, test_client: httpx.AsyncClient):
"""测试系统健康检查接口"""
response = await test_client.get("/api/system/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
assert "message" in data
print(f"Health check response: {data}")
@pytest.mark.asyncio
async def test_get_system_info(self, test_client: httpx.AsyncClient):
"""测试获取系统信息接口(公开接口)"""
response = await test_client.get("/api/system/info")
if response.status_code == 200:
data = response.json()
assert "success" in data
assert "data" in data
print(f"System info response: {data}")
else:
# 如果接口不存在或有其他问题,记录但不失败
print(f"System info request failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_get_config_unauthorized(self, test_client: httpx.AsyncClient):
"""测试未授权访问配置接口"""
response = await test_client.get("/api/system/config")
assert response.status_code == 401
print("Config access properly requires authentication")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_get_config_authorized(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试已授权访问配置接口"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
response = await test_client.get("/api/system/config", headers=auth_headers)
if response.status_code == 200:
data = response.json()
# 验证配置数据结构
assert isinstance(data, dict)
# 检查一些常见的配置项
expected_config_keys = [
"enable_reranker",
"enable_web_search",
"model_provider",
"model_name",
"embed_model",
]
for key in expected_config_keys:
if key in data:
print(f"Config contains {key}: {data[key]}")
print(f"Config keys: {list(data.keys())}")
elif response.status_code == 403:
print("User does not have admin privileges to access config")
else:
print(f"Get config failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_update_config_unauthorized(self, test_client: httpx.AsyncClient):
"""测试未授权更新配置"""
config_data = {"key": "test_key", "value": "test_value"}
response = await test_client.post("/api/system/config", json=config_data)
assert response.status_code == 401
@pytest.mark.asyncio
@pytest.mark.auth
@pytest.mark.slow
async def test_update_single_config(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试更新单个配置项"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
# 先获取当前配置
get_response = await test_client.get("/api/system/config", headers=auth_headers)
if get_response.status_code != 200:
pytest.skip("Cannot access config, skipping update test")
original_config = get_response.json()
# 尝试更新一个安全的配置项(如果存在的话)
test_config_updates = [
{"key": "enable_web_search", "value": True},
{"key": "enable_reranker", "value": False},
]
for config_update in test_config_updates:
key = config_update["key"]
if key in original_config:
original_value = original_config[key]
new_value = config_update["value"]
# 更新配置
response = await test_client.post("/api/system/config", json=config_update, headers=auth_headers)
if response.status_code == 200:
updated_config = response.json()
assert updated_config[key] == new_value
print(f"Successfully updated {key} from {original_value} to {new_value}")
# 恢复原值
restore_data = {"key": key, "value": original_value}
restore_response = await test_client.post(
"/api/system/config", json=restore_data, headers=auth_headers
)
if restore_response.status_code == 200:
print(f"Successfully restored {key} to {original_value}")
break
elif response.status_code == 403:
print("User does not have permission to update config")
break
else:
print(f"Config update failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
@pytest.mark.slow
async def test_batch_update_config(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试批量更新配置项"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
# 准备批量更新数据
batch_updates = {"enable_web_search": True, "enable_reranker": False}
response = await test_client.post("/api/system/config/update", json=batch_updates, headers=auth_headers)
if response.status_code == 200:
updated_config = response.json()
for key, value in batch_updates.items():
if key in updated_config:
assert updated_config[key] == value
print(f"Batch update verified: {key} = {value}")
elif response.status_code == 403:
print("User does not have permission to batch update config")
else:
print(f"Batch config update failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_get_system_logs(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试获取系统日志"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
response = await test_client.get("/api/system/logs", headers=auth_headers)
if response.status_code == 200:
data = response.json()
assert "log" in data
assert "message" in data
assert data["message"] == "success"
assert "log_file" in data
print(f"Log file path: {data.get('log_file')}")
print(f"Log length: {len(data.get('log', ''))}")
elif response.status_code == 403:
print("User does not have permission to access logs")
else:
print(f"Get logs failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_restart_system_permission(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试系统重启权限(仅超级管理员)"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
response = await test_client.post("/api/system/restart", headers=auth_headers)
if response.status_code == 200:
data = response.json()
assert "message" in data
print(f"Restart response: {data}")
elif response.status_code == 403:
print("User does not have superadmin privileges to restart system")
else:
print(f"Restart request failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_reload_info_config(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试重新加载信息配置"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
response = await test_client.post("/api/system/info/reload", headers=auth_headers)
if response.status_code == 200:
data = response.json()
assert "success" in data
assert "message" in data
print(f"Info reload response: {data}")
elif response.status_code == 403:
print("User does not have permission to reload info config")
else:
print(f"Info reload failed with status {response.status_code}: {response.json()}")
@pytest.mark.asyncio
@pytest.mark.auth
async def test_get_ocr_stats(self, test_client: httpx.AsyncClient, auth_headers: dict):
"""测试获取OCR统计信息"""
if not auth_headers:
pytest.skip("No auth token available, skipping authenticated test")
response = await test_client.get("/api/system/ocr/stats", headers=auth_headers)
if response.status_code == 200:
data = response.json()
print(f"OCR stats response: {data}")
elif response.status_code == 403:
print("User does not have permission to access OCR stats")
elif response.status_code == 404:
print("OCR stats endpoint not available")
else:
print(f"OCR stats request failed with status {response.status_code}: {response.json()}")
# 标记为集成测试
pytestmark = [pytest.mark.integration]