dcbb0f721e
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 20s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m20s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m45s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 30s
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 / PR Build Web Image (pull_request) Successful in 20s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 56s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m10s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m45s
AI Code Review / AI Code Review (pull_request) Successful in 2m36s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m30s
CI/CD Pipeline / Frontend Unit Tests (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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
- test_subtitle_render_engine.py: 53个(颜色转换/时间格式化/文字换行/ASS转义/样式) - test_sticker_engine.py: 27个(配置类/位置解析/常量/便捷函数) - test_tts_engine.py: 12个(数据类/入口判断/初始化) - test_render_audio_utils.py: 16个(clip_effective_duration/RenderContext) 覆盖worker层4个模块的纯逻辑部分
109 lines
4.1 KiB
Python
Executable File
109 lines
4.1 KiB
Python
Executable File
"""
|
||
render_audio 纯工具函数测试.
|
||
|
||
覆盖 clip_effective_duration / RenderContext 等纯逻辑.
|
||
核心混音逻辑依赖 FFmpeg,由集成测试覆盖.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from types import SimpleNamespace
|
||
from unittest.mock import MagicMock
|
||
|
||
import pytest
|
||
|
||
from video_processing.render_audio import RenderContext, clip_effective_duration
|
||
|
||
|
||
class TestClipEffectiveDuration:
|
||
"""clip_effective_duration 有效时长计算."""
|
||
|
||
def test_duration_specified_and_actual_longer(self):
|
||
"""指定了 duration,且实际时长更长 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=10.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_duration_specified_and_actual_shorter(self):
|
||
"""指定了 duration,但实际时长更短 → 取实际时长"""
|
||
clip = SimpleNamespace(duration=10.0, actual_duration=5.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_duration_specified_actual_zero(self):
|
||
"""指定了 duration,但实际时长为 0 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=0.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_duration_specified_actual_negative(self):
|
||
"""指定了 duration,但实际时长为负 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=-1.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_no_duration_use_actual(self):
|
||
"""未指定 duration(0),用实际时长"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=8.0)
|
||
assert clip_effective_duration(clip) == 8.0
|
||
|
||
def test_no_duration_negative_actual(self):
|
||
"""未指定 duration,实际时长也为负 → 返回 0"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=-5.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_no_duration_zero_actual(self):
|
||
"""都为 0 → 返回 0"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=0.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_negative_duration_use_actual(self):
|
||
"""duration 为负(视为未指定),用实际时长"""
|
||
clip = SimpleNamespace(duration=-1.0, actual_duration=5.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_both_negative_returns_zero(self):
|
||
"""两者都为负 → 返回 0"""
|
||
clip = SimpleNamespace(duration=-2.0, actual_duration=-3.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_exact_match(self):
|
||
"""duration 与实际时长相等"""
|
||
clip = SimpleNamespace(duration=7.5, actual_duration=7.5)
|
||
assert clip_effective_duration(clip) == 7.5
|
||
|
||
def test_very_short_duration(self):
|
||
"""很短的时长"""
|
||
clip = SimpleNamespace(duration=0.1, actual_duration=0.2)
|
||
assert clip_effective_duration(clip) == 0.1
|
||
|
||
|
||
class TestRenderContext:
|
||
"""RenderContext 渲染上下文."""
|
||
|
||
def test_create_with_work_dir_and_plan_id(self, tmp_path):
|
||
ctx = RenderContext(work_dir=tmp_path, plan_id="plan_123")
|
||
assert ctx.work_dir == tmp_path
|
||
assert ctx.plan_id == "plan_123"
|
||
assert ctx.noise_reduction_config is None
|
||
assert ctx._audio_cache == {}
|
||
|
||
def test_noise_reduction_config(self, tmp_path):
|
||
config = {"enabled": True, "strength": 0.5}
|
||
ctx = RenderContext(
|
||
work_dir=tmp_path,
|
||
plan_id="plan_123",
|
||
noise_reduction_config=config,
|
||
)
|
||
assert ctx.noise_reduction_config == config
|
||
assert ctx.noise_reduction_config["enabled"] is True
|
||
|
||
def test_audio_cache_isolation(self, tmp_path):
|
||
"""每个实例有独立的缓存字典."""
|
||
ctx1 = RenderContext(work_dir=tmp_path, plan_id="p1")
|
||
ctx2 = RenderContext(work_dir=tmp_path, plan_id="p2")
|
||
ctx1._audio_cache["key1"] = True
|
||
assert "key1" not in ctx2._audio_cache
|
||
assert len(ctx2._audio_cache) == 0
|
||
|
||
def test_work_dir_path_type(self, tmp_path):
|
||
ctx = RenderContext(work_dir=tmp_path, plan_id="test")
|
||
assert isinstance(ctx.work_dir, Path)
|