6cfab71d6e
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 4s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 5s
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
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m43s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m16s
CI/CD Pipeline / Build Staging Web Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 56s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m35s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m25s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m1s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m8s
CI/CD Pipeline / Integration Tests (push) Successful in 3m53s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 5s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Successful in 24s
CI/CD Pipeline / Validate - Style (push) Successful in 5m33s
AI Code Review / AI Code Review (pull_request) Successful in 6m54s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m49s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m15s
CI/CD Pipeline / Validate - Security (push) Successful in 9m17s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m5s
CI/CD Pipeline / Unit Tests (push) Successful in 12m12s
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 / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
166 lines
6.3 KiB
Python
166 lines
6.3 KiB
Python
"""#2308 parser字段映射单测:
|
||
1. 新schema XML(<shot>+<visual>+<action_details>)字段正确映射
|
||
2. 旧schema XML(<visual>混写)能拆分出cam/visual/action
|
||
3. markdown中文分镜字段正确提取
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from unittest.mock import MagicMock
|
||
|
||
import pytest
|
||
|
||
|
||
@pytest.fixture
|
||
def mock_job():
|
||
job = MagicMock()
|
||
job.duration = 15
|
||
job.video_ratio = "9:16"
|
||
job.marketing_purpose = "品牌推广"
|
||
job.image_analysis = {"images": []}
|
||
job.images = []
|
||
job.user_copy_text = ""
|
||
job.fusion_level = "ai_polish"
|
||
return job
|
||
|
||
|
||
def _import_wv(monkeypatch):
|
||
for mod in ["worker_app.celery_app", "worker_app.db"]:
|
||
monkeypatch.setitem(sys.modules, mod, MagicMock())
|
||
import celery
|
||
|
||
monkeypatch.setattr(celery, "shared_task", lambda **kw: (lambda f: f))
|
||
from worker_app.tasks import viral_video as wv
|
||
|
||
return wv
|
||
|
||
|
||
# === 1. 新schema(v3.1+):独立 <shot>/<visual>/<action_details> ===
|
||
NEW_SCHEMA_XML = """<script>
|
||
<copy_display_markdown><![CDATA[来御众堂放松一下]]></copy_display_markdown>
|
||
<clips>
|
||
<clip index="1">
|
||
<time_range>0-4秒</time_range>
|
||
<voiceover>生完娃,是不是连照镜子都没勇气了?</voiceover>
|
||
<shot>中近景平视,50mm f/2.0,缓慢推镜</shot>
|
||
<visual>暖光浴室镜前,妈妈略显疲惫地看向镜中自己,镜灯打在侧脸</visual>
|
||
<action_details>妈妈手扶洗手台,微微低头再抬眼看镜,眼眶微红</action_details>
|
||
<reference_image_index>0</reference_image_index>
|
||
</clip>
|
||
<clip index="2">
|
||
<time_range>4-10秒</time_range>
|
||
<voiceover>推开门,暖光和一杯热茶先接住了你。</voiceover>
|
||
<shot>中景平视,35mm f/2.8,缓慢平移</shot>
|
||
<visual>门店前台暖光木色装修,茶水冒热气,背景柔和虚化</visual>
|
||
<action_details>店员微笑双手递上茶杯,妈妈伸手接过,指尖轻触杯沿</action_details>
|
||
<reference_image_index>1</reference_image_index>
|
||
</clip>
|
||
</clips>
|
||
<voiceover_script>生完娃,是不是连照镜子都没勇气了?推开门,暖光和一杯热茶先接住了你。</voiceover_script>
|
||
<theme>产后妈妈走进御众堂</theme>
|
||
</script>"""
|
||
|
||
|
||
def test_new_schema_xml_field_mapping(monkeypatch, mock_job):
|
||
wv = _import_wv(monkeypatch)
|
||
r = wv._parse_script(NEW_SCHEMA_XML, mock_job)
|
||
assert r is not None
|
||
assert len(r["shots"]) == 2
|
||
s0 = r["shots"][0]
|
||
# shot_type 应是纯摄影参数
|
||
assert "缓慢推镜" in s0["shot_type_angle_movement"]
|
||
assert "妈妈" not in s0["shot_type_angle_movement"]
|
||
# scene_and_dialogue 只放对白
|
||
assert s0["scene_and_dialogue"] == "生完娃,是不是连照镜子都没勇气了?"
|
||
# action_details 有内容
|
||
assert "手扶洗手台" in s0["action_details"]
|
||
# visual 字段有画面
|
||
assert "暖光" in s0["visual"]
|
||
assert "推镜" not in s0["visual"]
|
||
|
||
|
||
# === 2. 旧schema(无<shot>/<action_details>标签,<visual>混写):兼容拆分 ===
|
||
OLD_SCHEMA_XML = """<script>
|
||
<clips>
|
||
<clip index="1">
|
||
<time_range>0-4秒</time_range>
|
||
<voiceover>生完娃,是不是连照镜子都没勇气了?</voiceover>
|
||
<visual>中近景平视,暖光下妈妈略显疲惫地看向镜中,镜头缓缓推近,妈妈手扶洗手台微微低头</visual>
|
||
<reference_image_index>0</reference_image_index>
|
||
</clip>
|
||
</clips>
|
||
<voiceover_script>生完娃,是不是连照镜子都没勇气了?</voiceover_script>
|
||
<theme>测试</theme>
|
||
</script>"""
|
||
|
||
|
||
def test_old_schema_xml_splits_visual(monkeypatch, mock_job):
|
||
wv = _import_wv(monkeypatch)
|
||
r = wv._parse_script(OLD_SCHEMA_XML, mock_job)
|
||
assert r is not None
|
||
s0 = r["shots"][0]
|
||
# 摄影参数段应含景别/运镜关键词
|
||
assert "中近景" in s0["shot_type_angle_movement"] or "推" in s0["shot_type_angle_movement"]
|
||
# action_details 应含动作动词
|
||
assert s0["action_details"], "action_details不应为空(旧schema兼容应从<visual>拆出动作)"
|
||
assert "手扶" in s0["action_details"] or "低头" in s0["action_details"]
|
||
# scene_and_dialogue 只放对白,不重复画面
|
||
assert s0["scene_and_dialogue"] == "生完娃,是不是连照镜子都没勇气了?"
|
||
|
||
|
||
# === 3. markdown 中文分镜字段 ===
|
||
MARKDOWN_INPUT = """## 视频主题:御众堂探店
|
||
|
||
### 镜头1
|
||
- 时间范围:0-4秒
|
||
- 景别/角度与运镜:中近景平视,缓慢推镜
|
||
- 画面:暖光下妈妈看向镜中,镜灯打在侧脸
|
||
- 动作与真人细节:妈妈手扶洗手台,微微低头
|
||
- 口播:"生完娃,是不是连照镜子都没勇气了?"
|
||
|
||
### 镜头2
|
||
- 时间范围:4-10秒
|
||
- 景别:中景平视
|
||
- 运镜:缓慢平移
|
||
- 画面:门店前台暖光木色装修
|
||
- 动作:店员递茶,妈妈伸手接过
|
||
- 口播:"推开门,暖光和一杯热茶先接住了你"
|
||
"""
|
||
|
||
|
||
def test_markdown_field_extraction(monkeypatch, mock_job):
|
||
wv = _import_wv(monkeypatch)
|
||
r = wv._parse_script(MARKDOWN_INPUT, mock_job)
|
||
assert r is not None
|
||
assert len(r["shots"]) == 2
|
||
s0 = r["shots"][0]
|
||
assert "中近景" in s0["shot_type_angle_movement"]
|
||
assert "妈妈手扶洗手台" in s0["action_details"]
|
||
assert s0["scene_and_dialogue"] == "生完娃,是不是连照镜子都没勇气了?"
|
||
|
||
|
||
# === 4. _split_visual_field 单元测试 ===
|
||
def test_split_visual_field(monkeypatch):
|
||
wv = _import_wv(monkeypatch)
|
||
# 典型旧schema混写:"中近景平视,暖光下一位妈妈略显疲惫地看向镜中,镜头缓缓推近,妈妈手扶洗手台微微低头"
|
||
cam, vis, act = wv._split_visual_field(
|
||
"中近景平视,暖光下妈妈略显疲惫地看向镜中,镜头缓缓推近,妈妈手扶洗手台微微低头"
|
||
)
|
||
assert cam and "中近景" in cam
|
||
# 动作/表情段应被拆到 act(含"妈妈"主语+动词)
|
||
assert act and ("手扶" in act or "疲惫" in act or "看向" in act)
|
||
# 画面描述允许为空(当动作段本身承载了主要画面信息时),或含"暖光"
|
||
assert (not vis) or ("暖光" in vis) or ("妈妈" in vis)
|
||
|
||
|
||
def test_split_visual_short(monkeypatch):
|
||
wv = _import_wv(monkeypatch)
|
||
cam, vis, act = wv._split_visual_field("特写固定镜头")
|
||
# 切不开时整体归camera/visual,action为空
|
||
assert "特写" in cam
|
||
|
||
|
||
if __name__ == "__main__":
|
||
pytest.main([__file__, "-v"])
|