test: 补充plan_id兜底逻辑执行测试,diff覆盖率100%
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 / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (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 / Check if frontend-only change (pull_request) Successful in 50s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 45s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m50s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m51s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m33s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m48s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
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
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
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 / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (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 / Check if frontend-only change (pull_request) Successful in 50s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 45s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m50s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m51s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m33s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m48s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
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
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
新增3个端到端测试覆盖generation_tasks.py的兜底关联逻辑: - test_fallback_sets_plan_id_when_empty: 验证空plan_id时自动查找并关联 - test_no_fallback_when_plan_id_already_set: 验证已有plan_id时不触发兜底 - test_fallback_handles_exception_gracefully: 验证查找异常不阻塞主流程 总计10个回归测试,diff覆盖率从7%提升到100%(14/14行全覆盖)
This commit is contained in:
+19875
File diff suppressed because it is too large
Load Diff
@@ -187,3 +187,229 @@ class TestPlanIdFallback:
|
||||
repo = SQLAlchemyEditPlanRepository(mock_db)
|
||||
result = repo.list_by_template("tpl-1", limit=20)
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestPlanIdFallbackExecution:
|
||||
"""Test that the fallback logic actually executes when source_edit_plan_id is empty."""
|
||||
|
||||
@staticmethod
|
||||
def _make_mock_task(source_edit_plan_id=""):
|
||||
t = MagicMock()
|
||||
t.id = "task-1"
|
||||
t.project_id = "proj-1"
|
||||
t.asset_library_id = ""
|
||||
t.strategy_id = "one_take"
|
||||
t.voice_library_id = ""
|
||||
t.template_id = "tpl-1"
|
||||
t.asset_ids = []
|
||||
t.title_ids = []
|
||||
t.voice_ids = []
|
||||
t.source_edit_plan_id = source_edit_plan_id
|
||||
t.asset_select_mode = "manual"
|
||||
t.batch_id = ""
|
||||
t.video_title = ""
|
||||
t.resolution = ""
|
||||
t.bgm_config = None
|
||||
t.is_preview = False
|
||||
t.source_task_id = ""
|
||||
t.output_width = 1280
|
||||
t.output_height = 720
|
||||
t.cover_url = ""
|
||||
t.custom_title = ""
|
||||
t.title_config = {}
|
||||
t.logs = "[]"
|
||||
t.status = "pending"
|
||||
t.progress = 0.0
|
||||
t.error_message = ""
|
||||
t.error_info = None
|
||||
t.created_at = "2026-01-01T00:00:00Z"
|
||||
t.updated_at = "2026-01-01T00:00:00Z"
|
||||
t.started_at = None
|
||||
t.completed_at = None
|
||||
t.created_by_user_id = "user-1"
|
||||
t.auto_retry_enabled = False
|
||||
t.auto_retry_max = 0
|
||||
t.auto_retry_count = 0
|
||||
return t
|
||||
|
||||
@staticmethod
|
||||
def _make_request(source_edit_plan_id="", template_id="tpl-1"):
|
||||
req = MagicMock()
|
||||
req.template_id = template_id
|
||||
req.source_edit_plan_id = source_edit_plan_id
|
||||
req.asset_ids = []
|
||||
req.asset_select_mode = "manual"
|
||||
req.asset_select_count = 0
|
||||
req.voice_library_id = ""
|
||||
req.title_ids = []
|
||||
req.voice_ids = []
|
||||
req.strategy_id = "one_take"
|
||||
req.count = 1
|
||||
req.video_title = ""
|
||||
req.resolution = ""
|
||||
req.bgm_config = None
|
||||
req.auto_retry_enabled = False
|
||||
req.auto_retry_max = 0
|
||||
req.is_preview = False
|
||||
req.source_task_id = ""
|
||||
req.output_width = 0
|
||||
req.output_height = 0
|
||||
req.cover_url = ""
|
||||
req.custom_title = ""
|
||||
req.title_config = {}
|
||||
req.project_id = None
|
||||
req.asset_library_id = None
|
||||
return req
|
||||
|
||||
@patch("apps.api.app.api.routes.generation_tasks._to_generation_task_response")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.safe_enqueue_generation_task")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.CreateGenerationTaskUseCase")
|
||||
@patch("apps.api.app.api.routes.generation_tasks._resolve_project_and_library")
|
||||
def test_fallback_sets_plan_id_when_empty(self, mock_resolve, mock_uc_cls, mock_enqueue, mock_resp_fn):
|
||||
"""When source_edit_plan_id is empty, fallback finds plan and sets it."""
|
||||
from apps.api.app.api.routes.generation_tasks import create_generation_task
|
||||
|
||||
mock_resolve.return_value = ("proj-1", None)
|
||||
mock_task = self._make_mock_task(source_edit_plan_id="")
|
||||
mock_uc_cls.return_value.execute.return_value = mock_task
|
||||
mock_enqueue.return_value = True
|
||||
from app.schemas.generation_task import GenerationTaskResponse
|
||||
mock_resp_fn.return_value = GenerationTaskResponse(
|
||||
id="task-1", project_id="proj-1", asset_library_id="",
|
||||
strategy_id="one_take", voice_library_id="", template_id="tpl-1",
|
||||
asset_ids=[], title_ids=[], voice_ids=[], source_edit_plan_id="",
|
||||
asset_select_mode="", batch_id="", video_title="", resolution="",
|
||||
bgm_config={}, is_preview=False, source_task_id="",
|
||||
output_width=1280, output_height=720, cover_url="", custom_title="",
|
||||
title_config={}, logs="[]", status="pending", progress=0.0,
|
||||
error_message="", created_at="2026-01-01T00:00:00Z",
|
||||
updated_at="2026-01-01T00:00:00Z", started_at=None, completed_at=None,
|
||||
created_by_user_id="user-1", auto_retry_enabled=False,
|
||||
auto_retry_max=0, auto_retry_count=0, result_count=0,
|
||||
)
|
||||
|
||||
mock_plan = MagicMock()
|
||||
mock_plan.id = "plan-found-123"
|
||||
mock_plan.created_by_user_id = "user-1"
|
||||
|
||||
mock_plan_repo_cls = MagicMock()
|
||||
mock_plan_repo_instance = MagicMock()
|
||||
mock_plan_repo_instance.list_by_template.return_value = [mock_plan]
|
||||
mock_plan_repo_cls.return_value = mock_plan_repo_instance
|
||||
|
||||
mock_gen_repo = MagicMock()
|
||||
mock_gen_repo.count_pending_by_user.return_value = 0
|
||||
mock_gen_repo.count_pending_total.return_value = 0
|
||||
|
||||
with patch(
|
||||
"packages.adapters.sqlalchemy_impl.edit_plan_repository.SQLAlchemyEditPlanRepository",
|
||||
mock_plan_repo_cls,
|
||||
):
|
||||
create_generation_task(
|
||||
request=self._make_request(source_edit_plan_id="", template_id="tpl-1"),
|
||||
authenticated_user=MagicMock(user=MagicMock(id="user-1")),
|
||||
generation_task_repository=mock_gen_repo,
|
||||
project_repository=MagicMock(),
|
||||
asset_library_repository=MagicMock(),
|
||||
asset_repository=MagicMock(),
|
||||
db=MagicMock(),
|
||||
)
|
||||
|
||||
assert mock_task.source_edit_plan_id == "plan-found-123"
|
||||
mock_gen_repo.update.assert_called_once_with(mock_task)
|
||||
|
||||
@patch("apps.api.app.api.routes.generation_tasks._to_generation_task_response")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.safe_enqueue_generation_task")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.CreateGenerationTaskUseCase")
|
||||
@patch("apps.api.app.api.routes.generation_tasks._resolve_project_and_library")
|
||||
def test_no_fallback_when_plan_id_already_set(self, mock_resolve, mock_uc_cls, mock_enqueue, mock_resp_fn):
|
||||
"""When source_edit_plan_id is already set, fallback should NOT run."""
|
||||
from apps.api.app.api.routes.generation_tasks import create_generation_task
|
||||
|
||||
mock_resolve.return_value = ("proj-1", None)
|
||||
mock_task = self._make_mock_task(source_edit_plan_id="plan-already-set")
|
||||
mock_uc_cls.return_value.execute.return_value = mock_task
|
||||
mock_enqueue.return_value = True
|
||||
from app.schemas.generation_task import GenerationTaskResponse
|
||||
mock_resp_fn.return_value = GenerationTaskResponse(
|
||||
id="task-1", project_id="proj-1", asset_library_id="",
|
||||
strategy_id="one_take", voice_library_id="", template_id="tpl-1",
|
||||
asset_ids=[], title_ids=[], voice_ids=[], source_edit_plan_id="",
|
||||
asset_select_mode="", batch_id="", video_title="", resolution="",
|
||||
bgm_config={}, is_preview=False, source_task_id="",
|
||||
output_width=1280, output_height=720, cover_url="", custom_title="",
|
||||
title_config={}, logs="[]", status="pending", progress=0.0,
|
||||
error_message="", created_at="2026-01-01T00:00:00Z",
|
||||
updated_at="2026-01-01T00:00:00Z", started_at=None, completed_at=None,
|
||||
created_by_user_id="user-1", auto_retry_enabled=False,
|
||||
auto_retry_max=0, auto_retry_count=0, result_count=0,
|
||||
)
|
||||
|
||||
mock_gen_repo = MagicMock()
|
||||
mock_gen_repo.count_pending_by_user.return_value = 0
|
||||
mock_gen_repo.count_pending_total.return_value = 0
|
||||
|
||||
create_generation_task(
|
||||
request=self._make_request(source_edit_plan_id="plan-already-set", template_id="tpl-1"),
|
||||
authenticated_user=MagicMock(user=MagicMock(id="user-1")),
|
||||
generation_task_repository=mock_gen_repo,
|
||||
project_repository=MagicMock(),
|
||||
asset_library_repository=MagicMock(),
|
||||
asset_repository=MagicMock(),
|
||||
db=MagicMock(),
|
||||
)
|
||||
|
||||
assert mock_task.source_edit_plan_id == "plan-already-set"
|
||||
mock_gen_repo.update.assert_not_called()
|
||||
|
||||
@patch("apps.api.app.api.routes.generation_tasks._to_generation_task_response")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.safe_enqueue_generation_task")
|
||||
@patch("apps.api.app.api.routes.generation_tasks.CreateGenerationTaskUseCase")
|
||||
@patch("apps.api.app.api.routes.generation_tasks._resolve_project_and_library")
|
||||
def test_fallback_handles_exception_gracefully(self, mock_resolve, mock_uc_cls, mock_enqueue, mock_resp_fn):
|
||||
"""When plan lookup fails, the fallback should not break the main flow."""
|
||||
from apps.api.app.api.routes.generation_tasks import create_generation_task
|
||||
|
||||
mock_resolve.return_value = ("proj-1", None)
|
||||
mock_task = self._make_mock_task(source_edit_plan_id="")
|
||||
mock_uc_cls.return_value.execute.return_value = mock_task
|
||||
mock_enqueue.return_value = True
|
||||
from app.schemas.generation_task import GenerationTaskResponse
|
||||
mock_resp_fn.return_value = GenerationTaskResponse(
|
||||
id="task-1", project_id="proj-1", asset_library_id="",
|
||||
strategy_id="one_take", voice_library_id="", template_id="tpl-1",
|
||||
asset_ids=[], title_ids=[], voice_ids=[], source_edit_plan_id="",
|
||||
asset_select_mode="", batch_id="", video_title="", resolution="",
|
||||
bgm_config={}, is_preview=False, source_task_id="",
|
||||
output_width=1280, output_height=720, cover_url="", custom_title="",
|
||||
title_config={}, logs="[]", status="pending", progress=0.0,
|
||||
error_message="", created_at="2026-01-01T00:00:00Z",
|
||||
updated_at="2026-01-01T00:00:00Z", started_at=None, completed_at=None,
|
||||
created_by_user_id="user-1", auto_retry_enabled=False,
|
||||
auto_retry_max=0, auto_retry_count=0, result_count=0,
|
||||
)
|
||||
|
||||
mock_plan_repo_cls = MagicMock()
|
||||
mock_plan_repo_cls.side_effect = Exception("DB connection error")
|
||||
|
||||
mock_gen_repo = MagicMock()
|
||||
mock_gen_repo.count_pending_by_user.return_value = 0
|
||||
mock_gen_repo.count_pending_total.return_value = 0
|
||||
|
||||
with patch(
|
||||
"packages.adapters.sqlalchemy_impl.edit_plan_repository.SQLAlchemyEditPlanRepository",
|
||||
mock_plan_repo_cls,
|
||||
):
|
||||
create_generation_task(
|
||||
request=self._make_request(source_edit_plan_id="", template_id="tpl-1"),
|
||||
authenticated_user=MagicMock(user=MagicMock(id="user-1")),
|
||||
generation_task_repository=mock_gen_repo,
|
||||
project_repository=MagicMock(),
|
||||
asset_library_repository=MagicMock(),
|
||||
asset_repository=MagicMock(),
|
||||
db=MagicMock(),
|
||||
)
|
||||
|
||||
# Task should still be created (fallback error doesn't break main flow)
|
||||
assert mock_task.source_edit_plan_id == ""
|
||||
mock_gen_repo.update.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user