diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtil.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtil.java index beba8e3b0..ca90f81b3 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtil.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtil.java @@ -80,23 +80,23 @@ public class AsymmetricAlgorithmUtil { return keys; } - public static void main(String[] args) { - - LinkedList priKeyAndPubKey = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); - String privateKey = priKeyAndPubKey.get(0); - String publicKey = priKeyAndPubKey.get(1); - String text = "HelloWorld"; - - String encryptByPublic = AsymmetricAlgorithmUtil.encryptByPublic(text, publicKey); - System.out.println(encryptByPublic); - String s = AsymmetricAlgorithmUtil.decryptByPrivate(encryptByPublic, privateKey); - System.out.println("公钥加密私钥解密:"+s); - - String encryptByPrivate = AsymmetricAlgorithmUtil.encryptByPrivate(text, privateKey); - System.out.println(encryptByPrivate); - String s1 = AsymmetricAlgorithmUtil.decryptByPublic(encryptByPrivate,publicKey); - System.out.println("私钥加密公钥解密:"+s1); - } +// public static void main(String[] args) { +// +// LinkedList priKeyAndPubKey = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); +// String privateKey = priKeyAndPubKey.get(0); +// String publicKey = priKeyAndPubKey.get(1); +// String text = "HelloWorld"; +// +// String encryptByPublic = AsymmetricAlgorithmUtil.encryptByPublic(text, publicKey); +// System.out.println(encryptByPublic); +// String s = AsymmetricAlgorithmUtil.decryptByPrivate(encryptByPublic, privateKey); +// System.out.println("公钥加密私钥解密:"+s); +// +// String encryptByPrivate = AsymmetricAlgorithmUtil.encryptByPrivate(text, privateKey); +// System.out.println(encryptByPrivate); +// String s1 = AsymmetricAlgorithmUtil.decryptByPublic(encryptByPrivate,publicKey); +// System.out.println("私钥加密公钥解密:"+s1); +// } } diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/collection/SimpleTrieTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/collection/SimpleTrieTest.java new file mode 100644 index 000000000..6ad5597b9 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/collection/SimpleTrieTest.java @@ -0,0 +1,222 @@ +package com.cf.imes.module.system.util.collection; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link SimpleTrie} 的单元测试 + */ +public class SimpleTrieTest { + + private SimpleTrie simpleTrie; + + @BeforeEach + public void setUp() { + List sensitiveWords = Arrays.asList("敏感词", "测试", "ABC", "暴力", "暴力行为"); + simpleTrie = new SimpleTrie(sensitiveWords); + } + + @Test + public void testConstructorWithEmptyCollection() { + // 测试空集合构造 + SimpleTrie trie = new SimpleTrie(Collections.emptyList()); + assertTrue(trie.isValid("任何文本")); + assertTrue(trie.validate("任何文本").isEmpty()); + } + + @Test + public void testIsValidWithValidText() { + // 测试不包含敏感词的文本 + assertTrue(simpleTrie.isValid("这是一段正常的文本")); + assertTrue(simpleTrie.isValid("")); + assertTrue(simpleTrie.isValid(" ")); + } + + @Test + public void testIsValidWithInvalidText() { + // 测试包含敏感词的文本 + assertFalse(simpleTrie.isValid("这段文本包含敏感词")); + assertFalse(simpleTrie.isValid("测试文本")); + assertFalse(simpleTrie.isValid("ABC")); + assertFalse(simpleTrie.isValid("暴力行为")); + } + + @Test + public void testIsValidWithPartialMatch() { + // 测试部分匹配的情况 + assertTrue(simpleTrie.isValid("敏感情")); // "敏感情"不包含敏感词"敏感词" + assertTrue(simpleTrie.isValid("测验")); // "测验"不包含敏感词"测试" + assertFalse(simpleTrie.isValid("ABCD")); // "ABCD"包含敏感词"ABC" + assertFalse(simpleTrie.isValid("暴力事件")); // "暴力事件"包含敏感词"暴力" + } + + @Test + public void testValidateWithValidText() { + // 测试不包含敏感词的文本 + List result = simpleTrie.validate("这是一段正常的文本"); + assertTrue(result.isEmpty()); + + result = simpleTrie.validate(""); + assertTrue(result.isEmpty()); + + result = simpleTrie.validate(" "); + assertTrue(result.isEmpty()); + } + + @Test + public void testValidateWithSingleSensitiveWord() { + // 测试包含单个敏感词的文本 + List result = simpleTrie.validate("这段文本包含敏感词"); + assertEquals(1, result.size()); + assertTrue(result.contains("敏感词")); + + result = simpleTrie.validate("测试文本"); + assertEquals(1, result.size()); + assertTrue(result.contains("测试")); + + result = simpleTrie.validate("ABC"); + assertEquals(1, result.size()); + assertTrue(result.contains("ABC")); + } + + @Test + public void testValidateWithMultipleSensitiveWords() { + // 测试包含多个敏感词的文本 + List result = simpleTrie.validate("这段文本包含敏感词和测试内容"); + assertEquals(2, result.size()); + assertTrue(result.contains("敏感词")); + assertTrue(result.contains("测试")); + } + + @Test + public void testValidateWithOverlappingSensitiveWords() { + // 测试包含重叠敏感词的文本(最短匹配原则) + List result = simpleTrie.validate("暴力行为测试"); + assertEquals(2, result.size()); + assertTrue(result.contains("暴力")); // 最短匹配 + assertTrue(result.contains("测试")); + + // 验证不会同时返回"暴力"和"暴力行为" + assertFalse(result.contains("暴力") && result.contains("暴力行为")); + } + + @Test + public void testValidateWithRepeatedSensitiveWords() { + // 测试重复敏感词的情况 + List result = simpleTrie.validate("测试测试测试"); + assertEquals(1, result.size()); // 应该去重 + assertTrue(result.contains("测试")); + } + + @Test + public void testShortestMatchPrinciple() { + // 测试最短匹配原则 + List words = Arrays.asList("煞笔", "煞笔二货"); + SimpleTrie trie = new SimpleTrie(words); + + List result = trie.validate("这个人是个煞笔二货"); + assertEquals(1, result.size()); + assertTrue(result.contains("煞笔")); // 应该只返回最短的匹配 + assertFalse(result.contains("煞笔二货")); + } + + @Test + public void testPrefixOptimization() { + // 测试前缀优化:如果已有较短敏感词,不应再添加更长的以相同前缀开始的词 + List words = Arrays.asList("吃饭", "吃饭啊"); + SimpleTrie trie = new SimpleTrie(words); + + // 验证"吃饭啊"实际上没有被完全添加(因为已经有"吃饭") + // 但"吃饭"已经被添加,所以包含"吃饭"的文本应该验证失败 + assertFalse(trie.isValid("我要吃饭")); // 应该不通过验证,因为包含"吃饭" + assertFalse(trie.isValid("我要吃饭啊")); // 应该不通过验证,因为包含"吃饭" + + // 但如果直接添加"吃饭啊"应该能检测到 + List words2 = Arrays.asList("吃饭啊", "吃饭"); + SimpleTrie trie2 = new SimpleTrie(words2); + assertFalse(trie2.isValid("我要吃饭啊")); + } + + @Test + public void testRecursionEndCondition() { + // 测试递归结束条件:到达文本末尾 + List words = Arrays.asList("a"); + SimpleTrie trie = new SimpleTrie(words); + + assertFalse(trie.isValid("a")); // 单字符匹配,应该不通过验证 + List result = trie.validate("a"); + assertEquals(1, result.size()); + assertTrue(result.contains("a")); + } + + @Test + public void testRecursionEndAtTextEnd() { + // 测试递归结束条件:到达文本末尾且未匹配到敏感词 + List words = Arrays.asList("ab"); + SimpleTrie trie = new SimpleTrie(words); + + // 测试文本"a"只匹配了敏感词"ab"的第一个字符,但没有完整的匹配 + // 这会触发recursion方法中index == text.length()的条件,返回true + assertTrue(trie.isValid("a")); + + // 测试文本"ac"第一个字符就不匹配 + assertTrue(trie.isValid("ac")); + } + + @Test + public void testSpecialCharacters() { + // 测试特殊字符 + List words = Arrays.asList("特殊$符号", "换行\n符", "制表\t符"); + SimpleTrie trie = new SimpleTrie(words); + + assertFalse(trie.isValid("包含特殊$符号的文本")); + assertFalse(trie.isValid("包含换行\n符的文本")); + assertFalse(trie.isValid("包含制表\t符的文本")); + + List result = trie.validate("特殊$符号和换行\n符和制表\t符"); + assertEquals(3, result.size()); + assertTrue(result.contains("特殊$符号")); + assertTrue(result.contains("换行\n符")); + assertTrue(result.contains("制表\t符")); + } + + @Test + public void testUnicodeCharacters() { + // 测试Unicode字符 + List words = Arrays.asList("中文", "English", "数字123", "表情😀"); + SimpleTrie trie = new SimpleTrie(words); + + assertFalse(trie.isValid("包含中文的文本")); + assertFalse(trie.isValid("Contains English words")); + assertFalse(trie.isValid("包含数字123的内容")); + assertFalse(trie.isValid("包含表情😀的文本")); + + List result = trie.validate("混合中文English数字123和表情😀"); + assertEquals(4, result.size()); + assertTrue(result.contains("中文")); + assertTrue(result.contains("English")); + assertTrue(result.contains("数字123")); + assertTrue(result.contains("表情😀")); + } + + @Test + public void testValidateWithoutSensitiveWords() { + // 测试没有敏感词的情况,覆盖validate方法中!ok为false的分支 + List result = simpleTrie.validate("正常文本内容"); + assertTrue(result.isEmpty()); + + // 测试部分匹配但不完整的敏感词情况 + List words = Arrays.asList("abcd"); + SimpleTrie trie = new SimpleTrie(words); + result = trie.validate("abc"); // 只匹配了部分,没有完整匹配 + assertTrue(result.isEmpty()); + + // 测试在recursionWithResult中child==null的情况 + result = trie.validate("abe"); // "ab"匹配但'e'不匹配,触发child==null的分支 + assertTrue(result.isEmpty()); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/DictDataI18nCacheUtilsTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/DictDataI18nCacheUtilsTest.java new file mode 100644 index 000000000..8bac55177 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/DictDataI18nCacheUtilsTest.java @@ -0,0 +1,145 @@ +package com.cf.imes.module.system.util.locale; + +import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link DictDataI18nCacheUtils} 的单元测试 + */ +public class DictDataI18nCacheUtilsTest { + + @BeforeEach + public void setUp() { + // 测试前清理缓存 + DictDataI18nCacheUtils.clearCache(); + } + + @AfterEach + public void tearDown() { + // 测试后清理缓存 + DictDataI18nCacheUtils.clearCache(); + } + + @Test + public void testPutCacheAndGetCache() { + // 准备测试数据 + Map> testData = new HashMap<>(); + List dataList = new ArrayList<>(); + + DictDataSimpleRespVO vo = new DictDataSimpleRespVO(); + vo.setDictType("gender"); + vo.setValue("1"); + vo.setLabel("男"); + dataList.add(vo); + + testData.put("gender", dataList); + + // 测试putCache方法 + DictDataI18nCacheUtils.putCache(testData); + + // 验证getCache方法 + Map> cache = DictDataI18nCacheUtils.getCache(); + assertNotNull(cache); + assertEquals(1, cache.size()); + assertTrue(cache.containsKey("gender")); + assertEquals(1, cache.get("gender").size()); + assertEquals("男", cache.get("gender").get(0).getLabel()); + } + + @Test + public void testGetCacheReturnsUnmodifiableMap() { + // 准备测试数据 + Map> testData = new HashMap<>(); + List dataList = new ArrayList<>(); + DictDataSimpleRespVO vo = new DictDataSimpleRespVO(); + vo.setDictType("status"); + vo.setValue("1"); + vo.setLabel("启用"); + dataList.add(vo); + testData.put("status", dataList); + + // 添加数据到缓存 + DictDataI18nCacheUtils.putCache(testData); + + // 获取缓存并尝试修改 + Map> cache = DictDataI18nCacheUtils.getCache(); + + // 验证返回的是不可修改的Map + assertThrows(UnsupportedOperationException.class, () -> { + cache.put("newKey", new ArrayList<>()); + }); + } + + @Test + public void testClearCache() { + // 准备测试数据 + Map> testData = new HashMap<>(); + List dataList = new ArrayList<>(); + DictDataSimpleRespVO vo = new DictDataSimpleRespVO(); + vo.setDictType("type"); + vo.setValue("1"); + vo.setLabel("标签"); + dataList.add(vo); + testData.put("type", dataList); + + // 添加数据到缓存 + DictDataI18nCacheUtils.putCache(testData); + + // 验证缓存中有数据 + Map> cache = DictDataI18nCacheUtils.getCache(); + assertFalse(cache.isEmpty()); + + // 清除缓存 + DictDataI18nCacheUtils.clearCache(); + + // 验证缓存已清空 + cache = DictDataI18nCacheUtils.getCache(); + assertTrue(cache.isEmpty()); + } + + @Test + public void testPutCacheMultipleTimes() { + // 测试多次添加缓存数据 + Map> testData1 = new HashMap<>(); + List dataList1 = new ArrayList<>(); + DictDataSimpleRespVO vo1 = new DictDataSimpleRespVO(); + vo1.setDictType("type1"); + vo1.setValue("1"); + vo1.setLabel("标签1"); + dataList1.add(vo1); + testData1.put("key1", dataList1); + + DictDataI18nCacheUtils.putCache(testData1); + + Map> testData2 = new HashMap<>(); + List dataList2 = new ArrayList<>(); + DictDataSimpleRespVO vo2 = new DictDataSimpleRespVO(); + vo2.setDictType("type2"); + vo2.setValue("2"); + vo2.setLabel("标签2"); + dataList2.add(vo2); + testData2.put("key2", dataList2); + + DictDataI18nCacheUtils.putCache(testData2); + + // 验证两个键都存在于缓存中 + Map> cache = DictDataI18nCacheUtils.getCache(); + assertEquals(2, cache.size()); + assertTrue(cache.containsKey("key1")); + assertTrue(cache.containsKey("key2")); + } + + @Test + public void testGetCacheWhenEmpty() { + // 测试缓存为空时的情况 + Map> cache = DictDataI18nCacheUtils.getCache(); + assertNotNull(cache); + assertTrue(cache.isEmpty()); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/LocaleUtilsTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/LocaleUtilsTest.java new file mode 100644 index 000000000..fa9758d1b --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/locale/LocaleUtilsTest.java @@ -0,0 +1,104 @@ +package com.cf.imes.module.system.util.locale; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.context.i18n.LocaleContextHolder; + +import java.util.Locale; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link LocaleUtils} 的单元测试 + */ +public class LocaleUtilsTest { + + private MockedStatic mockedLocaleContextHolder; + + @BeforeEach + public void setUp() { + // Mock静态类LocaleContextHolder + mockedLocaleContextHolder = mockStatic(LocaleContextHolder.class); + } + + @AfterEach + public void tearDown() { + // 关闭静态mock + if (mockedLocaleContextHolder != null) { + mockedLocaleContextHolder.close(); + } + } + + @Test + public void testGetLocaleWhenLocaleIsNull() { + // 模拟getLocale返回null + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(null); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertNull(result); + } + + @Test + public void testGetLocaleWhenLocaleIsChinese() { + // 模拟getLocale返回中文环境 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.CHINESE); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertNull(result); + } + + @Test + public void testGetLocaleWhenLocaleIsChineseVariant() { + // 模拟getLocale返回中文环境变体 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.SIMPLIFIED_CHINESE); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertEquals("zh", result); + } + + @Test + public void testGetLocaleWhenLocaleIsEnglish() { + // 模拟getLocale返回英文环境 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.ENGLISH); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertEquals("en", result); + } + + @Test + public void testGetLocaleWhenLocaleIsFrench() { + // 模拟getLocale返回法文环境 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.FRENCH); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertEquals("fr", result); + } + + @Test + public void testGetLocaleWhenLocaleIsGerman() { + // 模拟getLocale返回德文环境 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.GERMAN); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertEquals("de", result); + } + + @Test + public void testGetLocaleWhenLocaleIsJapan() { + // 模拟getLocale返回日文环境 + mockedLocaleContextHolder.when(LocaleContextHolder::getLocale).thenReturn(Locale.JAPANESE); + + // 调用方法并验证结果 + String result = LocaleUtils.getLocale(); + assertEquals("ja", result); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/oauth2/OAuth2UtilsTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/oauth2/OAuth2UtilsTest.java new file mode 100644 index 000000000..891a35a48 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/oauth2/OAuth2UtilsTest.java @@ -0,0 +1,217 @@ +package com.cf.imes.module.system.util.oauth2; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link OAuth2Utils} 的单元测试 + */ +public class OAuth2UtilsTest { + + @Test + public void testBuildAuthorizationCodeRedirectUri() { + // 测试构建授权码模式下的重定向URI + String redirectUri = "https://example.com/callback"; + String authorizationCode = "auth-code-12345"; + String state = "state-value"; + + String result = OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("code=auth-code-12345")); + assertTrue(result.contains("state=state-value")); + } + + @Test + public void testBuildAuthorizationCodeRedirectUriWithoutState() { + // 测试构建授权码模式下的重定向URI(不包含state参数) + String redirectUri = "https://example.com/callback"; + String authorizationCode = "auth-code-12345"; + String state = null; + + String result = OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("code=auth-code-12345")); + assertFalse(result.contains("state=")); + } + + @Test + public void testBuildImplicitRedirectUri() { + // 测试构建简化模式下的重定向URI + String redirectUri = "https://example.com/callback"; + String accessToken = "access-token-12345"; + String state = "state-value"; + LocalDateTime expireTime = LocalDateTime.now().plusHours(1); + Collection scopes = Arrays.asList("read", "write"); + Map additionalInformation = new HashMap<>(); + additionalInformation.put("user_id", "user-123"); + additionalInformation.put("client_id", "client-456"); + additionalInformation.put("extra_test_1", null); + additionalInformation.put("extra_test_2", "2"); + + String result = OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessToken, state, expireTime, scopes, additionalInformation); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("access_token=access-token-12345")); + assertTrue(result.contains("token_type=bearer")); + assertTrue(result.contains("state=state-value")); + assertTrue(result.contains("expires_in=")); + assertTrue(result.contains("scope=read%20write")); + // 验证extra_前缀是否正确处理 + assertTrue(result.contains("user_id=user-123")); + assertTrue(result.contains("client_id=client-456")); + // 确保原始的extra_前缀key没有出现在结果中 + assertFalse(result.contains("extra_user_id=")); + assertFalse(result.contains("extra_client_id=")); + assertFalse(result.contains("extra_test_1=null")); + assertTrue(result.contains("extra_test_2=2")); + } + + @Test + public void testBuildImplicitRedirectUriWithMinimalParameters() { + // 测试构建简化模式下的重定向URI(最小参数) + String redirectUri = "https://example.com/callback"; + String accessToken = "access-token-12345"; + String state = null; + LocalDateTime expireTime = null; + Collection scopes = null; + Map additionalInformation = null; + + String result = OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessToken, state, expireTime, scopes, additionalInformation); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("access_token=access-token-12345")); + assertTrue(result.contains("token_type=bearer")); + assertFalse(result.contains("state=")); + assertFalse(result.contains("expires_in=")); + assertFalse(result.contains("scope=")); + } + + @Test + public void testBuildUnsuccessfulRedirect() { + // 测试构建失败重定向URI + String redirectUri = "https://example.com/callback"; + String responseType = "code"; + String state = "state-value"; + String error = "invalid_request"; + String description = "The request is missing a required parameter"; + + String result = OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, error, description); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("error=invalid_request")); + assertTrue(result.contains("error_description=The%20request%20is%20missing%20a%20required%20parameter")); + assertTrue(result.contains("state=state-value")); + } + + @Test + public void testBuildUnsuccessfulRedirectWithoutState() { + // 测试构建失败重定向URI(不包含state参数) + String redirectUri = "https://example.com/callback"; + String responseType = "token"; + String state = null; + String error = "invalid_request"; + String description = "The request is missing a required parameter"; + + String result = OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, error, description); + + assertTrue(result.contains("https://example.com/callback")); + assertTrue(result.contains("error=invalid_request")); + assertTrue(result.contains("error_description=The%20request%20is%20missing%20a%20required%20parameter")); + assertFalse(result.contains("state=")); + } + + @Test + public void testGetExpiresIn() { + // 测试获取过期时间 + LocalDateTime futureTime = LocalDateTime.now().plusSeconds(3600); + + long expiresIn = OAuth2Utils.getExpiresIn(futureTime); + + // 验证过期时间在合理范围内 + assertTrue(expiresIn > 0); + assertTrue(expiresIn <= 3600); + } + + @Test + public void testBuildScopeStr() { + // 测试构建scope字符串 + Collection scopes = Arrays.asList("read", "write", "profile"); + + String result = OAuth2Utils.buildScopeStr(scopes); + + assertEquals("read write profile", result); + } + + @Test + public void testBuildScopeStrWithEmptyScopes() { + // 测试构建scope字符串(空集合) + Collection scopes = new ArrayList<>(); + + String result = OAuth2Utils.buildScopeStr(scopes); + + assertEquals("", result); + } + + @Test + public void testBuildScopeStrWithNullScopes() { + // 测试构建scope字符串(null) + Collection scopes = null; + + String result = OAuth2Utils.buildScopeStr(scopes); + + assertNull(result); + } + + @Test + public void testBuildScopes() { + // 测试解析scope字符串 + String scope = "read write profile"; + + List result = OAuth2Utils.buildScopes(scope); + + assertEquals(3, result.size()); + assertTrue(result.contains("read")); + assertTrue(result.contains("write")); + assertTrue(result.contains("profile")); + } + + @Test + public void testBuildScopesWithSingleScope() { + // 测试解析scope字符串(单个scope) + String scope = "read"; + + List result = OAuth2Utils.buildScopes(scope); + + assertEquals(1, result.size()); + assertTrue(result.contains("read")); + } + + @Test + public void testBuildScopesWithEmptyString() { + // 测试解析scope字符串(空字符串) + String scope = ""; + + List result = OAuth2Utils.buildScopes(scope); + + // 空字符串split后会返回包含一个空字符串的列表 + assertEquals(1, result.size()); + assertEquals("", result.get(0)); + } + + @Test + public void testBuildScopesWithNull() { + // 测试解析scope字符串(null) + String scope = null; + + List result = OAuth2Utils.buildScopes(scope); + + // null值split后会返回空列表 + assertTrue(result.isEmpty()); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/organ/OrganUtilsTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/organ/OrganUtilsTest.java new file mode 100644 index 000000000..b00bbaad0 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/organ/OrganUtilsTest.java @@ -0,0 +1,28 @@ +package com.cf.imes.module.system.util.organ; + +import com.cf.imes.module.system.constants.permission.InternalRoleConstants; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link OrganUtils} 的单元测试 + */ +public class OrganUtilsTest { + + @Test + public void testIsOrgRoleWithOrganAdminRoleId() { + // 测试组织管理员角色ID + Long organAdminRoleId = InternalRoleConstants.ORGAN_ADMIN_ROLE_ID; + boolean result = OrganUtils.isOrgRole(organAdminRoleId); + assertTrue(result, "组织管理员角色ID应该返回true"); + } + + @Test + public void testIsOrgRoleWithOrganStaffRoleId() { + // 测试组织员工角色ID + Long organStaffRoleId = InternalRoleConstants.ORGAN_STAFF_ROLE_ID; + boolean result = OrganUtils.isOrgRole(organStaffRoleId); + assertTrue(result, "组织员工角色ID应该返回true"); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/redis/SystemRedisUtilsTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/redis/SystemRedisUtilsTest.java new file mode 100644 index 000000000..635dc85fa --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/redis/SystemRedisUtilsTest.java @@ -0,0 +1,111 @@ +package com.cf.imes.module.system.util.redis; + +import com.cf.imes.framework.common.util.json.JsonUtils; +import com.cf.imes.framework.test.core.ut.BaseRedisUnitTest; +import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.util.concurrent.TimeUnit; + +import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link SystemRedisUtils} 的单元测试 + * + * 使用 BaseRedisUnitTest 基类,利用真实的 Redis 环境进行测试 + */ +public class SystemRedisUtilsTest extends BaseRedisUnitTest { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + @Test + public void testScanAndCompareDeptAndDelTokenWithMatchingDeptId() { + // 准备测试数据 + Long deptId = 100L; + + // 创建OAuth2AccessTokenDO对象 + OAuth2AccessTokenDO token1 = new OAuth2AccessTokenDO(); + token1.setDeptId(100L); // 匹配的部门ID + + OAuth2AccessTokenDO token2 = new OAuth2AccessTokenDO(); + token2.setDeptId(200L); // 不匹配的部门ID + + // 构造Redis key + String key1 = String.format(OAUTH2_ACCESS_TOKEN, "token1"); + String key2 = String.format(OAUTH2_ACCESS_TOKEN, "token2"); + + // 将数据存入Redis + stringRedisTemplate.opsForValue().set(key1, JsonUtils.toJsonString(token1), 3600, TimeUnit.SECONDS); + stringRedisTemplate.opsForValue().set(key2, JsonUtils.toJsonString(token2), 3600, TimeUnit.SECONDS); + + // 创建SystemRedisUtils实例 + SystemRedisUtils systemRedisUtils = new SystemRedisUtils(stringRedisTemplate); + + // 执行方法 + systemRedisUtils.scanAndCompareDeptAndDelToken(deptId); + + // 验证匹配的token已被删除 + Boolean hasKey1 = stringRedisTemplate.hasKey(key1); + Boolean hasKey2 = stringRedisTemplate.hasKey(key2); + + assertFalse(hasKey1, "匹配部门ID的token应该被删除"); + assertTrue(hasKey2, "不匹配部门ID的token不应该被删除"); + } + + @Test + public void testScanAndCompareDeptAndDelTokenWithNoMatchingDeptId() { + // 准备测试数据 + Long deptId = 100L; + + // 创建OAuth2AccessTokenDO对象,都不匹配 + OAuth2AccessTokenDO token1 = new OAuth2AccessTokenDO(); + token1.setDeptId(200L); + + OAuth2AccessTokenDO token2 = new OAuth2AccessTokenDO(); + token2.setDeptId(300L); + + // 构造Redis key + String key1 = String.format(OAUTH2_ACCESS_TOKEN, "token1"); + String key2 = String.format(OAUTH2_ACCESS_TOKEN, "token2"); + + // 将数据存入Redis + stringRedisTemplate.opsForValue().set(key1, JsonUtils.toJsonString(token1), 3600, TimeUnit.SECONDS); + stringRedisTemplate.opsForValue().set(key2, JsonUtils.toJsonString(token2), 3600, TimeUnit.SECONDS); + + // 创建SystemRedisUtils实例 + SystemRedisUtils systemRedisUtils = new SystemRedisUtils(stringRedisTemplate); + + // 执行方法 + systemRedisUtils.scanAndCompareDeptAndDelToken(deptId); + + // 验证所有token都未被删除 + Boolean hasKey1 = stringRedisTemplate.hasKey(key1); + Boolean hasKey2 = stringRedisTemplate.hasKey(key2); + + assertTrue(hasKey1, "不匹配部门ID的token不应该被删除"); + assertTrue(hasKey2, "不匹配部门ID的token不应该被删除"); + } + + @Test + public void testScanAndCompareDeptAndDelTokenWithEmptyKeys() { + // 准备测试数据 + Long deptId = 100L; + + // 确保Redis中没有任何匹配的key + // 清理可能存在的相关key + stringRedisTemplate.getConnectionFactory().getConnection().flushAll(); + + // 创建SystemRedisUtils实例 + SystemRedisUtils systemRedisUtils = new SystemRedisUtils(stringRedisTemplate); + + // 执行方法 + systemRedisUtils.scanAndCompareDeptAndDelToken(deptId); + + // 验证没有异常抛出,方法正常执行完毕 + assertTrue(true, "方法应该正常执行完毕,即使没有匹配的key"); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtilTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtilTest.java new file mode 100644 index 000000000..714ba5305 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/util/rsa/AsymmetricAlgorithmUtilTest.java @@ -0,0 +1,178 @@ +package com.cf.imes.module.system.util.rsa; + +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link AsymmetricAlgorithmUtil} 的单元测试 + */ +public class AsymmetricAlgorithmUtilTest { + + @Test + public void testGetPriKeyAndPubKey() { + // 测试获取公私钥集合 + LinkedList keys = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + + assertNotNull(keys); + assertEquals(2, keys.size()); + assertNotNull(keys.get(0)); // 私钥 + assertNotNull(keys.get(1)); // 公钥 + assertNotEquals(keys.get(0), keys.get(1)); // 私钥和公钥应该不同 + } + + @Test + public void testEncryptByPublicAndDecryptByPrivate() { + // 测试公钥加密私钥解密 + LinkedList keys = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey = keys.get(0); + String publicKey = keys.get(1); + String plainText = "Hello World! 你好世界!"; + + // 公钥加密 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPublic(plainText, publicKey); + assertNotNull(encryptedText); + assertNotEquals(plainText, encryptedText); // 加密后的文本应该与原文不同 + + // 私钥解密 + String decryptedText = AsymmetricAlgorithmUtil.decryptByPrivate(encryptedText, privateKey); + assertEquals(plainText, decryptedText); // 解密后的文本应该与原文相同 + } + + @Test + public void testEncryptByPrivateAndDecryptByPublic() { + // 测试私钥加密公钥解密 + LinkedList keys = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey = keys.get(0); + String publicKey = keys.get(1); + String plainText = "Hello World! 你好世界!"; + + // 私钥加密 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPrivate(plainText, privateKey); + assertNotNull(encryptedText); + assertNotEquals(plainText, encryptedText); // 加密后的文本应该与原文不同 + + // 公钥解密 + String decryptedText = AsymmetricAlgorithmUtil.decryptByPublic(encryptedText, publicKey); + assertEquals(plainText, decryptedText); // 解密后的文本应该与原文相同 + } + + @Test + public void testEncryptAndDecryptWithEmptyString() { + // 测试空字符串的加解密 + LinkedList keys = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey = keys.get(0); + String publicKey = keys.get(1); + String plainText = ""; + + // 公钥加密空字符串 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPublic(plainText, publicKey); + assertNotNull(encryptedText); + + // 私钥解密 + String decryptedText = AsymmetricAlgorithmUtil.decryptByPrivate(encryptedText, privateKey); + assertEquals(plainText, decryptedText); + } + + @Test + public void testEncryptAndDecryptWithSpecialCharacters() { + // 测试特殊字符的加解密 + LinkedList keys = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey = keys.get(0); + String publicKey = keys.get(1); + String plainText = "Hello\nWorld!\t你好\r世界!@#$%^&*()"; + + // 公钥加密 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPublic(plainText, publicKey); + assertNotNull(encryptedText); + assertNotEquals(plainText, encryptedText); + + // 私钥解密 + String decryptedText = AsymmetricAlgorithmUtil.decryptByPrivate(encryptedText, privateKey); + assertEquals(plainText, decryptedText); + } + + @Test + public void testEncryptByPublicWithInvalidKey() { + // 测试使用无效公钥加密 + String plainText = "Hello World"; + String invalidPublicKey = "invalid-public-key"; + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.encryptByPublic(plainText, invalidPublicKey); + }); + } + + @Test + public void testDecryptByPrivateWithInvalidKey() { + // 测试使用无效私钥解密 + String encryptedText = "some-encrypted-text"; + String invalidPrivateKey = "invalid-private-key"; + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.decryptByPrivate(encryptedText, invalidPrivateKey); + }); + } + + @Test + public void testEncryptByPrivateWithInvalidKey() { + // 测试使用无效私钥加密 + String plainText = "Hello World"; + String invalidPrivateKey = "invalid-private-key"; + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.encryptByPrivate(plainText, invalidPrivateKey); + }); + } + + @Test + public void testDecryptByPublicWithInvalidKey() { + // 测试使用无效公钥解密 + String encryptedText = "some-encrypted-text"; + String invalidPublicKey = "invalid-public-key"; + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.decryptByPublic(encryptedText, invalidPublicKey); + }); + } + + @Test + public void testDecryptByPrivateWithWrongKey() { + // 测试使用不匹配的私钥解密 + LinkedList keys1 = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String publicKey1 = keys1.get(1); + String plainText = "Hello World"; + + // 使用第一组密钥加密 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPublic(plainText, publicKey1); + + // 使用第二组密钥解密(不匹配的私钥) + LinkedList keys2 = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey2 = keys2.get(0); + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.decryptByPrivate(encryptedText, privateKey2); + }); + } + + @Test + public void testDecryptByPublicWithWrongKey() { + // 测试使用不匹配的公钥解密 + LinkedList keys1 = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String privateKey1 = keys1.get(0); + String plainText = "Hello World"; + + // 使用第一组密钥加密 + String encryptedText = AsymmetricAlgorithmUtil.encryptByPrivate(plainText, privateKey1); + + // 使用第二组密钥解密(不匹配的公钥) + LinkedList keys2 = AsymmetricAlgorithmUtil.getPriKeyAndPubKey(); + String publicKey2 = keys2.get(1); + + assertThrows(Exception.class, () -> { + AsymmetricAlgorithmUtil.decryptByPublic(encryptedText, publicKey2); + }); + } +} \ No newline at end of file