You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
AI Service API 测试数据 Fixtures 实现
日期: 2026-01-30
类型: 测试修复
状态: ✅ 完成
问题描述
集成测试失败,原因:
-
缺少定价配置数据:
- AI Service 调用
credit_service.calculate_credits()时需要从数据库查询定价配置 - 测试数据库中没有
CreditPricing表的数据 - 导致
NotFoundError: 未找到 image_generation 的定价配置
- AI Service 调用
-
缺少 AI 模型数据:
- AI Service 需要查询 AI 模型配置
- 测试数据库中没有
AIModel表的数据
-
事务嵌套问题:
- AI Service 中使用了
async with self.db.begin(): - 测试环境的 session 已经在事务中
- 导致
InvalidRequestError: A transaction is already begun on this Session
- AI Service 中使用了
解决方案
1. 添加定价配置 Fixtures
文件: server/tests/conftest.py
实现:
@pytest_asyncio.fixture
async def test_ai_pricing_configs(db_session: AsyncSession):
"""创建 AI 功能的定价配置"""
from app.models.credit import CreditPricing
from app.services.credit_service import FeatureType
from app.utils.id_generator import generate_uuid
from sqlmodel import select
# 先检查是否已存在,避免重复创建
result = await db_session.execute(
select(CreditPricing).where(
CreditPricing.feature_type == FeatureType.IMAGE_GENERATION
)
)
existing_pricing = result.scalar_one_or_none()
if existing_pricing:
result = await db_session.execute(select(CreditPricing))
return result.scalars().all()
pricing_configs = [
# 1. 图片生成定价
CreditPricing(
pricing_id=generate_uuid(),
feature_type=FeatureType.IMAGE_GENERATION,
pricing_rules={
"base": 10,
"hd_multiplier": 2,
"model_multipliers": {
"dall-e-3": 1.5,
"stable-diffusion": 1.0,
"midjourney": 2.0
}
},
description="图片生成定价",
is_active=True
),
# ... 其他 8 种功能类型的定价配置
]
for pricing in pricing_configs:
db_session.add(pricing)
await db_session.flush()
for pricing in pricing_configs:
await db_session.refresh(pricing)
return pricing_configs
覆盖的功能类型:
- IMAGE_GENERATION - 图片生成
- VIDEO_GENERATION - 视频生成
- SOUND_GENERATION - 音效生成
- VOICE_GENERATION - 配音生成
- SUBTITLE_GENERATION - 字幕生成
- TEXT_PROCESSING - 文本处理
- RESOURCE_GENERATION - 资源生成
- STORYBOARD_GENERATION - 分镜生成
- SCRIPT_GENERATION - 剧本生成
2. 添加 AI 模型 Fixtures
文件: server/tests/conftest.py
实现:
@pytest_asyncio.fixture
async def test_ai_models(db_session: AsyncSession):
"""创建测试 AI 模型配置"""
from app.models.ai_model import AIModel, AIModelType, AIProvider, UnitType
from app.utils.id_generator import generate_uuid
from decimal import Decimal
from sqlmodel import select
# 先检查是否已存在
result = await db_session.execute(
select(AIModel).where(AIModel.model_name == 'stable-diffusion-xl')
)
existing_model = result.scalar_one_or_none()
if existing_model:
result = await db_session.execute(select(AIModel))
return result.scalars().all()
models = [
# 图片生成模型
AIModel(
model_id=generate_uuid(),
model_name='stable-diffusion-xl',
display_name='Stable Diffusion XL',
description='高质量图片生成模型',
provider=AIProvider.STABILITY, # 使用枚举值
model_type=AIModelType.IMAGE,
cost_per_unit=Decimal('0.01'),
unit_type=UnitType.IMAGE,
credits_per_unit=10,
is_active=True,
is_beta=False,
config={'max_resolution': '1024x1024'}
),
# ... 其他 3 个模型
]
for model in models:
db_session.add(model)
await db_session.flush()
for model in models:
await db_session.refresh(model)
return models
包含的模型:
- stable-diffusion-xl - 图片生成(Stability AI)
- runway-gen2 - 视频生成(Runway)
- elevenlabs-tts - 语音合成(ElevenLabs)
- gpt-4 - 文本处理(OpenAI)
3. 更新测试用户积分余额
修改: test_credit_balance fixture
@pytest_asyncio.fixture
async def test_credit_balance(db_session: AsyncSession, test_user):
"""创建测试用户的积分余额"""
# 设置用户的积分余额(足够运行所有测试)
test_user.ai_credits_balance = 10000 # 从 1000 增加到 10000
await db_session.commit()
await db_session.refresh(test_user)
return test_user
4. 更新集成测试依赖
文件: server/tests/integration/test_ai_api_workflow.py
修改: 在所有测试方法中添加 test_ai_pricing_configs 和 test_ai_models 依赖
async def test_complete_image_generation_workflow(
self,
async_client: AsyncClient,
test_user_token: str,
test_user_id: str,
db_session,
test_ai_pricing_configs, # 添加
test_ai_models # 添加
):
"""测试完整的图片生成流程"""
# ...
遇到的问题
问题 1:Provider 字段类型错误
错误:
TypeError: 'str' object cannot be interpreted as an integer
invalid input for query argument $5: 'stability'
原因:
AIModel.provider字段定义为 SMALLINT(枚举值)- Fixture 中错误地使用了字符串
'stability'
解决:
使用 AIProvider 枚举值:
provider=AIProvider.STABILITY # ✅ 正确
provider='stability' # ❌ 错误
问题 2:唯一约束冲突
错误:
UniqueViolationError: duplicate key value violates unique constraint "ai_models_model_name_key"
原因:
- 测试数据库中已存在相同
model_name的记录 - 测试事务回滚机制可能未正常工作
解决: 在 fixture 中添加存在性检查:
# 先检查是否已存在
result = await db_session.execute(
select(AIModel).where(AIModel.model_name == 'stable-diffusion-xl')
)
existing_model = result.scalar_one_or_none()
if existing_model:
# 如果已存在,直接返回现有模型
result = await db_session.execute(select(AIModel))
return result.scalars().all()
问题 3:事务嵌套错误(待解决)
错误:
InvalidRequestError: A transaction is already begun on this Session
原因:
- AI Service 中使用了
async with self.db.begin(): - 测试环境的 session 已经在事务中(由
db_sessionfixture 创建) - SQLAlchemy 不允许嵌套事务(除非使用 savepoint)
位置:
ai_service.py:180-generate_image()ai_service.py:300-generate_video()ai_service.py:399-generate_sound()ai_service.py:459-generate_voice()ai_service.py:519-generate_subtitle()ai_service.py:581-process_text()
可能的解决方案:
方案 A:移除 Service 层的事务管理(推荐)
# 移除 async with self.db.begin():
# 让调用者(API 层或测试)控制事务
# 修改前
async with self.db.begin():
consumption_log = await self.credit_service.consume_credits(...)
job = await self.job_repository.create(...)
# 修改后
consumption_log = await self.credit_service.consume_credits(...)
job = await self.job_repository.create(...)
# 由调用者负责 commit/rollback
方案 B:使用 Savepoint
# 使用 savepoint 代替 begin
async with self.db.begin_nested(): # 创建 savepoint
consumption_log = await self.credit_service.consume_credits(...)
job = await self.job_repository.create(...)
方案 C:条件性事务
# 检查是否已在事务中
if self.db.in_transaction():
# 已在事务中,直接执行
consumption_log = await self.credit_service.consume_credits(...)
else:
# 未在事务中,开启新事务
async with self.db.begin():
consumption_log = await self.credit_service.consume_credits(...)
测试数据结构
CreditPricing 表
| 字段 | 类型 | 说明 |
|---|---|---|
| pricing_id | UUID | 主键 |
| feature_type | SMALLINT | 功能类型(1-10) |
| pricing_rules | JSONB | 定价规则 |
| description | TEXT | 描述 |
| is_active | BOOLEAN | 是否启用 |
定价规则示例:
{
"base": 10,
"hd_multiplier": 2,
"model_multipliers": {
"dall-e-3": 1.5,
"stable-diffusion": 1.0
}
}
AIModel 表
| 字段 | 类型 | 说明 |
|---|---|---|
| model_id | UUID | 主键 |
| model_name | VARCHAR | 模型名称(唯一) |
| display_name | VARCHAR | 显示名称 |
| provider | SMALLINT | 提供商(枚举) |
| model_type | SMALLINT | 模型类型(枚举) |
| cost_per_unit | NUMERIC(10,4) | 单位成本 |
| unit_type | SMALLINT | 单位类型(枚举) |
| credits_per_unit | INTEGER | 每单位积分 |
| config | JSONB | 模型配置 |
| is_active | BOOLEAN | 是否启用 |
下一步
- ✅ 添加定价配置 fixtures
- ✅ 添加 AI 模型 fixtures
- ✅ 更新测试用户积分余额
- ✅ 更新集成测试依赖
- ⏳ 解决事务嵌套问题
- ⏳ 运行完整的集成测试套件
- ⏳ 创建测试执行报告
技术栈合规性
- ✅ 使用 SMALLINT 存储枚举值
- ✅ 使用 JSONB 存储配置数据
- ✅ 使用 TIMESTAMPTZ 存储时间
- ✅ 使用 UUID v7 作为主键
- ✅ 使用 pytest-asyncio 进行异步测试
- ✅ 使用 fixture 管理测试数据
- ✅ 使用事务回滚确保测试隔离