"""P0-2 深度修复:Worker 端 OSS 工具函数测试. 测试: 1. oss_bucket() endpoint 自动补 https:// 前缀 2. get_signed_download_url() 生成预签名 URL 3. upload_to_oss() 返回 HTTPS URL """ from __future__ import annotations from pathlib import Path from unittest.mock import MagicMock, patch # ── oss_bucket endpoint scheme 修复 ────────────────────────────────────────── class TestOSSBucketEndpointScheme: """测试 oss_bucket() 自动为 endpoint 补 https:// 前缀.""" def test_endpoint_without_scheme_adds_https(self): """endpoint 不带 scheme 时,自动补 https://.""" 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 # 验证 endpoint 传的是带 https:// 的 call_args = mock_bucket_cls.call_args endpoint_arg = call_args[0][1] # 第 2 个位置参数是 endpoint assert endpoint_arg.startswith("https://"), f"endpoint 应该带 https:// 前缀,实际为: {endpoint_arg}" assert "oss-cn-hangzhou.aliyuncs.com" in endpoint_arg def test_endpoint_with_https_keeps_as_is(self): """endpoint 已有 https:// 时,不重复添加.""" 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", "https://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, ): oss_bucket() call_args = mock_bucket_cls.call_args endpoint_arg = call_args[0][1] # 不应该出现 https://https:// 这种双重前缀 assert endpoint_arg.count("https://") == 1 assert endpoint_arg == "https://oss-cn-hangzhou.aliyuncs.com" def test_endpoint_with_http_keeps_as_is(self): """endpoint 已有 http:// 时,不修改(保留用户选择).""" 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", "http://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, ): oss_bucket() call_args = mock_bucket_cls.call_args endpoint_arg = call_args[0][1] assert endpoint_arg == "http://oss-cn-hangzhou.aliyuncs.com" def test_missing_credentials_returns_none(self): """凭证缺失时返回 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 # ── get_signed_download_url ────────────────────────────────────────────────── class TestGetSignedDownloadUrl: """测试 get_signed_download_url() 预签名 URL 生成.""" def test_returns_signed_url_with_storage_key(self): """传入 storage key 时,调用 sign_url 并返回结果.""" from video_processing.oss_helpers import get_signed_download_url mock_bucket = MagicMock() mock_bucket.sign_url.return_value = ( "https://test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4" "?OSSAccessKeyId=xxx&Expires=xxx&Signature=xxx" ) 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), ): result = get_signed_download_url("generated/test.mp4", expires_seconds=3600) assert result is not None assert "Signature=" in result mock_bucket.sign_url.assert_called_once_with("GET", "generated/test.mp4", 3600) def test_normalizes_full_url_to_storage_key(self): """传入完整 URL 时,提取 storage key 再生成签名.""" from video_processing.oss_helpers import get_signed_download_url mock_bucket = MagicMock() mock_bucket.sign_url.return_value = ( "https://test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4?sign=xxx" ) 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), ): get_signed_download_url("https://test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4") mock_bucket.sign_url.assert_called_once() # 验证传给 sign_url 的是纯 storage key,不是完整 URL call_key = mock_bucket.sign_url.call_args[0][1] assert not call_key.startswith("http") assert call_key == "generated/test.mp4" def test_returns_none_when_bucket_none(self): """bucket 为 None 时返回 None(不抛异常).""" from video_processing.oss_helpers import get_signed_download_url with patch("video_processing.oss_helpers.oss_settings", return_value=None): result = get_signed_download_url("generated/test.mp4") assert result is None def test_sign_url_exception_returns_none(self): """sign_url 抛异常时,返回 None(不向上抛出).""" from video_processing.oss_helpers import get_signed_download_url mock_bucket = MagicMock() mock_bucket.sign_url.side_effect = Exception("sign failed") 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), ): result = get_signed_download_url("generated/test.mp4") assert result is None # ── upload_to_oss 返回 HTTPS URL ───────────────────────────────────────────── class TestUploadToOSSReturnsHTTPS: """测试 upload_to_oss() 返回的 URL 始终是 HTTPS.""" def test_endpoint_without_scheme_returns_https_url(self): """endpoint 不带 scheme 时,返回 HTTPS URL.""" from video_processing.oss_helpers import upload_to_oss mock_bucket = MagicMock() mock_bucket.put_object_from_file = 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), ): result = upload_to_oss(Path("/tmp/test.mp4"), "generated/test.mp4") assert result is not None assert result.startswith("https://") assert "test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4" in result def test_endpoint_with_https_returns_clean_url(self): """endpoint 带 https:// 时,URL 里不会有双重 https.""" from video_processing.oss_helpers import upload_to_oss mock_bucket = MagicMock() mock_bucket.put_object_from_file = MagicMock() with ( patch( "video_processing.oss_helpers.oss_settings", return_value=("test-key", "test-secret", "https://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), ): result = upload_to_oss(Path("/tmp/test.mp4"), "generated/test.mp4") assert result is not None assert result.startswith("https://") # 不应该出现 https://https:// assert result.count("https://") == 1