diff --git a/backend/xt-server/src/test/java/cn/tianbaotong/fill/module/file/service/FileProcessServiceTest.java b/backend/xt-server/src/test/java/cn/tianbaotong/fill/module/file/service/FileProcessServiceTest.java new file mode 100644 index 0000000..7c0f61e --- /dev/null +++ b/backend/xt-server/src/test/java/cn/tianbaotong/fill/module/file/service/FileProcessServiceTest.java @@ -0,0 +1,88 @@ +package cn.tianbaotong.fill.module.file.service; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * 规范文件名引擎的最小单测(《08》F-9 / F-46)。 + * + *

断言的是有理由的规则,不是覆盖率: + *

+ */ +class FileProcessServiceTest { + + private final FileProcessService svc = new FileProcessService(); + + private static Map vars(String name, String id, String material) { + Map m = new HashMap<>(); + m.put("姓名", name); + m.put("证件号", id); + m.put("材料名", material); + return m; + } + + @Test + void 默认模板_姓名证件号材料名_扩展名带点() { + String n = svc.renderName(null, vars("张三", "361029200907295110", "身份证"), ".jpg"); + assertEquals("张三-361029200907295110-身份证.jpg", n); + } + + @Test + void 模板带后缀占位符时不重复拼扩展名() { + String n = svc.renderName("{材料名}-{姓名}{后缀}", + vars("张三", "361029200907295110", "身份证"), ".jpg"); + assertEquals("身份证-张三.jpg", n); + } + + @Test + void 空变量用未填占位_不会出现双横线() { + String n = svc.renderName(null, vars("张三", "", "身份证"), ".jpg"); + assertEquals("张三-未填-身份证.jpg", n); + assertFalse(n.contains("--"), "留空会出现「张--身份证.jpg」,看起来像系统出错: " + n); + } + + @Test + void 文件名里的斜杠必须被清掉() { + // 「学籍表(1/2)」在真实学生的上传里很常见;带 / 的名字写不进磁盘 + String n = svc.renderName(null, vars("张三", "361029200907295110", "学籍表(1/2)"), ".jpg"); + assertFalse(n.contains("/"), "斜杠会导致写盘失败: " + n); + assertTrue(n.contains("学籍表(1_2)")); + } + + @Test + void 超长文件名截到安全长度() { + Map m = vars("张".repeat(120), "361029200907295110", "身份证"); + String n = svc.renderName(null, m, ".jpg"); + assertTrue(n.length() <= 90, "Windows 路径 260 限制,正文要给目录留空间: " + n.length()); + assertTrue(n.endsWith(".jpg"), "截断不能吃掉扩展名"); + } + + @Test + void sanitize_空白给未命名_全非法字符收拢成单个下划线() { + // "///" → "___" → 收拢成 "_"(不是「未命名」:未命名只留给纯空白, + // 全非法字符收拢后仍是可写盘的合法名字) + assertEquals("_", FileProcessService.sanitize("///")); + assertEquals("未命名", FileProcessService.sanitize(" ")); + assertEquals(null, FileProcessService.sanitize(null)); + } + + @Test + void sanitize_连续分隔符收拢() { + assertEquals("a_b", FileProcessService.sanitize("a___b")); + assertEquals("a-b", FileProcessService.sanitize("a---b")); + } +} diff --git a/backend/xt-server/src/test/java/cn/tianbaotong/fill/support/IdentityNormalizerTest.java b/backend/xt-server/src/test/java/cn/tianbaotong/fill/support/IdentityNormalizerTest.java new file mode 100644 index 0000000..2fbc550 --- /dev/null +++ b/backend/xt-server/src/test/java/cn/tianbaotong/fill/support/IdentityNormalizerTest.java @@ -0,0 +1,51 @@ +package cn.tianbaotong.fill.support; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link IdentityNormalizer} 的最小单测(《08》F-9:先给纯函数补单测,其余继续靠 E2E)。 + * + *

选这三条规则断言,是因为每一条背后都有一个真实踩过的坑: + *

    + *
  • 姓名去空白、但不去间隔号 —— 少数民族姓名里的「·」是真实的; + * 顺手"清一清"会弄坏一部分人的名字(F-20 的另一面:规范化规则不能互相借用);
  • + *
  • 全角转半角 —— 手机输入法常给全角数字,而它进校验位算法就是错的;
  • + *
  • 证件号小写 {@code x} 收尾要统一成大写 —— 名册比对按字符串等值。
  • + *
+ */ +class IdentityNormalizerTest { + + @Test + void 姓名去所有空白_含全角空格() { + assertEquals("张三", IdentityNormalizer.name("张 三")); + assertEquals("张三", IdentityNormalizer.name("张 三")); // 全角空格 U+3000 + assertEquals("张三", IdentityNormalizer.name(" 张\t三\n")); + } + + @Test + void 姓名里的间隔号必须保留() { + // 阿依古丽·买买提:间隔号是姓名的一部分,清掉等于改了人家的名字 + assertEquals("阿依古丽·买买提", IdentityNormalizer.name("阿依古丽·买买提")); + assertEquals("阿依古丽·买买提", IdentityNormalizer.name("阿依古丽 · 买买提")); + } + + @Test + void 全角ASCII转半角_其余不动() { + assertEquals("123X", IdentityNormalizer.toHalfWidth("123X")); + assertEquals("中文不变", IdentityNormalizer.toHalfWidth("中文不变")); + assertEquals("", IdentityNormalizer.toHalfWidth(null)); + } + + @Test + void 证件号去空白_转大写_全角可入() { + assertEquals("36102920090729511X", IdentityNormalizer.idCard("3610292009 0729511x")); + assertEquals("36102920090729511X", IdentityNormalizer.idCard("36102920090729511X")); + } + + @Test + void 姓名空值返回空串而不是NPE() { + assertEquals("", IdentityNormalizer.name(null)); + } +}