41e3fc7a58
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 2m42s
CI/CD Pipeline / Unit Tests (push) Successful in 2m55s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 3m27s
CI/CD Pipeline / Integration Tests (push) Successful in 1m16s
CI Build & Deploy Pipeline / Build Staging API Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Staging Web Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Waiting to run
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI Build & Deploy Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI Build & Deploy Pipeline / Build Production API Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Production Web Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Production Worker Image (push) Waiting to run
CI Build & Deploy Pipeline / Deploy Production (push) Blocked by required conditions
CI Build & Deploy Pipeline / Production Browser E2E (push) Blocked by required conditions
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
209 lines
8.5 KiB
Python
Executable File
209 lines
8.5 KiB
Python
Executable File
"""P0-staging:OSS 上传崩溃修复测试.
|
||
|
||
测试:
|
||
1. oss_bucket() 传递 connect_timeout 参数
|
||
2. upload_to_oss() 小文件走 put_object_from_file,大文件走分片上传
|
||
3. upload_to_oss() 超时保护(超过总超时返回 None)
|
||
4. upload_to_oss() 异常时返回 None
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import tempfile
|
||
import time
|
||
from pathlib import Path
|
||
from unittest.mock import MagicMock, patch
|
||
|
||
# ── oss_bucket connect_timeout 测试 ───────────────────────────────────────────
|
||
|
||
|
||
class TestOSSBucketConnectTimeout:
|
||
"""测试 oss_bucket() 传递 connect_timeout 参数."""
|
||
|
||
def test_oss_bucket_has_connect_timeout(self):
|
||
"""oss_bucket 应传递 connect_timeout=10s 参数."""
|
||
from video_processing.oss_helpers import oss_bucket
|
||
|
||
mock_bucket_instance = MagicMock()
|
||
with (
|
||
patch(
|
||
"video_processing.oss_helpers.oss_settings",
|
||
return_value=("test-key", "test-secret", "oss-cn-hangzhou.aliyuncs.com", "test-bucket"),
|
||
),
|
||
patch("video_processing.oss_helpers.oss2.Auth"),
|
||
patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket_instance) as mock_bucket_cls,
|
||
):
|
||
bucket = oss_bucket()
|
||
|
||
assert bucket is mock_bucket_instance
|
||
# 验证 connect_timeout 关键字参数
|
||
call_kwargs = mock_bucket_cls.call_args[1]
|
||
assert "connect_timeout" in call_kwargs, "oss_bucket 应传递 connect_timeout 参数"
|
||
assert (
|
||
call_kwargs["connect_timeout"] == 10
|
||
), f"connect_timeout 应为 10,实际为 {call_kwargs['connect_timeout']}"
|
||
|
||
def test_oss_bucket_no_config_returns_none(self):
|
||
"""OSS 配置缺失时返回 None."""
|
||
from video_processing.oss_helpers import oss_bucket
|
||
|
||
with patch("video_processing.oss_helpers.oss_settings", return_value=None):
|
||
bucket = oss_bucket()
|
||
assert bucket is None
|
||
|
||
|
||
# ── upload_to_oss 分片上传测试 ────────────────────────────────────────────────
|
||
|
||
|
||
class TestUploadToOSSMultipart:
|
||
"""测试 upload_to_oss() 根据文件大小选择上传方式."""
|
||
|
||
def _create_temp_file(self, size_bytes: int) -> Path:
|
||
"""创建指定大小的临时文件."""
|
||
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
||
tmp.write(b"x" * size_bytes)
|
||
tmp.close()
|
||
return Path(tmp.name)
|
||
|
||
def test_small_file_uses_put_object(self):
|
||
"""小文件(<100MB)走 put_object_from_file."""
|
||
from video_processing.oss_helpers import upload_to_oss
|
||
|
||
small_file = self._create_temp_file(10 * 1024 * 1024) # 10MB
|
||
try:
|
||
mock_bucket = MagicMock()
|
||
|
||
with (
|
||
patch(
|
||
"video_processing.oss_helpers.oss_settings",
|
||
return_value=("test-key", "test-secret", "oss-cn-hangzhou.aliyuncs.com", "test-bucket"),
|
||
),
|
||
patch("video_processing.oss_helpers.oss2.Auth"),
|
||
patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket),
|
||
patch("video_processing.oss_helpers.oss2.resumable_upload") as mock_resumable,
|
||
):
|
||
url = upload_to_oss(small_file, "test/small.mp4")
|
||
|
||
# 验证调用了 put_object_from_file
|
||
mock_bucket.put_object_from_file.assert_called_once()
|
||
# 验证没调用分片上传
|
||
mock_resumable.assert_not_called()
|
||
# 验证返回 URL
|
||
assert url == "https://test-bucket.oss-cn-hangzhou.aliyuncs.com/test/small.mp4"
|
||
finally:
|
||
small_file.unlink()
|
||
|
||
def test_large_file_uses_resumable_upload(self):
|
||
"""大文件(>=100MB)走 resumable_upload 分片上传."""
|
||
from video_processing.oss_helpers import upload_to_oss
|
||
|
||
large_file = self._create_temp_file(100 * 1024 * 1024) # 100MB
|
||
try:
|
||
mock_bucket = MagicMock()
|
||
|
||
with (
|
||
patch(
|
||
"video_processing.oss_helpers.oss_settings",
|
||
return_value=("test-key", "test-secret", "oss-cn-hangzhou.aliyuncs.com", "test-bucket"),
|
||
),
|
||
patch("video_processing.oss_helpers.oss2.Auth"),
|
||
patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket),
|
||
patch("video_processing.oss_helpers.oss2.resumable_upload") as mock_resumable,
|
||
):
|
||
url = upload_to_oss(large_file, "test/large.mp4")
|
||
|
||
# 验证调用了分片上传
|
||
mock_resumable.assert_called_once()
|
||
# 验证没调用 put_object_from_file
|
||
mock_bucket.put_object_from_file.assert_not_called()
|
||
# 验证分片参数
|
||
call_kwargs = mock_resumable.call_args[1]
|
||
assert call_kwargs["multipart_threshold"] == 100 * 1024 * 1024
|
||
assert call_kwargs["part_size"] == 8 * 1024 * 1024
|
||
assert call_kwargs["num_threads"] == 3
|
||
# 验证返回 URL
|
||
assert url == "https://test-bucket.oss-cn-hangzhou.aliyuncs.com/test/large.mp4"
|
||
finally:
|
||
large_file.unlink()
|
||
|
||
|
||
# ── upload_to_oss 超时测试 ────────────────────────────────────────────────────
|
||
|
||
|
||
class TestUploadToOSSTimeout:
|
||
"""测试 upload_to_oss() 超时保护."""
|
||
|
||
def test_upload_timeout_returns_none(self):
|
||
"""上传超过总超时时返回 None."""
|
||
from video_processing.oss_helpers import upload_to_oss
|
||
|
||
small_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
||
small_file.write(b"x" * 1024) # 1KB
|
||
small_file.close()
|
||
file_path = Path(small_file.name)
|
||
|
||
def slow_upload(*args, **kwargs):
|
||
"""模拟慢速上传,超过超时时间."""
|
||
time.sleep(2)
|
||
|
||
mock_bucket = MagicMock()
|
||
mock_bucket.put_object_from_file.side_effect = slow_upload
|
||
|
||
try:
|
||
with (
|
||
patch(
|
||
"video_processing.oss_helpers.oss_settings",
|
||
return_value=("test-key", "test-secret", "oss-cn-hangzhou.aliyuncs.com", "test-bucket"),
|
||
),
|
||
patch("video_processing.oss_helpers.oss2.Auth"),
|
||
patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket),
|
||
patch("video_processing.oss_helpers.OSS_UPLOAD_TOTAL_TIMEOUT", 1), # 1秒超时
|
||
):
|
||
url = upload_to_oss(file_path, "test/slow.mp4")
|
||
# 超时应返回 None
|
||
assert url is None, "上传超时应返回 None"
|
||
finally:
|
||
file_path.unlink()
|
||
|
||
def test_upload_exception_returns_none(self):
|
||
"""上传异常时返回 None."""
|
||
from video_processing.oss_helpers import upload_to_oss
|
||
|
||
small_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
||
small_file.write(b"x" * 1024)
|
||
small_file.close()
|
||
file_path = Path(small_file.name)
|
||
|
||
mock_bucket = MagicMock()
|
||
mock_bucket.put_object_from_file.side_effect = RuntimeError("Network error")
|
||
|
||
try:
|
||
with (
|
||
patch(
|
||
"video_processing.oss_helpers.oss_settings",
|
||
return_value=("test-key", "test-secret", "oss-cn-hangzhou.aliyuncs.com", "test-bucket"),
|
||
),
|
||
patch("video_processing.oss_helpers.oss2.Auth"),
|
||
patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket),
|
||
):
|
||
url = upload_to_oss(file_path, "test/error.mp4")
|
||
assert url is None, "上传异常应返回 None"
|
||
finally:
|
||
file_path.unlink()
|
||
|
||
def test_upload_no_bucket_returns_none(self):
|
||
"""OSS 未配置时返回 None."""
|
||
from video_processing.oss_helpers import upload_to_oss
|
||
|
||
small_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
||
small_file.write(b"x" * 1024)
|
||
small_file.close()
|
||
file_path = Path(small_file.name)
|
||
|
||
try:
|
||
with patch("video_processing.oss_helpers.oss_settings", return_value=None):
|
||
url = upload_to_oss(file_path, "test/noconfig.mp4")
|
||
assert url is None
|
||
finally:
|
||
file_path.unlink()
|