style: auto-format with black + isort + prettier
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m42s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m44s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m42s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m26s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m13s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m22s
CI/CD Pipeline / Frontend Lint (push) Successful in 4m29s
CI/CD Pipeline / Integration Tests (push) Successful in 2m16s
CI/CD Pipeline / Unit Tests (push) Failing after 5m46s
CI/CD Pipeline / Build Staging API Image (push) Successful in 11m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 3m18s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 30s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m13s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m58s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1289h2m32s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1289h5m6s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1289h5m7s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1289h6m49s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1289h6m50s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1289h6m52s
CI/CD Pipeline / Deploy Production (push) Failing after 1289h35m22s
CI/CD Pipeline / Build Production API Image (push) Failing after 1289h37m37s
CI/CD Pipeline / PR Build API Image (push) Failing after 1289h39m20s

This commit is contained in:
CI Bot
2026-07-24 14:07:12 +00:00
parent 04bcd2e6f9
commit 18bd0de3fa
7 changed files with 171 additions and 152 deletions
+13 -34
View File
@@ -1,20 +1,19 @@
"""task_enqueue 单测 — 队列限流 + 安全入队逻辑."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from app.core.task_enqueue import (
USER_PENDING_LIMIT,
GLOBAL_PENDING_LIMIT,
UserPendingLimitExceeded,
USER_PENDING_LIMIT,
GlobalQueueFull,
UserPendingLimitExceeded,
check_queue_limits,
safe_enqueue_generation_task,
)
# ── Fixtures / Helpers ─────────────────────────────────────────────────────
@@ -57,18 +56,14 @@ class TestCheckQueueLimits:
def test_global_at_limit_raises(self):
"""达到全局上限即拒绝."""
repo = MockRepository(
global_count=GLOBAL_PENDING_LIMIT, user_count=1
)
repo = MockRepository(global_count=GLOBAL_PENDING_LIMIT, user_count=1)
with pytest.raises(GlobalQueueFull) as exc_info:
check_queue_limits("user-1", repo)
assert exc_info.value.pending_count == GLOBAL_PENDING_LIMIT
assert exc_info.value.limit == GLOBAL_PENDING_LIMIT
def test_global_over_limit_raises(self):
repo = MockRepository(
global_count=GLOBAL_PENDING_LIMIT + 1, user_count=1
)
repo = MockRepository(global_count=GLOBAL_PENDING_LIMIT + 1, user_count=1)
with pytest.raises(GlobalQueueFull):
check_queue_limits("user-1", repo)
@@ -124,14 +119,10 @@ class TestSafeEnqueueGenerationTask:
repo = MockRepository(global_count=1, user_count=1)
task = make_mock_task()
result = safe_enqueue_generation_task(
task, repo, user_id="user-1"
)
result = safe_enqueue_generation_task(task, repo, user_id="user-1")
assert result is True
mock_celery.send_task.assert_called_once_with(
"worker.generate_video", args=[task.id]
)
mock_celery.send_task.assert_called_once_with("worker.generate_video", args=[task.id])
task.mark_failed.assert_not_called()
@patch("app.core.task_enqueue.celery_app")
@@ -146,9 +137,7 @@ class TestSafeEnqueueGenerationTask:
@patch("app.core.task_enqueue.celery_app")
def test_precheck_global_over_marks_failed(self, mock_celery):
"""入队前全局超限:标记 failed,抛异常."""
repo = MockRepository(
global_count=GLOBAL_PENDING_LIMIT + 1, user_count=0
)
repo = MockRepository(global_count=GLOBAL_PENDING_LIMIT + 1, user_count=0)
task = make_mock_task()
with pytest.raises(GlobalQueueFull):
@@ -161,9 +150,7 @@ class TestSafeEnqueueGenerationTask:
@patch("app.core.task_enqueue.celery_app")
def test_precheck_user_over_marks_failed(self, mock_celery):
"""入队前用户超限:标记 failed,抛异常."""
repo = MockRepository(
global_count=5, user_count=USER_PENDING_LIMIT + 1
)
repo = MockRepository(global_count=5, user_count=USER_PENDING_LIMIT + 1)
task = make_mock_task()
with pytest.raises(UserPendingLimitExceeded):
@@ -211,9 +198,7 @@ class TestSafeEnqueueGenerationTask:
return GLOBAL_PENDING_LIMIT + 1 # 入队后再查,超限
repo = MockRepository(global_count=GLOBAL_PENDING_LIMIT, user_count=0)
repo.count_pending_total = MagicMock(
side_effect=count_pending_total_side_effect
)
repo.count_pending_total = MagicMock(side_effect=count_pending_total_side_effect)
task = make_mock_task()
with pytest.raises(GlobalQueueFull):
@@ -227,9 +212,7 @@ class TestSafeEnqueueGenerationTask:
@patch("app.core.task_enqueue.celery_app")
def test_postcheck_user_over_rollback(self, mock_celery):
"""入队后用户超限:回滚标记 failed,抛异常."""
repo = MockRepository(
global_count=5, user_count=USER_PENDING_LIMIT
)
repo = MockRepository(global_count=5, user_count=USER_PENDING_LIMIT)
# 入队前用 > 判断,等于上限通过;入队后模拟并发超限
original_user_count = repo.count_pending_by_user
@@ -241,9 +224,7 @@ class TestSafeEnqueueGenerationTask:
return USER_PENDING_LIMIT # 用 > 判断,等于时通过
return USER_PENDING_LIMIT + 1 # 入队后,超限
repo.count_pending_by_user = MagicMock(
side_effect=count_by_user_side_effect
)
repo.count_pending_by_user = MagicMock(side_effect=count_by_user_side_effect)
task = make_mock_task()
with pytest.raises(UserPendingLimitExceeded):
@@ -257,9 +238,7 @@ class TestSafeEnqueueGenerationTask:
repo = MockRepository(global_count=1, user_count=1)
task = make_mock_task()
result = safe_enqueue_generation_task(
task, repo, user_id="user-1", log_task_status=True
)
result = safe_enqueue_generation_task(task, repo, user_id="user-1", log_task_status=True)
assert result is True
@patch("app.core.task_enqueue.celery_app")