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

- 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:
CI Bot
2026-08-28 00:31:15 +08:00
parent 753a3206a7
commit d771fee45a
2 changed files with 79 additions and 24 deletions
@@ -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 []
+42
View File
@@ -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