import os import sys import tempfile import unittest from pathlib import Path from types import SimpleNamespace from unittest.mock import patch sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from app.config import config from app.services import material class TestMaterialTlsVerification(unittest.TestCase): def setUp(self): self.original_app_config = dict(config.app) self.original_proxy_config = dict(config.proxy) def tearDown(self): config.app.clear() config.app.update(self.original_app_config) config.proxy.clear() config.proxy.update(self.original_proxy_config) def test_search_pexels_uses_tls_verification_by_default(self): """ 默认路径必须开启 TLS 校验,避免素材 API key 和返回的素材 URL 在公共网络或不可信代理环境中被中间人攻击截获或篡改。 """ config.app["pexels_api_keys"] = ["pexels-key"] config.app.pop("tls_verify", None) config.proxy.clear() fake_response = SimpleNamespace( json=lambda: { "videos": [ { "duration": 8, "video_files": [ { "width": 1080, "height": 1920, "link": "https://example.com/video.mp4", } ], } ] } ) with patch("app.services.material.requests.get", return_value=fake_response) as get: results = material.search_videos_pexels("cat", minimum_duration=1) self.assertEqual(len(results), 1) self.assertTrue(get.call_args.kwargs["verify"]) def test_search_pixabay_allows_explicit_tls_disable_for_proxy(self): """ 少数企业代理会使用自签证书。该场景必须显式配置关闭 TLS 校验, 不能再由代码硬编码默认关闭。 """ config.app["pixabay_api_keys"] = ["pixabay-key"] config.app["tls_verify"] = False config.proxy.clear() fake_response = SimpleNamespace( json=lambda: { "hits": [ { "duration": 8, "videos": { "large": { "width": 1920, "url": "https://example.com/video.mp4", } }, } ] } ) with patch("app.services.material.requests.get", return_value=fake_response) as get: results = material.search_videos_pixabay("cat", minimum_duration=1) self.assertEqual(len(results), 1) self.assertFalse(get.call_args.kwargs["verify"]) def test_save_video_uses_tls_verification_by_default(self): config.app.pop("tls_verify", None) config.proxy.clear() fake_response = SimpleNamespace(content=b"fake-video") class FakeVideoFileClip: duration = 1 fps = 24 def __init__(self, path): self.path = path def close(self): return None with tempfile.TemporaryDirectory() as temp_dir: with patch( "app.services.material.requests.get", return_value=fake_response ) as get, patch("app.services.material.VideoFileClip", FakeVideoFileClip): video_path = material.save_video( "https://example.com/video.mp4?token=abc", save_dir=temp_dir ) self.assertTrue(os.path.exists(video_path)) self.assertTrue(get.call_args.kwargs["verify"]) def test_download_videos_accepts_plain_string_concat_mode(self): """ download_videos 可能被服务层或测试直接传入字符串模式,而不是 VideoConcatMode 枚举。这里用空搜索词避免真实网络请求,只验证 字符串 "random" 不会再因为访问 `.value` 抛 AttributeError。 """ result = material.download_videos( task_id="string-concat-mode", search_terms=[], video_contact_mode="random", ) self.assertEqual(result, []) if __name__ == "__main__": unittest.main()