import os import unittest from unittest.mock import Mock, patch from api.services.memoir_images.storage import ( TencentCosStorageService, normalize_cos_url, resolve_image_storage_key, ) class MemoirImageStorageTest(unittest.TestCase): @patch.dict( os.environ, { "TENCENT_COS_SECRET_ID": "id", "TENCENT_COS_SECRET_KEY": "key", "TENCENT_COS_REGION": "ap-shanghai", "TENCENT_COS_BUCKET": "memoir-1250000000", "TENCENT_COS_BASE_URL": "https://memoir-1250000000.cos.ap-shanghai.myqcloud.com", }, clear=False, ) @patch("api.services.memoir_images.storage.CosS3Client") def test_from_env_reuses_singleton_for_same_config(self, client_cls): TencentCosStorageService._instance = None TencentCosStorageService._instance_config = None client_cls.return_value = Mock() first = TencentCosStorageService.from_env() second = TencentCosStorageService.from_env() self.assertIs(first, second) client_cls.assert_called_once() @patch("api.services.memoir_images.storage.CosS3Client") def test_upload_bytes_returns_persistent_cos_url(self, client_cls): client = Mock() client_cls.return_value = client storage = TencentCosStorageService( secret_id="id", secret_key="key", region="ap-shanghai", bucket="memoir-1250000000", base_url="https://memoir-1250000000.cos.ap-shanghai.myqcloud.com", ) url = storage.upload_bytes( image_bytes=b"png-bytes", key="memoirs/u1/c1/0-demo.png", content_type="image/png", ) self.assertEqual( url, "https://memoir-1250000000.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", ) client.put_object.assert_called_once() @patch("api.services.memoir_images.storage.CosS3Client") def test_upload_bytes_normalizes_duplicate_appid_suffix_in_base_url(self, client_cls): client = Mock() client_cls.return_value = client storage = TencentCosStorageService( secret_id="id", secret_key="key", region="ap-shanghai", bucket="life-echo-dev-1319381411", base_url="https://life-echo-dev-1319381411-appid.cos.ap-shanghai.myqcloud.com", ) url = storage.upload_bytes( image_bytes=b"png-bytes", key="memoirs/u1/c1/0-demo.png", content_type="image/png", ) self.assertEqual( url, "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", ) client.put_object.assert_called_once() def test_normalize_cos_url_repairs_existing_duplicate_appid_host(self): normalized = normalize_cos_url( "https://life-echo-dev-1319381411-appid.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", bucket="life-echo-dev-1319381411", region="ap-shanghai", ) self.assertEqual( normalized, "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", ) @patch("api.services.memoir_images.storage.CosS3Client") def test_get_download_url_returns_presigned_download_url(self, client_cls): client = Mock() client.get_presigned_download_url.return_value = "https://cos.example.com/0.png?q-sign-algorithm=sha1" client_cls.return_value = client storage = TencentCosStorageService( secret_id="id", secret_key="key", region="ap-shanghai", bucket="life-echo-dev-1319381411", base_url="https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com", ) url = storage.get_download_url("memoirs/u1/c1/0-demo.png", expires=1800) self.assertEqual(url, "https://cos.example.com/0.png?q-sign-algorithm=sha1") client.get_presigned_download_url.assert_called_once_with( Bucket="life-echo-dev-1319381411", Key="memoirs/u1/c1/0-demo.png", Expired=1800, ) def test_resolve_image_storage_key_prefers_explicit_storage_key(self): key = resolve_image_storage_key( { "storage_key": "memoirs/u1/c1/0-demo.png", "url": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/other.png", } ) self.assertEqual(key, "memoirs/u1/c1/0-demo.png") def test_resolve_image_storage_key_derives_key_from_existing_url(self): key = resolve_image_storage_key( { "url": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png?q-sign-algorithm=sha1" } ) self.assertEqual(key, "memoirs/u1/c1/0-demo.png")