#!/usr/bin/env python3
"""
OSS 签名 URL 功能测试

测试 OSSStorageProvider 的签名 URL 生成功能
"""

import unittest
import tempfile
import os
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
import sys

# 添加 SDK 路径
sys.path.insert(0, str(Path(__file__).parent.parent))

from sandai.operator.storage import OSSStorageProvider, create_storage_provider
from sandai.operator.env import OSSConfig


class TestOSSSignedURL(unittest.TestCase):
    """OSS 签名 URL 测试类"""
    
    def setUp(self):
        """测试前准备"""
        # 创建模拟的 OSS 配置
        self.mock_config = OSSConfig(
            endpoint="oss-cn-beijing.aliyuncs.com",
            access_key_id="test_access_key",
            access_key_secret="test_secret_key",
            bucket_name="test-bucket",
            region="oss-cn-beijing",
            prefix="test-prefix"
        )
        
        # 创建临时测试文件
        self.temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
        self.temp_file.write(b"Test content for OSS upload")
        self.temp_file.close()
        self.temp_file_path = self.temp_file.name
    
    def tearDown(self):
        """测试后清理"""
        # 删除临时文件
        if os.path.exists(self.temp_file_path):
            os.unlink(self.temp_file_path)
    
    @patch('sandai.operator.storage.get_oss_config')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_oss_provider_initialization_with_custom_expires(self, mock_auth, mock_bucket, mock_get_config):
        """测试 OSS 提供者初始化时自定义过期时间"""
        mock_get_config.return_value = self.mock_config
        
        # 创建 OSS 提供者，指定自定义过期时间
        custom_expires = 7200  # 2 小时
        provider = OSSStorageProvider(signed_url_expires=custom_expires)
        
        self.assertEqual(provider.signed_url_expires, custom_expires)
        self.assertEqual(provider.storage_type, "oss")
    
    @patch('sandai.operator.storage.get_oss_config')
    @patch('sandai.operator.storage.get_oss_signed_url_expires')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_oss_provider_initialization_with_env_expires(self, mock_auth, mock_bucket, mock_get_expires, mock_get_config):
        """测试 OSS 提供者初始化时使用环境变量过期时间"""
        mock_get_config.return_value = self.mock_config
        mock_get_expires.return_value = 1800  # 30 分钟
        
        # 创建 OSS 提供者，不指定过期时间（使用环境变量）
        provider = OSSStorageProvider()
        
        self.assertEqual(provider.signed_url_expires, 1800)
        mock_get_expires.assert_called_once()
    
    @patch('sandai.operator.storage.get_oss_config')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_upload_file_returns_signed_url(self, mock_auth, mock_bucket, mock_get_config):
        """测试上传文件返回签名 URL"""
        mock_get_config.return_value = self.mock_config
        
        # 模拟 OSS bucket
        mock_bucket_instance = MagicMock()
        mock_bucket.return_value = mock_bucket_instance
        
        # 模拟签名 URL
        expected_signed_url = "https://test-bucket.oss-cn-beijing.aliyuncs.com/test-prefix/test-file.txt?Expires=1234567890&OSSAccessKeyId=test_access_key&Signature=test_signature"
        mock_bucket_instance.sign_url.return_value = expected_signed_url
        
        # 创建 OSS 提供者
        provider = OSSStorageProvider(signed_url_expires=3600)
        
        # 执行上传（异步）
        import asyncio
        result_url = asyncio.run(provider.upload_file(self.temp_file_path))
        
        # 验证结果
        self.assertEqual(result_url, expected_signed_url)
        
        # 验证调用了上传方法
        mock_bucket_instance.put_object_from_file.assert_called_once()
        
        # 验证调用了签名 URL 生成方法
        mock_bucket_instance.sign_url.assert_called_once()
        call_args = mock_bucket_instance.sign_url.call_args
        self.assertEqual(call_args[0][0], 'GET')  # HTTP 方法
        self.assertEqual(call_args[0][2], 3600)   # 过期时间
    
    @patch('sandai.operator.storage.get_oss_config')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_generate_signed_url_method(self, mock_auth, mock_bucket, mock_get_config):
        """测试生成签名 URL 方法"""
        mock_get_config.return_value = self.mock_config
        
        # 模拟 OSS bucket
        mock_bucket_instance = MagicMock()
        mock_bucket.return_value = mock_bucket_instance
        
        # 模拟签名 URL
        expected_signed_url = "https://test-bucket.oss-cn-beijing.aliyuncs.com/test-prefix/existing-file.txt?Expires=1234567890&OSSAccessKeyId=test_access_key&Signature=test_signature"
        mock_bucket_instance.sign_url.return_value = expected_signed_url
        
        # 创建 OSS 提供者
        provider = OSSStorageProvider(signed_url_expires=3600)
        
        # 测试生成签名 URL
        object_key = "test-prefix/existing-file.txt"
        result_url = provider.generate_signed_url(object_key)
        
        # 验证结果
        self.assertEqual(result_url, expected_signed_url)
        
        # 验证调用参数
        mock_bucket_instance.sign_url.assert_called_once_with('GET', object_key, 3600)
    
    @patch('sandai.operator.storage.get_oss_config')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_generate_signed_url_with_custom_expires(self, mock_auth, mock_bucket, mock_get_config):
        """测试生成签名 URL 时使用自定义过期时间"""
        mock_get_config.return_value = self.mock_config
        
        # 模拟 OSS bucket
        mock_bucket_instance = MagicMock()
        mock_bucket.return_value = mock_bucket_instance
        
        # 模拟签名 URL
        expected_signed_url = "https://test-bucket.oss-cn-beijing.aliyuncs.com/test-prefix/existing-file.txt?Expires=1234567890&OSSAccessKeyId=test_access_key&Signature=test_signature"
        mock_bucket_instance.sign_url.return_value = expected_signed_url
        
        # 创建 OSS 提供者
        provider = OSSStorageProvider(signed_url_expires=3600)
        
        # 测试生成签名 URL，使用自定义过期时间
        object_key = "test-prefix/existing-file.txt"
        custom_expires = 7200  # 2 小时
        result_url = provider.generate_signed_url(object_key, expires=custom_expires)
        
        # 验证结果
        self.assertEqual(result_url, expected_signed_url)
        
        # 验证调用参数（使用了自定义过期时间）
        mock_bucket_instance.sign_url.assert_called_once_with('GET', object_key, custom_expires)
    
    @patch('sandai.operator.storage.get_storage_type_from_env')
    @patch('sandai.operator.storage.get_oss_config')
    @patch('oss2.Bucket')
    @patch('oss2.Auth')
    def test_create_storage_provider_with_oss_expires(self, mock_auth, mock_bucket, mock_get_config, mock_get_storage_type):
        """测试通过 create_storage_provider 创建 OSS 提供者时传递过期时间"""
        mock_get_storage_type.return_value = "oss"
        mock_get_config.return_value = self.mock_config
        
        # 创建存储提供者，传递签名 URL 过期时间
        custom_expires = 1800  # 30 分钟
        provider = create_storage_provider(
            storage_type="oss",
            signed_url_expires=custom_expires
        )
        
        # 验证类型和过期时间
        self.assertIsInstance(provider, OSSStorageProvider)
        self.assertEqual(provider.signed_url_expires, custom_expires)
    
    def test_oss_config_validation(self):
        """测试 OSS 配置验证"""
        # 完整配置
        complete_config = OSSConfig(
            endpoint="oss-cn-beijing.aliyuncs.com",
            access_key_id="test_key",
            access_key_secret="test_secret",
            bucket_name="test-bucket",
            region="oss-cn-beijing"
        )
        self.assertTrue(complete_config.is_configured)
        
        # 不完整配置
        incomplete_config = OSSConfig(
            endpoint="",
            access_key_id="test_key",
            access_key_secret="",
            bucket_name="test-bucket",
            region="oss-cn-beijing"
        )
        self.assertFalse(incomplete_config.is_configured)


