Files
xiaoxia-saas/tests/unit/test_reverse_engine_pure.py
CI Bot 5fa915cdad
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
style: auto-format with black + isort + prettier
2026-07-26 08:29:38 +00:00

228 lines
8.7 KiB
Python
Executable File

"""ReverseEngine 纯逻辑单测 — 配置解析 + 滤镜构建 + 安全限制.
全纯函数测试,不依赖 FFmpeg。
"""
from __future__ import annotations
from unittest.mock import patch
import pytest
from video_processing.reverse_engine import ReverseConfig, ReverseEngine
# ── ReverseConfig 解析 ────────────────────────────────────────────────────
class TestReverseConfigFromDict:
"""ReverseConfig.from_dict 配置解析测试."""
def test_none_returns_disabled(self):
"""None 输入返回 disabled 默认配置."""
config = ReverseConfig.from_dict(None)
assert config.enabled is False
assert config.reverse_video is True
assert config.reverse_audio is True
def test_empty_dict_returns_disabled(self):
"""空 dict 返回 disabled."""
config = ReverseConfig.from_dict({})
assert config.enabled is False
def test_enabled_false_returns_disabled(self):
"""显式 enabled=False."""
config = ReverseConfig.from_dict({"enabled": False})
assert config.enabled is False
def test_enabled_default_flags_default_video_audio(self):
"""只启用时默认视频音频都倒放."""
config = ReverseConfig.from_dict({"enabled": True})
assert config.enabled is True
assert config.reverse_video is True
assert config.reverse_audio is True
def test_video_only(self):
"""只倒放视频."""
config = ReverseConfig.from_dict(
{
"enabled": True,
"reverse_video": True,
"reverse_audio": False,
}
)
assert config.enabled is True
assert config.reverse_video is True
assert config.reverse_audio is False
def test_audio_only(self):
"""只倒放音频."""
config = ReverseConfig.from_dict(
{
"enabled": True,
"reverse_video": False,
"reverse_audio": True,
}
)
assert config.enabled is True
assert config.reverse_video is False
assert config.reverse_audio is True
def test_both_disabled_but_enabled_flag_true(self):
"""enabled=True 但两个子选项都关了(边缘情况)."""
config = ReverseConfig.from_dict(
{
"enabled": True,
"reverse_video": False,
"reverse_audio": False,
}
)
assert config.enabled is True
assert config.reverse_video is False
assert config.reverse_audio is False
def test_invalid_type_falls_back(self):
"""非 dict 类型回退到默认 disabled."""
config = ReverseConfig.from_dict("reverse=true")
assert config.enabled is False
def test_attribute_error_falls_back(self):
"""属性错误时回退到默认."""
class WeirdObj:
def get(self, key, default=None):
raise AttributeError("nope")
config = ReverseConfig.from_dict(WeirdObj())
assert config.enabled is False
def test_type_error_falls_back(self):
"""类型错误时回退到默认."""
config = ReverseConfig.from_dict([1, 2, 3])
assert config.enabled is False
# ── ReverseEngine 视频滤镜 ──────────────────────────────────────────────────
class TestReverseEngineBuildVideoFilter:
"""ReverseEngine.build_video_filter 视频滤镜测试."""
def test_disabled_returns_empty(self):
"""disabled 返回空字符串."""
config = ReverseConfig(enabled=False)
assert ReverseEngine.build_video_filter(config) == ""
def test_enabled_returns_reverse(self):
"""启用返回 reverse 滤镜."""
config = ReverseConfig(enabled=True, reverse_video=True)
assert ReverseEngine.build_video_filter(config) == "reverse"
def test_video_disabled_returns_empty(self):
"""reverse_video=False 返回空."""
config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=True)
assert ReverseEngine.build_video_filter(config) == ""
def test_short_duration_ok(self):
"""短时长正常返回 reverse."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_video_filter(config, duration=10.0)
assert result == "reverse"
def test_exactly_max_duration_ok(self):
"""恰好等于安全上限正常."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_video_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION)
assert result == "reverse"
def test_over_max_duration_skipped(self):
"""超过安全时长跳过倒放."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_video_filter(config, duration=200.0)
assert result == ""
def test_zero_duration_ok(self):
"""零时长正常倒放."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_video_filter(config, duration=0.0)
assert result == "reverse"
# ── ReverseEngine 音频滤镜 ──────────────────────────────────────────────
class TestReverseEngineBuildAudioFilter:
"""ReverseEngine.build_audio_filter 音频滤镜测试."""
def test_disabled_returns_empty(self):
"""disabled 返回空字符串."""
config = ReverseConfig(enabled=False)
assert ReverseEngine.build_audio_filter(config) == ""
def test_enabled_returns_areverse(self):
"""启用返回 areverse 滤镜."""
config = ReverseConfig(enabled=True, reverse_audio=True)
assert ReverseEngine.build_audio_filter(config) == "areverse"
def test_audio_disabled_returns_empty(self):
"""reverse_audio=False 返回空."""
config = ReverseConfig(enabled=True, reverse_video=True, reverse_audio=False)
assert ReverseEngine.build_audio_filter(config) == ""
def test_short_duration_ok(self):
"""短时长正常返回 areverse."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_audio_filter(config, duration=30.0)
assert result == "areverse"
def test_over_max_duration_skipped(self):
"""超过安全时长跳过音频倒放."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_audio_filter(config, duration=150.0)
assert result == ""
def test_exactly_max_duration_ok(self):
"""恰好等于安全上限正常."""
config = ReverseConfig(enabled=True)
result = ReverseEngine.build_audio_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION)
assert result == "areverse"
# ── 组合场景 ────────────────────────────────────────────────────────────
class TestReverseEngineCombined:
"""组合场景测试."""
def test_both_video_audio_reverse(self):
"""视频音频都倒放."""
config = ReverseConfig(enabled=True)
vf = ReverseEngine.build_video_filter(config)
af = ReverseEngine.build_audio_filter(config)
assert vf == "reverse"
assert af == "areverse"
def test_neither_video_nor_audio(self):
"""都不倒放."""
config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=False)
assert ReverseEngine.build_video_filter(config) == ""
assert ReverseEngine.build_audio_filter(config) == ""
def test_long_video_both_skipped(self):
"""超长视频两个都跳过."""
config = ReverseConfig(enabled=True)
duration = ReverseEngine.MAX_SAFE_DURATION + 1
assert ReverseEngine.build_video_filter(config, duration=duration) == ""
assert ReverseEngine.build_audio_filter(config, duration=duration) == ""
def test_from_dict_full_config_flow(self):
"""从 dict 解析到滤镜构建的完整流程."""
data = {"enabled": True, "reverse_video": True, "reverse_audio": False}
config = ReverseConfig.from_dict(data)
assert ReverseEngine.build_video_filter(config, duration=10) == "reverse"
assert ReverseEngine.build_audio_filter(config, duration=10) == ""
def test_from_dict_disabled_flow(self):
"""disabled 配置完整流程."""
config = ReverseConfig.from_dict({"enabled": False})
assert ReverseEngine.build_video_filter(config) == ""
assert ReverseEngine.build_audio_filter(config) == ""