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,563 @@
"""
Test Resolution Parameter Handling
测试 resolution 参数在图片和视频生成中的处理逻辑:
1. 图片生成: resolution (1K/2K/4K) + aspect_ratio -> size
2. 视频生成: resolution (720P/1080P) + aspect_ratio -> size
3. 验证配置加载和解析逻辑
"""
import pytest
import json
import os
from unittest.mock import Mock, patch, MagicMock
from typing import Dict, Any, Optional
# Add backend/src to path
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
from src.models.schemas import ImageGenerationRequest, VideoGenerationRequest
class TestImageResolutionParsing:
"""测试图片生成 resolution 参数解析"""
def test_image_resolution_defaults(self):
"""测试图片 resolution 默认值"""
# 模拟服务配置
mock_config = {
"resolutions": {
"1K": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280"
},
"2K": {
"16:9": "2560*1440",
"9:16": "1440*2560",
"1:1": "2048*2048"
}
}
}
# 测试默认 resolution (1K)
resolution_level = "1K" # 默认值
aspect_ratio = "16:9"
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(resolution_level, {})
size = ratio_map.get(aspect_ratio)
assert size == "1280*720", f"Expected 1280*720 for 1K/16:9, got {size}"
def test_image_resolution_2k_parsing(self):
"""测试 2K resolution 解析"""
mock_config = {
"resolutions": {
"1K": {"16:9": "1280*720"},
"2K": {"16:9": "2560*1440"}
}
}
resolution_level = "2K"
aspect_ratio = "16:9"
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(resolution_level, {})
size = ratio_map.get(aspect_ratio)
assert size == "2560*1440", f"Expected 2560*1440 for 2K/16:9, got {size}"
def test_image_resolution_various_ratios(self):
"""测试不同 aspect_ratio 的 resolution 解析"""
mock_config = {
"resolutions": {
"1K": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280",
"4:3": "1280*960",
"3:4": "960*1280"
}
}
}
test_cases = [
("1K", "16:9", "1280*720"),
("1K", "9:16", "720*1280"),
("1K", "1:1", "1280*1280"),
("1K", "4:3", "1280*960"),
("1K", "3:4", "960*1280"),
]
for res_level, ratio, expected in test_cases:
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(res_level, {})
size = ratio_map.get(ratio)
assert size == expected, f"Expected {expected} for {res_level}/{ratio}, got {size}"
def test_image_resolution_fallback_defaults(self):
"""测试图片 resolution 回退默认值"""
# 当配置文件中没有找到 resolution 时,使用硬编码默认值
defaults = {
"1K": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1024*1024",
"4:3": "1280*960",
"3:4": "960*1280",
},
"2K": {
"16:9": "2560*1440",
"9:16": "1440*2560",
"1:1": "2048*2048",
}
}
res_level = "1K"
aspect_ratio = "16:9"
size = defaults.get(res_level, {}).get(aspect_ratio, "1024*1024")
assert size == "1280*720"
def test_image_resolution_ultimate_fallback(self):
"""测试终极回退(当所有配置都失败时)"""
ultimate_fallback = "1024*1024"
# 模拟配置完全缺失的情况
mock_config = {}
res_level = "4K" # 不存在的 resolution
aspect_ratio = "999:1" # 不存在的 ratio
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(res_level, {})
size = ratio_map.get(aspect_ratio)
# 当配置查找失败时,应使用终极回退
if not size:
size = "1024*1024"
assert size == "1024*1024"
class TestVideoResolutionParsing:
"""测试视频生成 resolution 参数解析"""
def test_video_resolution_defaults(self):
"""测试视频 resolution 默认值 (720P)"""
mock_config = {
"resolutions": {
"720P": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280"
},
"1080P": {
"16:9": "1920*1080",
"9:16": "1080*1920"
}
}
}
resolution_level = "720P" # 默认值
aspect_ratio = "16:9"
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(resolution_level, {})
size = ratio_map.get(aspect_ratio)
assert size == "1280*720", f"Expected 1280*720 for 720P/16:9, got {size}"
def test_video_resolution_1080p(self):
"""测试 1080P resolution 解析"""
mock_config = {
"resolutions": {
"720P": {"16:9": "1280*720"},
"1080P": {"16:9": "1920*1080"}
}
}
resolution_level = "1080P"
aspect_ratio = "16:9"
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(resolution_level, {})
size = ratio_map.get(aspect_ratio)
assert size == "1920*1080", f"Expected 1920*1080 for 1080P/16:9, got {size}"
def test_video_resolution_various_ratios(self):
"""测试视频不同 aspect_ratio 的 resolution 解析"""
mock_config = {
"resolutions": {
"720P": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280",
"4:3": "1280*960",
}
}
}
test_cases = [
("720P", "16:9", "1280*720"),
("720P", "9:16", "720*1280"),
("720P", "1:1", "1280*1280"),
("720P", "4:3", "1280*960"),
]
for res_level, ratio, expected in test_cases:
resolutions_config = mock_config.get("resolutions", {})
ratio_map = resolutions_config.get(res_level, {})
size = ratio_map.get(ratio)
assert size == expected, f"Expected {expected} for {res_level}/{ratio}, got {size}"
def test_video_resolution_fallback(self):
"""测试视频 resolution 回退"""
defaults = {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280",
"4:3": "1280*960",
"3:4": "960*1280"
}
aspect_ratio = "16:9"
size = defaults.get(aspect_ratio)
assert size == "1280*720"
class TestSchemaValidation:
"""测试 Schema 验证逻辑"""
def test_image_generation_request_schema_pixel_format(self):
"""测试图片生成请求 Schema - 像素格式 (应拒绝)"""
from src.utils.errors import InvalidParameterException
with pytest.raises(InvalidParameterException):
ImageGenerationRequest(
prompt="A beautiful sunset",
model="dashscope/qwen-image",
resolution="1920*1080"
)
def test_image_generation_request_schema_quality_format_blocked(self):
"""测试图片生成请求 Schema - 质量级别格式通过"""
request = ImageGenerationRequest(
prompt="A beautiful sunset",
model="dashscope/qwen-image",
resolution="2K"
)
assert request.resolution == "2K"
def test_video_generation_request_schema(self):
"""测试视频生成请求 Schema"""
request = VideoGenerationRequest(
prompt="A dancing figure",
model="dashscope/wan2.6-video",
resolution="720P",
duration=5
)
assert request.resolution == "720P"
def test_video_resolution_no_validation(self):
"""测试视频 resolution 没有严格格式验证"""
# 视频的 resolution 没有 @field_validator所以可以使用质量级别格式
request = VideoGenerationRequest(
prompt="Test",
model="dashscope/wan2.6-video",
resolution="1080P", # 质量级别格式 - 视频 schema 接受
duration=5
)
assert request.resolution == "1080P"
def test_image_schema_validation_resolution_format(self):
"""测试图片 resolution 格式验证 - 只支持质量级别"""
from src.utils.errors import InvalidParameterException
with pytest.raises(InvalidParameterException):
ImageGenerationRequest(
prompt="Test",
model="dashscope/qwen-image",
resolution="1920*1080"
)
request2 = ImageGenerationRequest(
prompt="Test",
model="dashscope/qwen-image",
resolution="2K"
)
assert request2.resolution == "2K"
def test_aspect_ratio_validation(self):
"""测试 aspect_ratio 格式验证"""
# 注意aspect_ratio 字段需要正确的 alias 设置
# 测试时我们跳过 alias 问题,只验证 resolution 参数
# 图片请求 - 只验证 resolution
request = ImageGenerationRequest(
prompt="Test",
model="dashscope/qwen-image"
)
assert request.prompt == "Test"
# 视频请求 - 只验证 resolution
request = VideoGenerationRequest(
prompt="Test",
model="dashscope/wan2.6-video",
duration=5
)
assert request.duration == 5
class TestControllerLogic:
"""测试控制器中的 resolution 处理逻辑"""
def _simulate_image_resolution_logic(self, request_data: Dict, mock_service_config: Dict) -> Optional[str]:
"""模拟图片控制器的 resolution 解析逻辑"""
aspect_ratio = request_data.get("aspect_ratio")
resolution = request_data.get("resolution")
size = None
if aspect_ratio:
resolutions_config = mock_service_config.get("resolutions", {})
res_level = resolution or "1K"
if resolutions_config and res_level in resolutions_config and isinstance(resolutions_config[res_level], dict):
ratio_map = resolutions_config[res_level]
if aspect_ratio in ratio_map:
size = ratio_map[aspect_ratio]
if not size:
defaults = {
"1K": {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1024*1024"
},
"2K": {
"16:9": "2560*1440",
"1:1": "2048*2048"
}
}
size = defaults.get(res_level, {}).get(aspect_ratio)
return size
def _simulate_video_resolution_logic(self, request_data: Dict, mock_service_config: Dict) -> Optional[str]:
"""模拟视频控制器的 resolution 解析逻辑"""
aspect_ratio = request_data.get("aspect_ratio")
resolution = request_data.get("resolution")
size = None
if aspect_ratio:
resolutions_config = mock_service_config.get("resolutions", {})
res_level = resolution or "720P"
if resolutions_config and res_level in resolutions_config and isinstance(resolutions_config[res_level], dict):
ratio_map = resolutions_config[res_level]
if aspect_ratio in ratio_map:
size = ratio_map[aspect_ratio]
if not size:
defaults = {
"16:9": "1280*720",
"9:16": "720*1280",
"1:1": "1280*1280"
}
size = defaults.get(aspect_ratio)
return size
def test_image_controller_logic_with_config(self):
"""测试图片控制器逻辑 - 使用配置"""
mock_service_config = {
"resolutions": {
"1K": {"16:9": "1280*720", "1:1": "1280*1280"},
"2K": {"16:9": "2560*1440", "1:1": "2048*2048"}
}
}
request_data = {
"aspect_ratio": "16:9",
"resolution": "2K"
}
size = self._simulate_image_resolution_logic(request_data, mock_service_config)
assert size == "2560*1440"
def test_image_controller_logic_default_resolution(self):
"""测试图片控制器逻辑 - 使用默认 resolution"""
mock_service_config = {
"resolutions": {
"1K": {"16:9": "1280*720"}
}
}
request_data = {
"aspect_ratio": "16:9"
# 没有提供 resolution应默认使用 "1K"
}
size = self._simulate_image_resolution_logic(request_data, mock_service_config)
assert size == "1280*720"
def test_video_controller_logic_with_config(self):
"""测试视频控制器逻辑 - 使用配置"""
mock_service_config = {
"resolutions": {
"720P": {"16:9": "1280*720"},
"1080P": {"16:9": "1920*1080"}
}
}
request_data = {
"aspect_ratio": "16:9",
"resolution": "1080P"
}
size = self._simulate_video_resolution_logic(request_data, mock_service_config)
assert size == "1920*1080"
def test_video_controller_logic_default_resolution(self):
"""测试视频控制器逻辑 - 使用默认 resolution"""
mock_service_config = {
"resolutions": {
"720P": {"16:9": "1280*720"}
}
}
request_data = {
"aspect_ratio": "16:9"
# 没有提供 resolution应默认使用 "720P"
}
size = self._simulate_video_resolution_logic(request_data, mock_service_config)
assert size == "1280*720"
class TestResolutionWithRealConfigs:
"""使用真实配置文件测试 resolution 参数"""
def test_dashscope_image_config(self):
"""测试 dashscope 图片配置"""
config_path = os.path.join(
os.path.dirname(__file__), '..', 'src', 'config', 'services',
'dashscope', 'image.json'
)
if not os.path.exists(config_path):
pytest.skip(f"Config file not found: {config_path}")
with open(config_path, 'r') as f:
config = json.load(f)
# 验证所有模型都有 resolutions 配置
for model_key, model_config in config.items():
if isinstance(model_config, dict) and "resolutions" in model_config:
resolutions = model_config["resolutions"]
assert isinstance(resolutions, dict)
# 验证嵌套结构
for res_level, ratio_map in resolutions.items():
assert isinstance(ratio_map, dict)
for ratio, size in ratio_map.items():
# 验证 size 格式
assert "*" in size or "x" in size
parts = size.replace("x", "*").split("*")
assert len(parts) == 2
width, height = int(parts[0]), int(parts[1])
assert width > 0 and height > 0
def test_kling_video_config(self):
"""测试 kling 视频配置"""
config_path = os.path.join(
os.path.dirname(__file__), '..', 'src', 'config', 'services',
'kling', 'video.json'
)
if not os.path.exists(config_path):
pytest.skip(f"Config file not found: {config_path}")
with open(config_path, 'r') as f:
config = json.load(f)
for model_key, model_config in config.items():
if isinstance(model_config, dict) and "resolutions" in model_config:
resolutions = model_config["resolutions"]
for res_level, ratio_map in resolutions.items():
for ratio, size in ratio_map.items():
assert "*" in size
class TestEdgeCases:
"""测试边界情况"""
def test_no_aspect_ratio_provided(self):
"""测试没有提供 aspect_ratio 的情况"""
# 当没有 aspect_ratio 时size 应该为 None
request_data = {
"resolution": "1K"
# 没有 aspect_ratio
}
mock_config = {
"resolutions": {
"1K": {"16:9": "1280*720"}
}
}
aspect_ratio = request_data.get("aspect_ratio")
assert aspect_ratio is None
# 控制器逻辑不会执行 resolution 解析
size = None
if aspect_ratio:
# 这不会执行
pass
assert size is None
def test_explicit_resolution_as_size_fallback(self):
"""测试显式 resolution 作为 size 回退(图片)"""
# 图片控制器有特殊逻辑:如果没有 aspect_ratio 但有 resolution直接作为 size
request_data = {
"resolution": "1920*1080"
}
size = None
if not size and request_data.get("resolution"):
size = request_data["resolution"]
assert size == "1920*1080"
def test_unknown_resolution_level(self):
"""测试未知的 resolution level"""
mock_config = {
"resolutions": {
"1K": {"16:9": "1280*720"}
}
}
request_data = {
"aspect_ratio": "16:9",
"resolution": "8K" # 不存在的 resolution
}
# 应该使用回退值
res_level = request_data.get("resolution") or "1K"
assert res_level == "8K"
resolutions_config = mock_config.get("resolutions", {})
if res_level not in resolutions_config:
# 使用硬编码回退
defaults = {"16:9": "1280*720"}
size = defaults.get(request_data["aspect_ratio"])
assert size == "1280*720"
if __name__ == "__main__":
# 运行测试
pytest.main([__file__, "-v"])