fix: validate segment durations with _safe_segment_duration + upgrade fallback logs to warning
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m5s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m9s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 4m27s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m21s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m36s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m36s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 5m56s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 470h51m10s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 470h51m11s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 470h54m10s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 470h51m11s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 470h54m11s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 470h54m16s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 470h54m14s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 470h54m15s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 471h28m9s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 471h28m16s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m5s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m9s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 4m27s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m21s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m36s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m36s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 5m56s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 470h51m10s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 470h51m11s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 470h54m10s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 470h51m11s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 470h54m11s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 470h54m16s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 470h54m14s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 470h54m15s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 471h28m9s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 471h28m16s
- Add _safe_segment_duration() to handle None/invalid/negative/NaN values - Swap dur_min/dur_max if dirty data has min > max - Change fallback exception logs from DEBUG to WARNING for observability - Add 7 unit tests for _safe_segment_duration - 47 tests passing
This commit is contained in:
@@ -360,6 +360,22 @@ def batch_delete_editor_clips(
|
||||
|
||||
|
||||
|
||||
def _safe_segment_duration(value, default: float) -> float:
|
||||
"""安全地将数据库中的时长值转换为正浮点数.
|
||||
|
||||
处理 None、无效类型、负数、NaN 等异常情况。
|
||||
"""
|
||||
if value is None:
|
||||
return default
|
||||
try:
|
||||
result = float(value)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
if result != result or result <= 0: # NaN check or non-positive
|
||||
return default
|
||||
return result
|
||||
|
||||
|
||||
def _get_template_segments(
|
||||
template_id: str,
|
||||
tpl_svc: EditTemplateService,
|
||||
@@ -377,38 +393,35 @@ def _get_template_segments(
|
||||
try:
|
||||
clip_configs = tpl_svc.list_clip_configs(template_id)
|
||||
if clip_configs:
|
||||
return sorted(
|
||||
[
|
||||
(
|
||||
cc.order,
|
||||
float(cc.min_duration or _DEFAULT_EDITOR_CLIP_DURATION),
|
||||
float(
|
||||
cc.max_duration
|
||||
or cc.min_duration
|
||||
or _DEFAULT_EDITOR_CLIP_DURATION
|
||||
),
|
||||
)
|
||||
for cc in clip_configs
|
||||
],
|
||||
key=lambda x: x[0],
|
||||
)
|
||||
result = []
|
||||
for cc in clip_configs:
|
||||
dur_min = _safe_segment_duration(
|
||||
cc.min_duration, _DEFAULT_EDITOR_CLIP_DURATION
|
||||
)
|
||||
dur_max = _safe_segment_duration(
|
||||
cc.max_duration or cc.min_duration,
|
||||
_DEFAULT_EDITOR_CLIP_DURATION,
|
||||
)
|
||||
dur_min, dur_max = min(dur_min, dur_max), max(dur_min, dur_max)
|
||||
result.append((cc.order, dur_min, dur_max))
|
||||
return sorted(result, key=lambda x: x[0])
|
||||
except Exception:
|
||||
logger.debug("新模板系统查询clip_configs失败,回退到旧系统", exc_info=True)
|
||||
logger.warning("新模板系统查询clip_configs失败,回退到旧系统", exc_info=True)
|
||||
|
||||
# 回退到旧模板系统(template_segments表)
|
||||
try:
|
||||
old_repo = SQLAlchemyTemplateRepository(db)
|
||||
segments = old_repo.list_segments(template_id)
|
||||
if segments:
|
||||
return sorted(
|
||||
[
|
||||
(s.segment_order, float(s.duration_min), float(s.duration_max))
|
||||
for s in segments
|
||||
],
|
||||
key=lambda x: x[0],
|
||||
)
|
||||
result = []
|
||||
for s in segments:
|
||||
dur_min = _safe_segment_duration(s.duration_min, 0.0)
|
||||
dur_max = _safe_segment_duration(s.duration_max, 0.0)
|
||||
dur_min, dur_max = min(dur_min, dur_max), max(dur_min, dur_max)
|
||||
result.append((s.segment_order, dur_min, dur_max))
|
||||
return sorted(result, key=lambda x: x[0])
|
||||
except Exception:
|
||||
logger.debug("旧模板系统查询segments失败", exc_info=True)
|
||||
logger.warning("旧模板系统查询segments失败", exc_info=True)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -618,3 +618,45 @@ class TestFromAssetsByTemplateSegments:
|
||||
assert clips_data[0]["start_time"] == 15.0
|
||||
# 第二个片段(同一素材)不应使用推荐时间
|
||||
assert clips_data[1]["start_time"] != 15.0
|
||||
|
||||
# ── _safe_segment_duration 单元测试 ─────────────────────────────────────────
|
||||
|
||||
|
||||
class TestSafeSegmentDuration:
|
||||
"""测试片段时长安全转换。"""
|
||||
|
||||
def test_normal_float(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration(3.5, 1.0) == 3.5
|
||||
|
||||
def test_none_returns_default(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration(None, 5.0) == 5.0
|
||||
|
||||
def test_string_number(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration("4.2", 1.0) == 4.2
|
||||
|
||||
def test_invalid_string_returns_default(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration("abc", 5.0) == 5.0
|
||||
|
||||
def test_negative_returns_default(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration(-1.0, 5.0) == 5.0
|
||||
|
||||
def test_zero_returns_default(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration(0, 5.0) == 5.0
|
||||
|
||||
def test_integer_value(self):
|
||||
from app.api.routes.templates_editor.clips import _safe_segment_duration
|
||||
|
||||
assert _safe_segment_duration(10, 1.0) == 10.0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user