Files
svc-hall-service/src/test/java/com/yunhai/svchall/utils/FileUtilTest.java
T
chenxing 27e19923fb
build / build-push (push) Successful in 51s
fix(file): 封面 URL /upload/ 段逐轮叠加致门户裂图——出入口折叠自愈
- 生产 public-base-url 以 /upload/ 结尾而库内相对值以 /upload/ 开头,
  buildUrl 盲拼每轮"绝对化→存库"多叠一层(部署态实测叠到 4 层 404)。
- FileUtil.squashDuplicateUploadSeg:/upload/upload/ 永远非法(upload 非
  法类型名),buildUrl/absolutize 出口与 toRelativeIfLocal 存库侧统一折叠——
  发版即恢复显示无需清库,库内损伤值下次保存永久收敛。
- adminGet 封面回显绝对化:修管理端编辑页相对路径 404 被误当"没保存上"。
- 新增 FileUtilTest 8 用例锁行为(不叠加/损伤自愈/透传/往返幂等)。
2026-09-21 08:52:02 +08:00

103 lines
4.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.svchall.utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 文件 URL 拼接契约单测(2026-09-20 封面裂图修复):
* 生产 public-base-url 以 /upload/ 结尾(https://base.yunhaixingtong.com/svc-hall/upload/),
* 而库内相对值以 /upload/ 开头——拼接必须不产生 /upload/upload/ 叠加,
* 且对历史已叠加损伤值(/upload/upload/…,实测库里叠了三层导致门户裂图)具备折叠自愈能力。
* 不变式:存储布局 /upload/{image|file}/uuid.ext,upload 不是合法类型名,/upload/upload/ 永远非法。
*/
@DisplayName("FileUtil - 文件 URL 拼接与 /upload/ 段折叠")
class FileUtilTest {
/** 生产形态:带 ctx 且以 /upload/ 结尾 */
private static final String PROD_BASE = "http://base.yunhaixingtong.com/svc-hall/upload/";
/** 本地形态:裸 host 以 /upload/ 结尾 */
private static final String LOCAL_BASE = "http://localhost:8086/upload/";
@AfterEach
void reset() {
FileUtil.init("target/test-fileutil-root", "http://localhost:8086/upload/");
}
@Test
@DisplayName("buildUrl:生产 base+/upload/ 结尾,干净相对值不叠加")
void buildUrlProdBaseNoDouble() {
FileUtil.init("target/test-fileutil-root", PROD_BASE);
assertThat(FileUtil.buildUrl("/upload/image/e7df.png"))
.isEqualTo("http://base.yunhaixingtong.com/svc-hall/upload/image/e7df.png");
}
@Test
@DisplayName("buildUrl:本地 base+/upload/ 结尾,干净相对值不叠加")
void buildUrlLocalBaseNoDouble() {
FileUtil.init("target/test-fileutil-root", LOCAL_BASE);
assertThat(FileUtil.buildUrl("/upload/file/abc.pdf"))
.isEqualTo("http://localhost:8086/upload/file/abc.pdf");
}
@Test
@DisplayName("buildUrl:base 不带 /upload/ 尾时行为不变")
void buildUrlBaseWithoutUploadTail() {
FileUtil.init("target/test-fileutil-root", "http://localhost:8086");
assertThat(FileUtil.buildUrl("/upload/image/a.png"))
.isEqualTo("http://localhost:8086/upload/image/a.png");
}
@Test
@DisplayName("buildUrl:历史损伤相对值(叠三层)出口折叠自愈——部署态门户裂图修复点")
void buildUrlSquashesCorruptedRelative() {
FileUtil.init("target/test-fileutil-root", PROD_BASE);
assertThat(FileUtil.buildUrl("/upload/upload/upload/image/e7df.png"))
.isEqualTo("http://base.yunhaixingtong.com/svc-hall/upload/image/e7df.png");
}
@Test
@DisplayName("absolutizePlain:绝对地址原样返回(含历史损伤值,不篡改外链)")
void absolutizePlainPassthrough() {
FileUtil.init("target/test-fileutil-root", PROD_BASE);
String external = "https://cdn.example.com/pic.png";
assertThat(FileUtil.absolutizePlain(external)).isEqualTo(external);
}
@Test
@DisplayName("absolutize:富文本内链绝对化不叠加 /upload/")
void absolutizeHtmlNoDouble() {
FileUtil.init("target/test-fileutil-root", PROD_BASE);
String html = "<p><img src=\"/upload/image/a.png\">§<a href=\"/upload/file/b.pdf\">附件</a></p>";
String out = FileUtil.absolutize(html);
assertThat(out).contains("src=\"http://base.yunhaixingtong.com/svc-hall/upload/image/a.png\"");
assertThat(out).contains("href=\"http://base.yunhaixingtong.com/svc-hall/upload/file/b.pdf\"");
assertThat(out).doesNotContain("/upload/upload/");
}
@Test
@DisplayName("squashDuplicateUploadSeg:任意层数叠加收敛为一层")
void squashCollapsesAnyDepth() {
assertThat(FileUtil.squashDuplicateUploadSeg("/upload/upload/upload/upload/image/x.png"))
.isEqualTo("/upload/image/x.png");
assertThat(FileUtil.squashDuplicateUploadSeg("/upload/image/ok.png"))
.isEqualTo("/upload/image/ok.png");
assertThat(FileUtil.squashDuplicateUploadSeg(null)).isNull();
}
@Test
@DisplayName("往返幂等:绝对化 → 存库相对化 → 再绝对化,值稳定不增长")
void roundTripIdempotent() {
FileUtil.init("target/test-fileutil-root", PROD_BASE);
String stored = "/upload/image/e7df.png";
String out1 = FileUtil.absolutizePlain(stored);
// 模拟保存侧:取第一个 /upload/ 起的相对段(ServiceItemService.toRelativeIfLocal 同款逻辑)
String back = out1.substring(out1.indexOf("/upload/"));
String out2 = FileUtil.absolutizePlain(back);
assertThat(out2).isEqualTo(out1);
assertThat(out2).doesNotContain("/upload/upload/");
}
}