Files
xiaoxia-saas/tests/unit/test_generation_tasks.py
T
xiaoxia a78e8c3480
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
test: P3-1 第十九波 generation_tasks应用层用例单元测试 17个 (#725)
2026-07-23 00:25:23 +08:00

321 lines
10 KiB
Python
Executable File

"""
生成任务应用层用例单元测试(第十九波)
覆盖:
- CreateGenerationTaskUseCase
- GetGenerationTaskUseCase
- ListUserTasksFilteredUseCase
- RetryGenerationTaskUseCase
- Command / Filter / Result 对象
"""
from unittest.mock import MagicMock
import pytest
from packages.application.generation_tasks import (
CreateGenerationTaskCommand,
CreateGenerationTaskUseCase,
GetGenerationTaskUseCase,
ListGenerationTasksResult,
ListTasksFilter,
ListUserTasksFilteredUseCase,
RetryGenerationTaskUseCase,
)
from packages.domain.generation_task import GenerationTask, GenerationTaskStatus
@pytest.fixture
def mock_repo():
return MagicMock()
def make_task(status=GenerationTaskStatus.PENDING, **kwargs):
task = GenerationTask(
id="task-1",
project_id="proj-1",
asset_library_id="lib-1",
strategy_id="strat-1",
template_id="tmpl-1",
asset_ids=["asset-1"],
title_ids=["title-1"],
voice_ids=["voice-1"],
created_by_user_id="user-1",
video_title="测试标题",
)
if status != GenerationTaskStatus.PENDING:
object.__setattr__(task, "status", status)
# 应用额外 kwargs
for k, v in kwargs.items():
object.__setattr__(task, k, v)
return task
# ============================================================
# CreateGenerationTaskUseCase
# ============================================================
class TestCreateGenerationTaskUseCase:
"""CreateGenerationTaskUseCase 创建生成任务"""
def test_create_success(self, mock_repo):
"""正常创建任务"""
mock_repo.create.side_effect = lambda t: t
cmd = CreateGenerationTaskCommand(
project_id="proj-1",
asset_library_id="lib-1",
strategy_id="strat-1",
voice_library_id="vlib-1",
template_id="tmpl-1",
asset_ids=["a1", "a2"],
title_ids=["t1"],
voice_ids=["v1"],
created_by_user_id="user-1",
source_edit_plan_id="plan-1",
asset_select_mode="auto",
batch_id="batch-1",
video_title="我的视频",
auto_retry_enabled=True,
auto_retry_max=3,
)
uc = CreateGenerationTaskUseCase(mock_repo)
task = uc.execute(cmd)
assert task.project_id == "proj-1"
assert task.asset_library_id == "lib-1"
assert task.strategy_id == "strat-1"
assert task.voice_library_id == "vlib-1"
assert task.template_id == "tmpl-1"
assert task.asset_ids == ["a1", "a2"]
assert task.title_ids == ["t1"]
assert task.voice_ids == ["v1"]
assert task.created_by_user_id == "user-1"
assert task.source_edit_plan_id == "plan-1"
assert task.asset_select_mode == "auto"
assert task.batch_id == "batch-1"
assert task.video_title == "我的视频"
assert task.auto_retry_enabled is True
assert task.auto_retry_max == 3
assert task.status == GenerationTaskStatus.PENDING
assert task.progress == 0.0
assert task.result_count == 0
mock_repo.create.assert_called_once()
def test_create_default_values(self, mock_repo):
"""默认参数值"""
mock_repo.create.side_effect = lambda t: t
cmd = CreateGenerationTaskCommand(
project_id="proj-1",
asset_library_id="lib-1",
)
uc = CreateGenerationTaskUseCase(mock_repo)
task = uc.execute(cmd)
assert task.asset_ids == []
assert task.title_ids == []
assert task.voice_ids == []
assert task.created_by_user_id == ""
assert task.video_title == ""
assert task.auto_retry_enabled is False
assert task.auto_retry_max == 0
def test_create_id_is_generated(self, mock_repo):
"""ID 会自动生成"""
mock_repo.create.side_effect = lambda t: t
cmd = CreateGenerationTaskCommand(project_id="proj-1", asset_library_id="lib-1")
uc = CreateGenerationTaskUseCase(mock_repo)
task = uc.execute(cmd)
assert task.id
assert isinstance(task.id, str)
assert len(task.id) > 10 # uuid hex
# ============================================================
# GetGenerationTaskUseCase
# ============================================================
class TestGetGenerationTaskUseCase:
"""GetGenerationTaskUseCase 获取任务"""
def test_get_existing(self, mock_repo):
"""获取存在的任务"""
task = make_task()
mock_repo.get.return_value = task
uc = GetGenerationTaskUseCase(mock_repo)
result = uc.execute("task-1")
assert result is task
mock_repo.get.assert_called_once_with("task-1")
def test_get_not_found(self, mock_repo):
"""获取不存在的任务返回 None"""
mock_repo.get.return_value = None
uc = GetGenerationTaskUseCase(mock_repo)
result = uc.execute("nonexistent")
assert result is None
# ============================================================
# ListUserTasksFilteredUseCase
# ============================================================
class TestListUserTasksFilteredUseCase:
"""ListUserTasksFilteredUseCase 按用户筛选任务"""
def test_list_without_filters(self, mock_repo):
"""无筛选条件查询"""
tasks = [make_task(), make_task()]
mock_repo.list_by_user_filtered.return_value = tasks
mock_repo.count_by_user_filtered.return_value = 2
uc = ListUserTasksFilteredUseCase(mock_repo)
result = uc.execute("user-1")
assert isinstance(result, ListGenerationTasksResult)
assert len(result.items) == 2
assert result.total == 2
mock_repo.list_by_user_filtered.assert_called_once_with("user-1", status=None, limit=None, offset=0)
mock_repo.count_by_user_filtered.assert_called_once_with("user-1", status=None)
def test_list_with_status_filter(self, mock_repo):
"""按状态筛选"""
mock_repo.list_by_user_filtered.return_value = []
mock_repo.count_by_user_filtered.return_value = 0
uc = ListUserTasksFilteredUseCase(mock_repo)
uc.execute("user-1", status="running")
mock_repo.list_by_user_filtered.assert_called_once_with("user-1", status="running", limit=None, offset=0)
mock_repo.count_by_user_filtered.assert_called_once_with("user-1", status="running")
def test_list_with_pagination(self, mock_repo):
"""分页查询"""
mock_repo.list_by_user_filtered.return_value = []
mock_repo.count_by_user_filtered.return_value = 100
uc = ListUserTasksFilteredUseCase(mock_repo)
result = uc.execute("user-1", limit=10, offset=20)
assert result.total == 100
mock_repo.list_by_user_filtered.assert_called_once_with("user-1", status=None, limit=10, offset=20)
def test_list_empty_result(self, mock_repo):
"""空结果"""
mock_repo.list_by_user_filtered.return_value = []
mock_repo.count_by_user_filtered.return_value = 0
uc = ListUserTasksFilteredUseCase(mock_repo)
result = uc.execute("user-1", status="failed")
assert result.items == []
assert result.total == 0
# ============================================================
# RetryGenerationTaskUseCase
# ============================================================
class TestRetryGenerationTaskUseCase:
"""RetryGenerationTaskUseCase 重试失败任务"""
def test_retry_success(self, mock_repo):
"""失败任务重试成功"""
task = make_task(
status=GenerationTaskStatus.FAILED,
error_message="网络超时",
retry_count=0,
)
mock_repo.get.return_value = task
mock_repo.update.side_effect = lambda t: t
uc = RetryGenerationTaskUseCase(mock_repo)
result = uc.execute("task-1")
assert result.status == GenerationTaskStatus.PENDING
assert result.retry_count == 1
assert result.error_message == ""
assert result.error_info == {}
assert result.progress == 0.0
assert result.result_count == 0
assert result.started_at is None
assert result.completed_at is None
mock_repo.update.assert_called_once()
def test_retry_not_found(self, mock_repo):
"""任务不存在"""
mock_repo.get.return_value = None
uc = RetryGenerationTaskUseCase(mock_repo)
with pytest.raises(ValueError, match="任务不存在"):
uc.execute("nonexistent")
def test_retry_not_failed(self, mock_repo):
"""非失败状态不能重试"""
task = make_task(status=GenerationTaskStatus.RUNNING)
mock_repo.get.return_value = task
uc = RetryGenerationTaskUseCase(mock_repo)
with pytest.raises(ValueError, match="只有失败状态"):
uc.execute("task-1")
def test_retry_pending_not_allowed(self, mock_repo):
"""pending 状态不能重试"""
task = make_task(status=GenerationTaskStatus.PENDING)
mock_repo.get.return_value = task
uc = RetryGenerationTaskUseCase(mock_repo)
with pytest.raises(ValueError, match="只有失败状态"):
uc.execute("task-1")
def test_retry_preserves_id(self, mock_repo):
"""重试复用同一个 task_id"""
task = make_task(status=GenerationTaskStatus.FAILED)
original_id = task.id
mock_repo.get.return_value = task
mock_repo.update.side_effect = lambda t: t
uc = RetryGenerationTaskUseCase(mock_repo)
result = uc.execute("task-1")
assert result.id == original_id
# ============================================================
# Command / Filter / Result 对象
# ============================================================
class TestCommandAndDataObjects:
"""命令对象和数据对象"""
def test_create_command_defaults(self):
cmd = CreateGenerationTaskCommand()
assert cmd.project_id == ""
assert cmd.asset_library_id == ""
assert cmd.asset_ids == []
assert cmd.title_ids == []
assert cmd.voice_ids == []
assert cmd.auto_retry_enabled is False
assert cmd.auto_retry_max == 0
def test_list_filter_defaults(self):
f = ListTasksFilter()
assert f.status is None
def test_list_result(self):
task = make_task()
r = ListGenerationTasksResult(items=[task], total=1)
assert len(r.items) == 1
assert r.total == 1