class TestOSSEnvironmentConfig(unittest.TestCase):
    """OSS 环境配置测试"""
    
    def test_get_oss_signed_url_expires_default(self):
        """测试获取 OSS 签名 URL 过期时间默认值"""
        from sandai.operator.env import get_oss_signed_url_expires
        
        # 清除环境变量
        if 'SANDAI_OPERATOR_OSS_SIGNED_URL_EXPIRES' in os.environ:
            del os.environ['SANDAI_OPERATOR_OSS_SIGNED_URL_EXPIRES']
        
        # 测试默认值
        expires = get_oss_signed_url_expires()
        self.assertEqual(expires, 3600)  # 默认 1 小时
    
    def test_get_oss_signed_url_expires_custom(self):
        """测试获取 OSS 签名 URL 过期时间自定义值"""
        from sandai.operator.env import get_oss_signed_url_expires
        
        # 设置环境变量
        os.environ['SANDAI_OPERATOR_OSS_SIGNED_URL_EXPIRES'] = '7200'
        
        try:
            # 测试自定义值
            expires = get_oss_signed_url_expires()
            self.assertEqual(expires, 7200)  # 2 小时
        finally:
            # 清理环境变量
            if 'SANDAI_OPERATOR_OSS_SIGNED_URL_EXPIRES' in os.environ:
                del os.environ['SANDAI_OPERATOR_OSS_SIGNED_URL_EXPIRES']


if __name__ == "__main__":
    # 运行测试
    unittest.main(verbosity=2)