474d7d77e5
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m8s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m12s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m48s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m55s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m48s
CI/CD Pipeline / Integration Tests (push) Successful in 1m41s
CI/CD Pipeline / Unit Tests (push) Successful in 9m21s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 19m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m3s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 39s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 51s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m48s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
191 lines
6.3 KiB
Python
191 lines
6.3 KiB
Python
"""Tests for preview title_config feature.
|
|
|
|
验证预览 API 的 title_config 字段和 Worker 的标题配置解析逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
|
|
class TestPreviewTitleConfigSchema:
|
|
"""测试 CreatePreviewGenerationTaskRequest 的 title_config 字段."""
|
|
|
|
def test_title_config_default_empty(self):
|
|
"""title_config 默认为空 dict."""
|
|
from app.schemas.generation_task import CreatePreviewGenerationTaskRequest
|
|
|
|
req = CreatePreviewGenerationTaskRequest(
|
|
template_id="test_template",
|
|
asset_ids=["asset1"],
|
|
)
|
|
assert req.title_config == {}
|
|
|
|
def test_title_config_with_text(self):
|
|
"""传入标题文本."""
|
|
from app.schemas.generation_task import CreatePreviewGenerationTaskRequest
|
|
|
|
req = CreatePreviewGenerationTaskRequest(
|
|
template_id="test_template",
|
|
asset_ids=["asset1"],
|
|
title_config={"text": "测试标题"},
|
|
)
|
|
assert req.title_config["text"] == "测试标题"
|
|
|
|
def test_title_config_with_full_style(self):
|
|
"""传入完整标题样式配置."""
|
|
from app.schemas.generation_task import CreatePreviewGenerationTaskRequest
|
|
|
|
config = {
|
|
"text": "我的视频标题",
|
|
"font": "思源黑体",
|
|
"font_size": 48,
|
|
"font_color": "#ffffff",
|
|
"position": "top",
|
|
"bold": True,
|
|
"stroke": 2,
|
|
"shadow": True,
|
|
}
|
|
req = CreatePreviewGenerationTaskRequest(
|
|
template_id="test_template",
|
|
asset_ids=["asset1"],
|
|
title_config=config,
|
|
)
|
|
assert req.title_config["text"] == "我的视频标题"
|
|
assert req.title_config["font_size"] == 48
|
|
assert req.title_config["position"] == "top"
|
|
|
|
|
|
class TestCommandTitleConfig:
|
|
"""测试 CreateGenerationTaskCommand 的 title_config 字段."""
|
|
|
|
def test_command_has_title_config(self):
|
|
"""Command 包含 title_config 字段."""
|
|
from packages.application.generation_tasks import CreateGenerationTaskCommand
|
|
|
|
cmd = CreateGenerationTaskCommand(
|
|
title_config={"text": "hello", "font_size": 32},
|
|
)
|
|
assert cmd.title_config["text"] == "hello"
|
|
assert cmd.title_config["font_size"] == 32
|
|
|
|
def test_command_title_config_default_empty(self):
|
|
"""Command 的 title_config 默认为空 dict."""
|
|
from packages.application.generation_tasks import CreateGenerationTaskCommand
|
|
|
|
cmd = CreateGenerationTaskCommand()
|
|
assert cmd.title_config == {}
|
|
|
|
|
|
class TestWorkerTitleConfigParsing:
|
|
"""测试 Worker 渲染时的标题配置解析逻辑."""
|
|
|
|
def test_json_format_parsing(self):
|
|
"""JSON 格式的 custom_title 能正确解析."""
|
|
config = {"text": "测试标题", "font_size": 48, "font_color": "#ff0000"}
|
|
custom_title = json.dumps(config, ensure_ascii=False)
|
|
|
|
ct_stripped = custom_title.strip()
|
|
parsed = None
|
|
if ct_stripped.startswith("{"):
|
|
try:
|
|
parsed = json.loads(ct_stripped)
|
|
except (json.JSONDecodeError, ValueError):
|
|
parsed = None
|
|
|
|
assert parsed is not None
|
|
assert parsed["text"] == "测试标题"
|
|
assert parsed["font_size"] == 48
|
|
|
|
def test_plain_text_fallback(self):
|
|
"""纯文本的 custom_title 不触发 JSON 解析."""
|
|
custom_title = "简单的标题文字"
|
|
|
|
ct_stripped = custom_title.strip()
|
|
parsed = None
|
|
if ct_stripped.startswith("{"):
|
|
try:
|
|
parsed = json.loads(ct_stripped)
|
|
except (json.JSONDecodeError, ValueError):
|
|
parsed = None
|
|
|
|
assert parsed is None
|
|
|
|
def test_invalid_json_fallback(self):
|
|
"""无效 JSON 的 custom_title 降级为纯文本."""
|
|
custom_title = "{invalid json"
|
|
|
|
ct_stripped = custom_title.strip()
|
|
parsed = None
|
|
if ct_stripped.startswith("{"):
|
|
try:
|
|
parsed = json.loads(ct_stripped)
|
|
except (json.JSONDecodeError, ValueError):
|
|
parsed = None
|
|
|
|
assert parsed is None
|
|
|
|
def test_json_without_text_skipped(self):
|
|
"""JSON 格式但缺少 text 字段时,跳过标题注入."""
|
|
config = {"font_size": 48}
|
|
custom_title = json.dumps(config, ensure_ascii=False)
|
|
|
|
ct_stripped = custom_title.strip()
|
|
parsed = json.loads(ct_stripped)
|
|
title_text = (parsed.get("text") or "").strip()
|
|
|
|
assert title_text == ""
|
|
|
|
def test_style_key_mapping(self):
|
|
"""前端字段名正确映射到 ASS 字段名."""
|
|
config = {
|
|
"text": "标题",
|
|
"font_size": 48,
|
|
"font_color": "#ffffff",
|
|
"font_preset": "思源黑体",
|
|
}
|
|
|
|
style_keys = ["font", "font_size", "font_color", "position", "bold", "stroke", "shadow", "font_preset"]
|
|
title_cfg = {}
|
|
for key in style_keys:
|
|
if key in config and config[key] is not None:
|
|
mapped_key = {
|
|
"font_size": "size",
|
|
"font_color": "color",
|
|
"font_preset": "font",
|
|
}.get(key, key)
|
|
title_cfg[mapped_key] = config[key]
|
|
|
|
assert title_cfg["size"] == 48
|
|
assert title_cfg["color"] == "#ffffff"
|
|
assert title_cfg["font"] == "思源黑体"
|
|
|
|
|
|
class TestPreviewRouteTitleConfigPassing:
|
|
"""测试预览路由正确序列化 title_config 到 custom_title."""
|
|
|
|
def test_title_config_serialization(self):
|
|
"""title_config 序列化为 JSON 字符串."""
|
|
title_config = {
|
|
"text": "我的标题",
|
|
"font_size": 32,
|
|
"font_color": "#d4a843",
|
|
}
|
|
serialized = json.dumps(title_config, ensure_ascii=False)
|
|
|
|
parsed = json.loads(serialized)
|
|
assert parsed["text"] == "我的标题"
|
|
assert parsed["font_size"] == 32
|
|
|
|
def test_empty_title_config_produces_empty_string(self):
|
|
"""空 title_config 时 custom_title 为空字符串."""
|
|
title_config = {}
|
|
title_text = (title_config.get("text") or "").strip()
|
|
custom_title_value = ""
|
|
if title_text:
|
|
custom_title_value = json.dumps(title_config, ensure_ascii=False)
|
|
|
|
assert custom_title_value == ""
|