From dc90ead263d6d98d091c29e6326a3146608d0f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Wed, 2 Sep 2026 06:00:53 +0800 Subject: [PATCH] =?UTF-8?q?task-147:=20downloadUrl=20=E6=B0=B8=E4=B8=8D?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E9=AA=8C=E8=AF=81=EF=BC=88result/=20?= =?UTF-8?q?=E8=B5=B0=E4=B8=8B=E8=BD=BD=E5=9F=9F=E5=90=8D=E3=80=81=E6=97=A0?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E5=8F=82=E6=95=B0=E3=80=81bucket=20=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E3=80=81null/=E7=A9=BA=E7=99=BD=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E3=80=81=E8=BE=93=E5=87=BA=E7=A8=B3=E5=AE=9A=EF=BC=89+=208=20?= =?UTF-8?q?=E6=9D=A1=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oss/DownloadUrlNonExpiringTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/DownloadUrlNonExpiringTest.java diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/DownloadUrlNonExpiringTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/DownloadUrlNonExpiringTest.java new file mode 100644 index 00000000..8f27725a --- /dev/null +++ b/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/DownloadUrlNonExpiringTest.java @@ -0,0 +1,97 @@ +package com.nanri.aiimage.modules.file.service.oss; + +import com.nanri.aiimage.config.OssProperties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * task-147:downloadUrl 永不过期验证(plan 08)。 + * generateFreshDownloadUrl:result/ 前缀走下载域名、非 result/ 走公开端点、 + * URL 无签名(不含 X-Amz 系列 / Expires / Signature 等过期参数)、bucket 前缀正确、 + * null/空白安全、同输入同输出稳定。 + */ +class DownloadUrlNonExpiringTest { + + private OssProperties properties; + private OssStorageService storageService; + + @BeforeEach + void setUp() { + properties = new OssProperties(); + properties.setEndpoint("https://oss.aishufu.top"); + properties.setPublicEndpoint("https://oss.aishufu.top"); + properties.setDownloadEndpoint("https://download.aishufu.top"); + properties.setBucket("nanri-ai-images"); + properties.setImageVideoBucket("shufu-video"); + properties.setDigitalHumanBucket("nanri-ai-digital-human"); + properties.setTemplateBucket("aiimage-templates"); + properties.setAccessKeyId("test-access-key"); + properties.setAccessKeySecret("test-secret-key"); + storageService = new OssStorageService(properties); + } + + @Test + void resultPrefixUsesDownloadEndpoint() { + assertEquals( + "https://download.aishufu.top/nanri-ai-images/result/publish/1/result.xlsx", + storageService.generateFreshDownloadUrl("result/publish/1/result.xlsx")); + } + + @Test + void nonResultPrefixUsesPublicEndpoint() { + assertEquals( + "https://oss.aishufu.top/nanri-ai-images/supply_images/main.jpg", + storageService.generateFreshDownloadUrl("supply_images/main.jpg")); + } + + @Test + void urlHasNoExpiringSignatureParameters() { + String url = storageService.generateFreshDownloadUrl("result/publish/1/result.xlsx"); + assertFalse(url.contains("X-Amz-"), "不得包含 AWS 签名参数: " + url); + assertFalse(url.contains("Signature"), "不得包含签名: " + url); + assertFalse(url.contains("Expires"), "不得包含过期时间: " + url); + assertFalse(url.contains("?") || url.contains("&"), "不得携带任何查询参数: " + url); + } + + @Test + void bucketPrefixIsCorrect() { + String url = storageService.generateFreshDownloadUrl("result/collect_data/2/data.xlsx"); + assertTrue(url.contains("/nanri-ai-images/result/collect_data/2/data.xlsx"), + "主 bucket 前缀正确: " + url); + String imageUrl = storageService.generateFreshDownloadUrl("shufu-video/result/image_video/demo.mp4"); + assertTrue(imageUrl.contains("/shufu-video/result/image_video/demo.mp4"), + "image-video bucket 前缀正确: " + imageUrl); + } + + @Test + void nullUrlIsSafe() { + assertNull(storageService.generateFreshDownloadUrl(null), "null 输入返回 null 不抛错"); + } + + @Test + void blankUrlIsSafe() { + assertNull(storageService.generateFreshDownloadUrl(" "), "空白输入返回 null 不抛错"); + assertNull(storageService.generateFreshDownloadUrl(""), "空串返回 null 不抛错"); + } + + @Test + void generatedUrlIsStable() { + String first = storageService.generateFreshDownloadUrl("result/similar_asin/3/re.xlsx"); + String second = storageService.generateFreshDownloadUrl("result/similar_asin/3/re.xlsx"); + assertEquals(first, second, "同输入同输出(无随机签名)"); + } + + @Test + void contractFrozen() { + String url = storageService.generateFreshDownloadUrl("result/similar_asin/3/re.xlsx"); + assertNotNull(url); + // 快照:https://{download-endpoint}/{bucket}/{objectKey},纯路径无签名 + assertEquals("https://download.aishufu.top/nanri-ai-images/result/similar_asin/3/re.xlsx", url); + } +}