test(p0): add unit tests for default template fallback (diff cov gate)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m25s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 24s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 4m39s
AI Code Review / AI Code Review (pull_request) Successful in 6m31s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m45s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m57s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m11s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 8m56s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 39s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 57s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 28h38m5s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 28h46m40s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 28h46m58s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 28h47m24s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 28h46m30s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 28h46m3s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 28h46m3s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 28h37m27s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 28h46m9s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 28h46m21s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 28h46m21s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 28h46m47s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 28h50m36s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m25s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 24s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 4m39s
AI Code Review / AI Code Review (pull_request) Successful in 6m31s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m45s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m57s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m11s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 8m56s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 39s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 57s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 28h38m5s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 28h46m40s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 28h46m58s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 28h47m24s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 28h50m35s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 28h37m29s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 28h46m30s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 28h46m3s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 28h46m3s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 28h37m27s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 28h46m9s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 28h46m21s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 28h46m21s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 28h46m47s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 28h50m36s
- 覆盖 resolve_draft_plan_id 空tid兜底/400/404/草稿复用/全局模板分支 - 满足 diff coverage ≥40% 门槛
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
"""P0 #1922 默认模板自动兜底单元测试。
|
||||
|
||||
覆盖 resolve_draft_plan_id 的核心路径(使用可mock的外部依赖):
|
||||
- 空 tid 时调用 get_or_create_default_template_id 兜底
|
||||
- auto_create_default=False 时空tid抛400
|
||||
- 模板不存在/无权访问抛404
|
||||
- 已有草稿直接返回 plan_id
|
||||
- 旧模板(templates表)走 generate_from_template 创建草稿
|
||||
|
||||
_get_or_create_default_template_id 是薄SQL封装,其逻辑分支通过依赖注入路径间接覆盖;
|
||||
直接SQL分支通过integration test/容器验证,不做单元级mock(避免内部import脆弱mock)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from app.api.routes.templates_editor.dependencies import resolve_draft_plan_id
|
||||
|
||||
|
||||
class TestResolveDraftPlanId:
|
||||
"""resolve_draft_plan_id 行为。"""
|
||||
|
||||
def _make_services(self, global_tpl=None, draft=None):
|
||||
tpl_svc = MagicMock()
|
||||
plan_svc = MagicMock()
|
||||
tpl_svc.get_template.return_value = global_tpl
|
||||
tpl_svc.get_template_draft.return_value = draft
|
||||
return (tpl_svc, plan_svc)
|
||||
|
||||
def _make_user(self, uid: str = "user-1"):
|
||||
u = MagicMock()
|
||||
u.user.id = uid
|
||||
return u
|
||||
|
||||
def test_empty_tid_auto_fallback_creates_default_then_draft(self):
|
||||
"""template_id 为空 + auto_create_default=True:先兜底拿到tid,再走旧模板草稿创建。"""
|
||||
db = MagicMock()
|
||||
services = self._make_services()
|
||||
user = self._make_user()
|
||||
old_tpl = MagicMock()
|
||||
old_tpl.id = "tpl-auto"
|
||||
old_tpl.name = "默认配音模板"
|
||||
old_tpl.mode = "voice_over"
|
||||
old_tpl.segments = [MagicMock(id="seg-1", segment_order=0, duration_min=1.0, duration_max=30.0)]
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_active.return_value = old_tpl
|
||||
mock_plan = MagicMock()
|
||||
mock_plan.id = "plan-new"
|
||||
mock_generator = MagicMock()
|
||||
mock_generator.generate_from_template.return_value = {"plan": mock_plan}
|
||||
|
||||
with (
|
||||
patch("app.api.routes.templates_editor.dependencies.SQLAlchemyTemplateRepository", return_value=mock_repo),
|
||||
patch(
|
||||
"app.api.routes.templates_editor.dependencies.get_or_create_default_template_id",
|
||||
return_value="tpl-auto",
|
||||
) as mock_fb,
|
||||
patch("app.services.plan_generator_service.PlanGeneratorService", return_value=mock_generator),
|
||||
):
|
||||
plan_id = resolve_draft_plan_id(
|
||||
template_id="",
|
||||
services=services,
|
||||
current_user=user,
|
||||
db=db,
|
||||
auto_create_default=True,
|
||||
)
|
||||
|
||||
assert plan_id == "plan-new"
|
||||
mock_fb.assert_called_once_with(db, "user-1")
|
||||
|
||||
def test_empty_tid_without_auto_raises_400(self):
|
||||
"""auto_create_default=False 且 tid 为空 → 400。"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
db = MagicMock()
|
||||
services = self._make_services()
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
resolve_draft_plan_id(
|
||||
template_id="",
|
||||
services=services,
|
||||
current_user=self._make_user(),
|
||||
db=db,
|
||||
auto_create_default=False,
|
||||
)
|
||||
assert exc.value.status_code == 400
|
||||
|
||||
def test_missing_template_raises_404(self):
|
||||
"""tid 存在但模板找不到/无权访问 → 404。"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
db = MagicMock()
|
||||
services = self._make_services() # get_template returns None
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_active.return_value = None
|
||||
with patch("app.api.routes.templates_editor.dependencies.SQLAlchemyTemplateRepository", return_value=mock_repo):
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
resolve_draft_plan_id(
|
||||
template_id="tpl-ghost",
|
||||
services=services,
|
||||
current_user=self._make_user(),
|
||||
db=db,
|
||||
auto_create_default=False,
|
||||
)
|
||||
assert exc.value.status_code == 404
|
||||
|
||||
def test_existing_draft_returned_directly(self):
|
||||
"""已有模板草稿直接返回 plan_id,不重复创建。"""
|
||||
db = MagicMock()
|
||||
draft = MagicMock()
|
||||
draft.id = "plan-existing"
|
||||
services = self._make_services(draft=draft)
|
||||
old_tpl = MagicMock()
|
||||
old_tpl.id = "tpl-1"
|
||||
old_tpl.segments = []
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_active.return_value = old_tpl
|
||||
with patch("app.api.routes.templates_editor.dependencies.SQLAlchemyTemplateRepository", return_value=mock_repo):
|
||||
plan_id = resolve_draft_plan_id(
|
||||
template_id="tpl-1",
|
||||
services=services,
|
||||
current_user=self._make_user(),
|
||||
db=db,
|
||||
auto_create_default=False,
|
||||
)
|
||||
assert plan_id == "plan-existing"
|
||||
# 没触发generate
|
||||
services[1].update_plan_config.assert_not_called()
|
||||
|
||||
def test_global_template_creates_draft_via_new_service(self):
|
||||
"""全局模板(新系统edit_templates表存在)走tpl_svc.create_template_draft。"""
|
||||
db = MagicMock()
|
||||
services = self._make_services(global_tpl=MagicMock(), draft=None)
|
||||
new_draft = MagicMock()
|
||||
new_draft.id = "plan-global"
|
||||
services[0].create_template_draft.return_value = new_draft
|
||||
plan_id = resolve_draft_plan_id(
|
||||
template_id="tpl-global",
|
||||
services=services,
|
||||
current_user=self._make_user(),
|
||||
db=db,
|
||||
auto_create_default=False,
|
||||
)
|
||||
assert plan_id == "plan-global"
|
||||
services[0].create_template_draft.assert_called_once_with("tpl-global", user_id="user-1")
|
||||
Reference in New Issue
Block a user