Initial commit: Pixel AI comic/video creation platform

- FastAPI backend with SQLModel, Alembic migrations, AgentScope agents
- Next.js 15 frontend with React 19, Tailwind, Zustand, React Flow
- Multi-provider AI system (DashScope, Kling, MiniMax, Volcengine, OpenAI, etc.)
- All HTTP clients migrated from sync requests to async httpx
- Admin-managed API keys via environment variables
- SSRF vulnerability fixed in ensure_url()
This commit is contained in:
张鹏
2026-04-29 01:20:12 +08:00
commit f9f4560459
808 changed files with 151724 additions and 0 deletions

View File

@@ -0,0 +1,241 @@
"""
集成测试
测试完整的请求流程和组件集成
"""
import pytest
import sys
import os
# 添加项目根目录到路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from fastapi.testclient import TestClient
from src.main import app
from src.utils.errors import ErrorCode
client = TestClient(app)
def unwrap_response_data(response):
payload = response.json()
return payload.get("data", payload)
class TestHealthEndpoints:
"""健康检查端点测试"""
def test_basic_health_check(self):
"""测试基础健康检查"""
response = client.get("/health")
assert response.status_code == 200
data = unwrap_response_data(response)
assert data["status"] == "healthy"
assert "service" in data
assert "timestamp" in data
assert "uptime_seconds" in data
def test_detailed_health_check(self):
"""测试详细健康检查"""
response = client.get("/health/detailed")
assert response.status_code == 200
data = unwrap_response_data(response)
assert "status" in data
assert "components" in data
assert "database" in data["components"]
assert "task_manager" in data["components"]
def test_liveness_probe(self):
"""测试存活探针"""
response = client.get("/health/live")
assert response.status_code == 200
data = unwrap_response_data(response)
assert data["status"] == "alive"
def test_readiness_probe(self):
"""测试就绪探针"""
response = client.get("/health/ready")
# 可能返回 200 或 503取决于组件状态
assert response.status_code in [200, 503]
def test_metrics_endpoint(self):
"""测试 Prometheus 指标端点"""
response = client.get("/metrics")
assert response.status_code == 200
assert "text/plain" in response.headers["content-type"]
# 检查是否包含一些关键指标
content = response.text
assert "task_created_total" in content or "http_request" in content
class TestErrorHandling:
"""错误处理测试"""
def test_404_not_found(self):
"""测试 404 错误"""
response = client.get("/api/projects/non-existent-id-12345")
# 项目不存在会返回 404HTTPException
assert response.status_code == 404
data = response.json()
assert "detail" in data or "message" in data
def test_invalid_parameter(self):
"""测试参数验证错误"""
# 测试一个需要参数的端点
response = client.post("/api/v1/generations/image", json={
# 缺少必需的 prompt 参数
})
# 应该返回 422Pydantic 验证错误)
assert response.status_code == 422
def test_method_not_allowed(self):
"""测试方法不允许"""
response = client.put("/health") # health 只支持 GET
assert response.status_code == 405
class TestConfigEndpoints:
"""配置端点测试"""
def test_get_system_config(self):
"""测试获取系统配置"""
response = client.get("/api/v1/config/system")
assert response.status_code == 200
def test_get_models_config(self):
"""测试获取模型配置"""
response = client.get("/api/v1/config/models")
assert response.status_code == 200
payload = unwrap_response_data(response)
assert isinstance(payload, dict)
def test_get_defaults(self):
"""测试获取默认模型"""
response = client.get("/api/v1/config/defaults")
assert response.status_code == 200
data = response.json()
assert "data" in data
def test_get_styles(self):
"""测试获取样式配置"""
response = client.get("/api/v1/config/styles")
assert response.status_code == 200
data = unwrap_response_data(response)
assert isinstance(data, dict)
class TestTaskEndpoints:
"""任务管理端点测试"""
def test_get_task_stats_from_new_controller(self):
"""测试获取任务统计(新的 tasks 控制器)"""
# 由于路由冲突,旧的 generations.py 的 /tasks 路由会先匹配
# 我们测试新的 tasks 控制器的功能通过直接导入
from src.services.task_manager import task_manager
stats = task_manager.get_stats()
assert "total_tasks" in stats
assert "queue_size" in stats
def test_get_task_from_old_controller(self):
"""测试获取任务(旧的 generations 控制器)"""
# 旧的 generations.py 控制器有 /tasks 路由
response = client.get("/api/v1/tasks")
# 应该返回 200旧控制器的响应
assert response.status_code == 200
def test_get_nonexistent_task(self):
"""测试获取不存在的任务"""
response = client.get("/api/v1/tasks/non-existent-task-id-12345")
# 可能返回 404 或 200取决于哪个控制器处理
assert response.status_code in [200, 404]
class TestCanvasEndpoints:
"""画布端点测试"""
def test_get_canvas_default(self):
"""测试获取默认画布"""
response = client.get("/api/v1/canvas?id=default")
assert response.status_code == 200
data = response.json()
assert "data" in data
def test_save_canvas(self):
"""测试保存画布"""
canvas_data = {
"id": "test-canvas",
"projectId": "test-project",
"nodes": [],
"connections": [],
"groups": [],
"history": [],
"history_index": 0
}
response = client.post("/api/v1/canvas", json=canvas_data)
assert response.status_code == 200
data = response.json()
assert "data" in data
class TestPerformanceHeaders:
"""性能监控头测试"""
def test_process_time_header(self):
"""测试处理时间头"""
response = client.get("/health")
assert response.status_code == 200
# 检查是否有处理时间头
assert "X-Process-Time" in response.headers
# 处理时间应该是一个数字
process_time = float(response.headers["X-Process-Time"])
assert process_time >= 0
class TestCORS:
"""CORS 测试"""
def test_cors_headers(self):
"""测试 CORS 头"""
response = client.options("/api/v1/config/system", headers={
"Origin": "http://localhost:3000",
"Access-Control-Request-Method": "GET"
})
# 检查 CORS 头
assert "access-control-allow-origin" in response.headers
def test_openapi_schema():
"""测试 OpenAPI 规范"""
response = client.get("/openapi.json")
assert response.status_code == 200
schema = response.json()
assert "openapi" in schema
assert "info" in schema
assert "paths" in schema
def test_docs_endpoint():
"""测试文档端点"""
response = client.get("/docs")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
if __name__ == "__main__":
pytest.main([__file__, "-v", "--tb=short"])