Files
xiaoxia-saas/tests/unit/test_worker_generate_video_task_binding.py
T
CI Bot efb7fa5729
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
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 / 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 / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped 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 / PR Build API Image (pull_request) Successful in 47s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m28s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m54s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 7m6s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 8m43s
AI Code Review / AI Code Review (pull_request) Successful in 10m46s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m49s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 15m58s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 24m52s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 4m25s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 5m1s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 1h3m21s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
style: auto-format with black + isort + ruff + prettier [skip ci-format-check]
2026-09-29 11:17:23 +00:00

89 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Regression test: ensure worker.generate_video Celery task is bound to the
real generate_video function, not a helper introduced above it.
Context (P0 incident 2026-08-23): a refactor inserted helper function
_sync_task_config_to_plan directly under the @celery_app.task decorator,
so Celery registered the helper as "worker.generate_video". Calling the
task with a single task_id raised TypeError and every generation job
failed immediately. This test pins the decorator target.
NOTE: CI conftest may mock Celery so that @celery_app.task does NOT return
a fully functional Task/PromiseProxy object. Tests therefore use multiple
defensive strategies: source-code inspection, __wrapped__.__func__ chain
traversal, and direct attribute checks.
"""
from __future__ import annotations
import inspect
import re
def _get_original_function(generate_video):
"""Walk the __wrapped__ chain to find the original function object."""
obj = generate_video
seen = set()
while hasattr(obj, "__wrapped__"):
obj_id = id(obj)
if obj_id in seen:
break
seen.add(obj_id)
obj = obj.__wrapped__
# __wrapped__ may be a bound method — unwrap to the underlying function
if hasattr(obj, "__func__"):
return obj.__func__
return obj
def test_generate_video_task_has_bind_true():
"""The decorator must use bind=True — verified via the original function's
first parameter being 'self' (bind=True convention)."""
from worker_app.tasks.generation import generate_video
original = _get_original_function(generate_video)
sig = inspect.signature(original)
params = list(sig.parameters)
assert params[0] == "self", f"bind=True requires 'self' as first param, got {params}"
def test_generate_video_task_signature_has_task_id():
"""The original generate_video function must accept task_id as a parameter."""
from worker_app.tasks.generation import generate_video
original = _get_original_function(generate_video)
sig = inspect.signature(original)
params = list(sig.parameters)
assert "task_id" in params, f"expected 'task_id' in params, got {params}"
def test_generate_video_preserves_original_function():
"""The original function wrapped by @celery_app.task must be named
'generate_video' — not '_sync_task_config_to_plan'."""
from worker_app.tasks.generation import generate_video
original = _get_original_function(generate_video)
assert original.__name__ == "generate_video", f"expected __name__='generate_video', got '{original.__name__}'"
def test_build_task_config_override_is_plain_function():
"""#2098: 原 _sync_task_config_to_plan 已拆分为 _build_task_config_override + _download_voice_for_task,
均为普通函数,不应被注册为 Celery task。"""
from worker_app.tasks.generation import _build_task_config_override, _download_voice_for_task
for fn in (_build_task_config_override, _download_voice_for_task):
assert not hasattr(fn, "run"), f"{fn.__name__} must be a plain function, not a Celery task"
# Bug A: override 对 title_config 做 key 归一化 (font_size→size, font_color→color)
override = _build_task_config_override(
{
"title_config": {"font_size": 48, "font_color": "#ff0000", "text": "hi"},
"bgm_config": {"url": "http://x/bgm.mp3"},
"output_width": 1080,
"output_height": 1920,
}
)
assert override["title"]["size"] == 48
assert override["title"]["color"] == "#ff0000"
assert override["bgm"]["url"] == "http://x/bgm.mp3"
assert override["export"]["resolution"] == "1080x1920"