mirror of
https://gitee.com/jzsw-it/yexuejc-base.git
synced 2025-09-28 00:13:20 +08:00
[update] 单元测试完善
Some checks failed
yexuejc-base package jre11 / package_job (push) Failing after 19s
Some checks failed
yexuejc-base package jre11 / package_job (push) Failing after 19s
This commit is contained in:
232
src/test/java/com/yexuejc/base/encrypt/AESEnhancedTest.java
Normal file
232
src/test/java/com/yexuejc/base/encrypt/AESEnhancedTest.java
Normal file
@@ -0,0 +1,232 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AES加密增强测试类 - 验证优化后的安全性和功能
|
||||
*
|
||||
* @author optimization
|
||||
* @date 2025/09/23
|
||||
*/
|
||||
@DisplayName("AES加密增强测试")
|
||||
class AESEnhancedTest {
|
||||
|
||||
private AES aes;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
aes = AES.builder();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试密钥和IV必须设置")
|
||||
void testKeyAndIvRequired() {
|
||||
// 测试未设置密钥时抛出异常
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
aes.encrypt("test data");
|
||||
});
|
||||
assertTrue(exception.getMessage().contains("AES密钥未设置"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试密钥长度验证")
|
||||
void testKeyLengthValidation() {
|
||||
// 测试无效密钥长度
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
aes.setKey("short");
|
||||
});
|
||||
assertTrue(exception.getMessage().contains("AES密钥长度必须是16、24或32字节"));
|
||||
|
||||
// 测试有效密钥长度
|
||||
assertDoesNotThrow(() -> {
|
||||
aes.setKey("1234567890123456"); // 16字节
|
||||
aes.setKey("123456789012345678901234"); // 24字节
|
||||
aes.setKey("12345678901234567890123456789012"); // 32字节
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试IV长度验证")
|
||||
void testIvLengthValidation() {
|
||||
// 设置有效密钥
|
||||
aes.setKey("1234567890123456");
|
||||
|
||||
// 测试无效IV长度
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
aes.setIv("short");
|
||||
});
|
||||
assertTrue(exception.getMessage().contains("AES初始向量长度必须是16字节"));
|
||||
|
||||
// 测试有效IV长度
|
||||
assertDoesNotThrow(() -> {
|
||||
aes.setIv("1234567890123456"); // 16字节
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试输入参数验证")
|
||||
void testInputValidation() {
|
||||
aes.setKey("1234567890123456")
|
||||
.setIv("1234567890123456")
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
// 测试空输入
|
||||
IllegalArgumentException encryptException = assertThrows(IllegalArgumentException.class, () -> {
|
||||
aes.encrypt(null);
|
||||
});
|
||||
assertTrue(encryptException.getMessage().contains("加密数据不能为空"));
|
||||
|
||||
IllegalArgumentException decryptException = assertThrows(IllegalArgumentException.class, () -> {
|
||||
aes.decrypt(null);
|
||||
});
|
||||
assertTrue(decryptException.getMessage().contains("解密数据不能为空"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试字符集设置验证")
|
||||
void testCharsetValidation() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
aes.setCharset(null);
|
||||
});
|
||||
assertTrue(exception.getMessage().contains("字符集不能为空"));
|
||||
|
||||
// 测试有效字符集设置
|
||||
assertDoesNotThrow(() -> {
|
||||
aes.setCharset(StandardCharsets.UTF_8);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试ECB模式不需要IV")
|
||||
void testECBModeWithoutIV() throws BaseException {
|
||||
String data = "Hello World!";
|
||||
aes.setKey("1234567890123456")
|
||||
.setAlgorithm(AES.ALGORITHM.AES_ECB_PKCS5Padding);
|
||||
|
||||
// ECB模式不需要IV
|
||||
String encrypted = aes.encrypt(data);
|
||||
assertNotNull(encrypted);
|
||||
assertFalse(encrypted.isEmpty());
|
||||
|
||||
String decrypted = aes.decrypt(encrypted);
|
||||
assertEquals(data, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试完整的加密解密流程")
|
||||
void testCompleteEncryptDecryptFlow() throws BaseException {
|
||||
String originalData = "这是一个测试字符串包含中文!";
|
||||
|
||||
aes.setKey("1234567890123456")
|
||||
.setIv("1234567890123456")
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding)
|
||||
.setCharset(StandardCharsets.UTF_8);
|
||||
|
||||
String encrypted = aes.encrypt(originalData);
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(originalData, encrypted);
|
||||
|
||||
String decrypted = aes.decrypt(encrypted);
|
||||
assertEquals(originalData, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试不同AES算法模式")
|
||||
void testDifferentAlgorithms() throws BaseException {
|
||||
String data = "Test Data";
|
||||
String key = "1234567890123456";
|
||||
String iv = "1234567890123456";
|
||||
|
||||
AES.ALGORITHM[] algorithms = {
|
||||
AES.ALGORITHM.AES_CBC_PKCS5Padding,
|
||||
AES.ALGORITHM.AES_OFB_PKCS5Padding,
|
||||
AES.ALGORITHM.AES_ECB_PKCS5Padding
|
||||
};
|
||||
|
||||
for (AES.ALGORITHM algorithm : algorithms) {
|
||||
AES testAes = AES.builder()
|
||||
.setKey(key)
|
||||
.setAlgorithm(algorithm);
|
||||
|
||||
if (!algorithm.code.contains("ECB")) {
|
||||
testAes.setIv(iv);
|
||||
}
|
||||
|
||||
String encrypted = testAes.encrypt(data);
|
||||
String decrypted = testAes.decrypt(encrypted);
|
||||
|
||||
assertEquals(data, decrypted, "Algorithm " + algorithm.code + " failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试建造者模式")
|
||||
void testBuilderPattern() {
|
||||
AES aes1 = AES.builder();
|
||||
AES aes2 = AES.builder();
|
||||
|
||||
// 现在返回不同的实例,避免状态共享
|
||||
assertNotNull(aes1);
|
||||
assertNotNull(aes2);
|
||||
assertNotSame(aes1, aes2); // 确认不是单例
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试解密异常处理")
|
||||
void testDecryptErrorHandling() {
|
||||
aes.setKey("1234567890123456")
|
||||
.setIv("1234567890123456")
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
// 测试无效Base64数据
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
aes.decrypt("invalid_base64_data");
|
||||
});
|
||||
assertNotNull(exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试不同密钥长度")
|
||||
void testDifferentKeyLengths() throws BaseException {
|
||||
String data = "Test Data";
|
||||
String iv = "1234567890123456";
|
||||
|
||||
// 测试16字节密钥
|
||||
AES aes16 = AES.builder()
|
||||
.setKey("1234567890123456")
|
||||
.setIv(iv)
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
String encrypted16 = aes16.encrypt(data);
|
||||
String decrypted16 = aes16.decrypt(encrypted16);
|
||||
assertEquals(data, decrypted16);
|
||||
|
||||
// 测试24字节密钥
|
||||
AES aes24 = AES.builder()
|
||||
.setKey("123456789012345678901234")
|
||||
.setIv(iv)
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
String encrypted24 = aes24.encrypt(data);
|
||||
String decrypted24 = aes24.decrypt(encrypted24);
|
||||
assertEquals(data, decrypted24);
|
||||
|
||||
// 测试32字节密钥
|
||||
AES aes32 = AES.builder()
|
||||
.setKey("12345678901234567890123456789012")
|
||||
.setIv(iv)
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
String encrypted32 = aes32.encrypt(data);
|
||||
String decrypted32 = aes32.decrypt(encrypted32);
|
||||
assertEquals(data, decrypted32);
|
||||
}
|
||||
}
|
237
src/test/java/com/yexuejc/base/util/ObjUtilEnhancedTest.java
Normal file
237
src/test/java/com/yexuejc/base/util/ObjUtilEnhancedTest.java
Normal file
@@ -0,0 +1,237 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* ObjUtil增强测试类 - 验证优化后的异常处理和功能
|
||||
*
|
||||
* @author optimization
|
||||
* @date 2025/09/23
|
||||
*/
|
||||
@DisplayName("ObjUtil增强测试")
|
||||
class ObjUtilEnhancedTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试基本字段复制功能")
|
||||
void testBasicFieldCopy() throws Exception {
|
||||
SourceObject source = new SourceObject();
|
||||
source.setName("Test Name");
|
||||
source.setAge(25);
|
||||
source.setEmail("test@example.com");
|
||||
|
||||
TargetObject target = ObjUtil.copy(source, TargetObject.class, null, null);
|
||||
|
||||
assertNotNull(target);
|
||||
assertEquals(source.getName(), target.getName());
|
||||
assertEquals(source.getAge(), target.getAge());
|
||||
assertEquals(source.getEmail(), target.getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试包含字段过滤")
|
||||
void testIncludeFieldFiltering() throws Exception {
|
||||
SourceObject source = new SourceObject();
|
||||
source.setName("Test Name");
|
||||
source.setAge(25);
|
||||
source.setEmail("test@example.com");
|
||||
|
||||
List<String> includeFields = Arrays.asList("name", "age");
|
||||
TargetObject target = ObjUtil.copy(source, TargetObject.class, includeFields, null);
|
||||
|
||||
assertNotNull(target);
|
||||
assertEquals(source.getName(), target.getName());
|
||||
assertEquals(source.getAge(), target.getAge());
|
||||
assertNull(target.getEmail()); // 不在包含列表中
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试排除字段过滤")
|
||||
void testExcludeFieldFiltering() throws Exception {
|
||||
SourceObject source = new SourceObject();
|
||||
source.setName("Test Name");
|
||||
source.setAge(25);
|
||||
source.setEmail("test@example.com");
|
||||
|
||||
List<String> excludeFields = Arrays.asList("email");
|
||||
TargetObject target = ObjUtil.copy(source, TargetObject.class, null, excludeFields);
|
||||
|
||||
assertNotNull(target);
|
||||
assertEquals(source.getName(), target.getName());
|
||||
assertEquals(source.getAge(), target.getAge());
|
||||
assertNull(target.getEmail()); // 被排除
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试使用Setter方法复制")
|
||||
void testCopyWithSetter() throws Exception {
|
||||
SourceObject source = new SourceObject();
|
||||
source.setName("Test Name");
|
||||
source.setAge(25);
|
||||
|
||||
TargetObjectWithSetter target = ObjUtil.copy(source, TargetObjectWithSetter.class, true);
|
||||
|
||||
assertNotNull(target);
|
||||
assertEquals(source.getName(), target.getName());
|
||||
assertEquals(source.getAge(), target.getAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试字段不匹配的处理")
|
||||
void testFieldMismatchHandling() throws Exception {
|
||||
SourceObjectWithExtraField source = new SourceObjectWithExtraField();
|
||||
source.setName("Test Name");
|
||||
source.setAge(25);
|
||||
source.setExtraField("Extra Value");
|
||||
|
||||
// 目标类没有extraField字段
|
||||
TargetObject target = ObjUtil.copy(source, TargetObject.class, null, null);
|
||||
|
||||
assertNotNull(target);
|
||||
assertEquals(source.getName(), target.getName());
|
||||
assertEquals(source.getAge(), target.getAge());
|
||||
// extraField应该被忽略,不会导致异常
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试空源对象处理")
|
||||
void testNullSourceHandling() {
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
ObjUtil.copy(null, TargetObject.class, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取所有字段功能")
|
||||
void testGetAllFields() {
|
||||
List<java.lang.reflect.Field> fields = ObjUtil.getAllFields(ChildObject.class);
|
||||
|
||||
assertNotNull(fields);
|
||||
assertTrue(fields.size() >= 3); // 至少包含child字段和父类的两个字段
|
||||
|
||||
// 验证包含父类字段
|
||||
boolean hasParentField1 = fields.stream().anyMatch(f -> "parentField1".equals(f.getName()));
|
||||
boolean hasParentField2 = fields.stream().anyMatch(f -> "parentField2".equals(f.getName()));
|
||||
boolean hasChildField = fields.stream().anyMatch(f -> "childField".equals(f.getName()));
|
||||
|
||||
assertTrue(hasParentField1);
|
||||
assertTrue(hasParentField2);
|
||||
assertTrue(hasChildField);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取所有Getter方法功能")
|
||||
void testGetAllGetterMethods() {
|
||||
List<java.lang.reflect.Method> methods = ObjUtil.getAllGetterMethods(ChildObject.class, "get");
|
||||
|
||||
assertNotNull(methods);
|
||||
assertTrue(methods.size() >= 3); // 至少包含三个getter方法
|
||||
|
||||
// 验证包含父类方法
|
||||
boolean hasGetParentField1 = methods.stream().anyMatch(m -> "getParentField1".equals(m.getName()));
|
||||
boolean hasGetParentField2 = methods.stream().anyMatch(m -> "getParentField2".equals(m.getName()));
|
||||
boolean hasGetChildField = methods.stream().anyMatch(m -> "getChildField".equals(m.getName()));
|
||||
|
||||
assertTrue(hasGetParentField1);
|
||||
assertTrue(hasGetParentField2);
|
||||
assertTrue(hasGetChildField);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试首字母小写工具方法")
|
||||
void testLowerCaseFirstChar() {
|
||||
assertEquals("test", ObjUtil.lowerCaseFirstChar("Test"));
|
||||
assertEquals("tEST", ObjUtil.lowerCaseFirstChar("TEST"));
|
||||
assertEquals("", ObjUtil.lowerCaseFirstChar(""));
|
||||
assertNull(ObjUtil.lowerCaseFirstChar(null));
|
||||
assertEquals("a", ObjUtil.lowerCaseFirstChar("A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试目标类没有默认构造函数的情况")
|
||||
void testTargetClassWithoutDefaultConstructor() {
|
||||
SourceObject source = new SourceObject();
|
||||
source.setName("Test");
|
||||
|
||||
assertThrows(Exception.class, () -> {
|
||||
ObjUtil.copy(source, ClassWithoutDefaultConstructor.class, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
// 测试用的内部类
|
||||
|
||||
static class SourceObject {
|
||||
private String name;
|
||||
private Integer age;
|
||||
private String email;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Integer getAge() { return age; }
|
||||
public void setAge(Integer age) { this.age = age; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
}
|
||||
|
||||
static class SourceObjectWithExtraField extends SourceObject {
|
||||
private String extraField;
|
||||
|
||||
public String getExtraField() { return extraField; }
|
||||
public void setExtraField(String extraField) { this.extraField = extraField; }
|
||||
}
|
||||
|
||||
static class TargetObject {
|
||||
private String name;
|
||||
private Integer age;
|
||||
private String email;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Integer getAge() { return age; }
|
||||
public void setAge(Integer age) { this.age = age; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
}
|
||||
|
||||
static class TargetObjectWithSetter {
|
||||
private String name;
|
||||
private Integer age;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Integer getAge() { return age; }
|
||||
public void setAge(Integer age) { this.age = age; }
|
||||
}
|
||||
|
||||
static class ParentObject {
|
||||
private String parentField1;
|
||||
private String parentField2;
|
||||
|
||||
public String getParentField1() { return parentField1; }
|
||||
public void setParentField1(String parentField1) { this.parentField1 = parentField1; }
|
||||
public String getParentField2() { return parentField2; }
|
||||
public void setParentField2(String parentField2) { this.parentField2 = parentField2; }
|
||||
}
|
||||
|
||||
static class ChildObject extends ParentObject {
|
||||
private String childField;
|
||||
|
||||
public String getChildField() { return childField; }
|
||||
public void setChildField(String childField) { this.childField = childField; }
|
||||
}
|
||||
|
||||
static class ClassWithoutDefaultConstructor {
|
||||
private String name;
|
||||
|
||||
public ClassWithoutDefaultConstructor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
}
|
||||
}
|
288
src/test/java/com/yexuejc/base/util/StrUtilEnhancedTest.java
Normal file
288
src/test/java/com/yexuejc/base/util/StrUtilEnhancedTest.java
Normal file
@@ -0,0 +1,288 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* StrUtil增强测试类 - 验证优化后的性能和线程安全性
|
||||
*
|
||||
* @author optimization
|
||||
* @date 2025/09/23
|
||||
*/
|
||||
@DisplayName("StrUtil增强测试")
|
||||
class StrUtilEnhancedTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试MD5计算的一致性")
|
||||
void testMD5Consistency() {
|
||||
String input = "Hello World!";
|
||||
String expected = "ed076287532e86365e841e92bfc50d8c"; // 已知MD5值
|
||||
|
||||
String result1 = StrUtil.toMD5(input);
|
||||
String result2 = StrUtil.toMD5(input);
|
||||
|
||||
assertEquals(expected, result1);
|
||||
assertEquals(result1, result2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试SHA256计算的一致性")
|
||||
void testSHA256Consistency() {
|
||||
String input = "Hello World!";
|
||||
String expected = "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"; // 已知SHA256值
|
||||
|
||||
String result1 = StrUtil.toSHA256(input);
|
||||
String result2 = StrUtil.toSHA256(input);
|
||||
|
||||
assertEquals(expected, result1);
|
||||
assertEquals(result1, result2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试MD5处理null值")
|
||||
void testMD5WithNull() {
|
||||
assertNull(StrUtil.toMD5(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试SHA256处理null值")
|
||||
void testSHA256WithNull() {
|
||||
assertNull(StrUtil.toSHA256(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试不支持的SHA算法")
|
||||
void testUnsupportedSHAAlgorithm() {
|
||||
String result = StrUtil.toSHA("test", "INVALID_ALGORITHM");
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试中文字符的MD5计算")
|
||||
void testMD5WithChinese() {
|
||||
String input = "你好世界";
|
||||
String result = StrUtil.toMD5(input);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(32, result.length()); // MD5长度应为32
|
||||
assertTrue(result.matches("[a-f0-9]+"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试中文字符的SHA256计算")
|
||||
void testSHA256WithChinese() {
|
||||
String input = "你好世界";
|
||||
String result = StrUtil.toSHA256(input);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(64, result.length()); // SHA256长度应为64
|
||||
assertTrue(result.matches("[a-f0-9]+"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试长字符串的MD5计算")
|
||||
void testMD5WithLongString() {
|
||||
StringBuilder longString = new StringBuilder();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
longString.append("abcdefghij");
|
||||
}
|
||||
|
||||
String result = StrUtil.toMD5(longString.toString());
|
||||
assertNotNull(result);
|
||||
assertEquals(32, result.length());
|
||||
}
|
||||
|
||||
@RepeatedTest(10)
|
||||
@DisplayName("测试SecureRandom生成的随机性")
|
||||
void testCodeIdRandomness() {
|
||||
String id = "12345678901234567890123456789012"; // 32位ID
|
||||
|
||||
String coded1 = StrUtil.codeId(id);
|
||||
String coded2 = StrUtil.codeId(id);
|
||||
|
||||
assertEquals(64, coded1.length());
|
||||
assertEquals(64, coded2.length());
|
||||
|
||||
// 由于使用SecureRandom,两次编码结果应该不同
|
||||
assertNotEquals(coded1, coded2);
|
||||
|
||||
// 解码后应该得到原始ID
|
||||
String decoded1 = StrUtil.decodeId(coded1);
|
||||
String decoded2 = StrUtil.decodeId(coded2);
|
||||
|
||||
assertEquals(id, decoded1);
|
||||
assertEquals(id, decoded2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试codeId处理无效输入")
|
||||
void testCodeIdWithInvalidInput() {
|
||||
// 测试null
|
||||
assertNull(StrUtil.codeId(null));
|
||||
|
||||
// 测试长度不是32的字符串
|
||||
String shortId = "short";
|
||||
assertEquals(shortId, StrUtil.codeId(shortId));
|
||||
|
||||
String longId = "this_is_a_very_long_string_that_exceeds_32_characters";
|
||||
assertEquals(longId, StrUtil.codeId(longId));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试decodeId处理无效输入")
|
||||
void testDecodeIdWithInvalidInput() {
|
||||
// 测试null
|
||||
assertNull(StrUtil.decodeId(null));
|
||||
|
||||
// 测试长度不是64的字符串
|
||||
String shortCoded = "short";
|
||||
assertEquals(shortCoded, StrUtil.decodeId(shortCoded));
|
||||
|
||||
String longCoded = "this_is_a_very_long_string_that_exceeds_64_characters_and_should_be_returned_as_is";
|
||||
assertEquals(longCoded, StrUtil.decodeId(longCoded));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试线程安全性 - MD5计算")
|
||||
void testMD5ThreadSafety() throws InterruptedException {
|
||||
final int threadCount = 20;
|
||||
final int iterationsPerThread = 100;
|
||||
final String testString = "Thread Safety Test";
|
||||
final String expectedMD5 = StrUtil.toMD5(testString);
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||
AtomicBoolean hasError = new AtomicBoolean(false);
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
executor.submit(() -> {
|
||||
for (int j = 0; j < iterationsPerThread; j++) {
|
||||
String result = StrUtil.toMD5(testString);
|
||||
if (!expectedMD5.equals(result)) {
|
||||
hasError.set(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
|
||||
assertFalse(hasError.get(), "MD5计算在多线程环境下出现不一致结果");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试线程安全性 - SHA256计算")
|
||||
void testSHA256ThreadSafety() throws InterruptedException {
|
||||
final int threadCount = 20;
|
||||
final int iterationsPerThread = 100;
|
||||
final String testString = "Thread Safety Test";
|
||||
final String expectedSHA256 = StrUtil.toSHA256(testString);
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||
AtomicBoolean hasError = new AtomicBoolean(false);
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
executor.submit(() -> {
|
||||
for (int j = 0; j < iterationsPerThread; j++) {
|
||||
String result = StrUtil.toSHA256(testString);
|
||||
if (!expectedSHA256.equals(result)) {
|
||||
hasError.set(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
|
||||
assertFalse(hasError.get(), "SHA256计算在多线程环境下出现不一致结果");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试线程安全性 - codeId生成")
|
||||
void testCodeIdThreadSafety() throws InterruptedException {
|
||||
final int threadCount = 10;
|
||||
final int iterationsPerThread = 50;
|
||||
final String testId = "12345678901234567890123456789012";
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||
Set<String> results = new HashSet<>();
|
||||
AtomicBoolean hasError = new AtomicBoolean(false);
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
executor.submit(() -> {
|
||||
for (int j = 0; j < iterationsPerThread; j++) {
|
||||
String coded = StrUtil.codeId(testId);
|
||||
String decoded = StrUtil.decodeId(coded);
|
||||
|
||||
if (!testId.equals(decoded)) {
|
||||
hasError.set(true);
|
||||
break;
|
||||
}
|
||||
|
||||
synchronized (results) {
|
||||
results.add(coded);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
|
||||
assertFalse(hasError.get(), "codeId在多线程环境下解码失败");
|
||||
|
||||
// 验证生成的编码都是唯一的(由于使用SecureRandom)
|
||||
assertEquals(threadCount * iterationsPerThread, results.size(),
|
||||
"生成的编码应该都是唯一的");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试性能 - MD5计算")
|
||||
void testMD5Performance() {
|
||||
String testString = "Performance test string with some content to hash";
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
StrUtil.toMD5(testString);
|
||||
}
|
||||
|
||||
long endTime = System.nanoTime();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
// 确保性能在合理范围内(这里只是确保能完成,具体时间取决于硬件)
|
||||
assertTrue(duration > 0, "MD5计算应该花费一些时间");
|
||||
|
||||
// 可以添加更具体的性能断言,但要考虑不同硬件的差异
|
||||
System.out.println("MD5 10000次计算耗时: " + duration / 1_000_000 + "ms");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试边界值")
|
||||
void testBoundaryValues() {
|
||||
// 测试空字符串
|
||||
String emptyResult = StrUtil.toMD5("");
|
||||
assertNotNull(emptyResult);
|
||||
assertEquals(32, emptyResult.length());
|
||||
|
||||
// 测试单字符
|
||||
String singleCharResult = StrUtil.toMD5("a");
|
||||
assertNotNull(singleCharResult);
|
||||
assertEquals(32, singleCharResult.length());
|
||||
|
||||
// 测试特殊字符
|
||||
String specialCharResult = StrUtil.toMD5("!@#$%^&*()");
|
||||
assertNotNull(specialCharResult);
|
||||
assertEquals(32, specialCharResult.length());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user