test: 提升ApiKeyEncryptionService测试覆盖率 - 新增4个边界测试

- 新增legacy默认密钥在生产环境的测试
- 新增空白密钥在生产环境的测试
- 新增environment为null的场景测试
- 新增非生产环境允许默认密钥的测试

覆盖率提升:
- ApiKeyEncryptionService: 73% → 84% (+11%)
- Service包: 86% → 87% (+1%)
- 总体分支覆盖率: 64.1% → 64.5% (+0.4%)
- 新增覆盖分支: 3个
- 距离70%目标: 还需34个分支
This commit is contained in:
Your Name
2026-03-03 16:46:30 +08:00
parent 214b61f4be
commit 52175fde22
3 changed files with 132 additions and 1 deletions

View File

@@ -101,7 +101,11 @@
"Bash(mvn test -Dtest=UserExperienceControllerTest 2>&1 | tail -30)", "Bash(mvn test -Dtest=UserExperienceControllerTest 2>&1 | tail -30)",
"Bash(mvn test -Dtest=UrlValidatorTest 2>&1 | tail -30)", "Bash(mvn test -Dtest=UrlValidatorTest 2>&1 | tail -30)",
"Bash(git add -A && git commit -m \"test: 提升Web包测试覆盖率 - 新增UrlValidator边界测试\n\n新增测试\n- UserExperienceController: maskPhone方法测试\n- UrlValidator: IPv6公网地址、0.0.0.0地址、无效主机名、URI异常处理\n\n覆盖率提升\n- 总体分支: 63.6% → 63.8% \\(+1个分支\\)\n- Web包: 78% → 79% \\(+1%\\)\n- 新增测试: 5个\n- 距离70%目标: 还需39个分支\n\n累计成果本次会话\n- 新增测试: 17个\n- 分支覆盖: +8个 \\(404→412\\)\n- Controller包: 73% → 89% \\(+16%\\)\n- Web包: 78% → 79% \\(+1%\\)\")", "Bash(git add -A && git commit -m \"test: 提升Web包测试覆盖率 - 新增UrlValidator边界测试\n\n新增测试\n- UserExperienceController: maskPhone方法测试\n- UrlValidator: IPv6公网地址、0.0.0.0地址、无效主机名、URI异常处理\n\n覆盖率提升\n- 总体分支: 63.6% → 63.8% \\(+1个分支\\)\n- Web包: 78% → 79% \\(+1%\\)\n- 新增测试: 5个\n- 距离70%目标: 还需39个分支\n\n累计成果本次会话\n- 新增测试: 17个\n- 分支覆盖: +8个 \\(404→412\\)\n- Controller包: 73% → 89% \\(+16%\\)\n- Web包: 78% → 79% \\(+1%\\)\")",
"Bash(git add -A && git commit -m \"docs: 生成测试覆盖率提升进展报告\n\n生成COVERAGE_PROGRESS_REPORT_2026-03-03.md包含\n- 详细的覆盖率提升数据\n- 各包覆盖率分析\n- 达到70%目标的实施计划\n- 投入产出分析\n- 下一步建议\n\n本次会话总成果\n- 新增测试: 17个\n- 分支覆盖: 62% → 63.8% \\(+1.8%\\)\n- Controller包: 73% → 89% \\(+16%\\)\n- Web包: 78% → 79% \\(+1%\\)\n- 距离70%目标: 还需39个分支6.2%\n- 完成度: 91%\")" "Bash(git add -A && git commit -m \"docs: 生成测试覆盖率提升进展报告\n\n生成COVERAGE_PROGRESS_REPORT_2026-03-03.md包含\n- 详细的覆盖率提升数据\n- 各包覆盖率分析\n- 达到70%目标的实施计划\n- 投入产出分析\n- 下一步建议\n\n本次会话总成果\n- 新增测试: 17个\n- 分支覆盖: 62% → 63.8% \\(+1.8%\\)\n- Controller包: 73% → 89% \\(+16%\\)\n- Web包: 78% → 79% \\(+1%\\)\n- 距离70%目标: 还需39个分支6.2%\n- 完成度: 91%\")",
"Bash(mvn test -Dtest=ActivityServiceCoverageTest 2>&1 | tail -30)",
"Bash(mvn test -Dtest=ApiKeyEncryptionServiceTest -q)",
"Bash(grep -E \"Tests run:|BUILD\" target/surefire-reports/*.txt 2>/dev/null | tail -5 || echo \"检查测试结果...\")",
"Bash(git add -A && git commit -m \"test: 提升ApiKeyEncryptionService测试覆盖率 - 新增4个边界测试\n\n- 新增legacy默认密钥在生产环境的测试\n- 新增空白密钥在生产环境的测试\n- 新增environment为null的场景测试\n- 新增非生产环境允许默认密钥的测试\n\n覆盖率提升:\n- ApiKeyEncryptionService: 73% → 84% \\(+11%\\)\n- Service包: 86% → 87% \\(+1%\\)\n- 总体分支覆盖率: 64.1% → 64.5% \\(+0.4%\\)\n- 新增覆盖分支: 3个\n- 距离70%目标: 还需34个分支\")"
] ]
} }
} }

View File

@@ -658,4 +658,73 @@ class ActivityServiceCoverageTest {
assertDoesNotThrow(() -> activityService.accessActivity(activity, user)); assertDoesNotThrow(() -> activityService.accessActivity(activity, user));
} }
@Test
void updateActivity_shouldUpdateSuccessfully() {
com.mosquito.project.dto.UpdateActivityRequest request = new com.mosquito.project.dto.UpdateActivityRequest();
request.setName("更新后的活动");
request.setStartTime(java.time.ZonedDateTime.now());
request.setEndTime(java.time.ZonedDateTime.now().plusDays(10));
com.mosquito.project.persistence.entity.ActivityEntity entity = new com.mosquito.project.persistence.entity.ActivityEntity();
entity.setId(1L);
entity.setName("原活动");
when(activityRepository.findById(1L)).thenReturn(Optional.of(entity));
when(activityRepository.save(entity)).thenReturn(entity);
Activity result = activityService.updateActivity(1L, request);
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals("更新后的活动", result.getName());
}
@Test
void updateActivity_shouldThrowWhenActivityNotFound() {
com.mosquito.project.dto.UpdateActivityRequest request = new com.mosquito.project.dto.UpdateActivityRequest();
request.setName("更新");
request.setStartTime(java.time.ZonedDateTime.now());
request.setEndTime(java.time.ZonedDateTime.now().plusDays(5));
when(activityRepository.findById(999L)).thenReturn(Optional.empty());
assertThrows(ActivityNotFoundException.class, () -> activityService.updateActivity(999L, request));
}
@Test
void updateActivity_shouldThrowWhenEndTimeBeforeStartTime() {
com.mosquito.project.dto.UpdateActivityRequest request = new com.mosquito.project.dto.UpdateActivityRequest();
request.setName("无效活动");
request.setStartTime(java.time.ZonedDateTime.now().plusDays(10));
request.setEndTime(java.time.ZonedDateTime.now());
com.mosquito.project.persistence.entity.ActivityEntity entity = new com.mosquito.project.persistence.entity.ActivityEntity();
entity.setId(1L);
when(activityRepository.findById(1L)).thenReturn(Optional.of(entity));
assertThrows(InvalidActivityDataException.class, () -> activityService.updateActivity(1L, request));
}
@Test
void getActivityById_shouldReturnActivity() {
com.mosquito.project.persistence.entity.ActivityEntity entity = new com.mosquito.project.persistence.entity.ActivityEntity();
entity.setId(1L);
entity.setName("测试活动");
entity.setStartTimeUtc(java.time.OffsetDateTime.now());
entity.setEndTimeUtc(java.time.OffsetDateTime.now().plusDays(7));
when(activityRepository.findById(1L)).thenReturn(Optional.of(entity));
Activity result = activityService.getActivityById(1L);
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals("测试活动", result.getName());
}
@Test
void getActivityById_shouldThrowWhenNotFound() {
when(activityRepository.findById(999L)).thenReturn(Optional.empty());
assertThrows(ActivityNotFoundException.class, () -> activityService.getActivityById(999L));
}
} }

View File

@@ -254,4 +254,62 @@ class ApiKeyEncryptionServiceTest {
// 加密后应该比原文长包含IV和tag // 加密后应该比原文长包含IV和tag
assertTrue(encrypted.length() > TEST_PLAIN_TEXT.length()); assertTrue(encrypted.length() > TEST_PLAIN_TEXT.length());
} }
@Test
@DisplayName("初始化 - 生产环境legacy默认密钥禁止")
void shouldFailInit_WhenLegacyDefaultKeyInProd() {
ApiKeyEncryptionService service = new ApiKeyEncryptionService();
ReflectionTestUtils.setField(service, "encryptionKey", "default-32-byte-key-for-dev-only!!");
MockEnvironment environment = new MockEnvironment();
environment.setActiveProfiles("prod");
ReflectionTestUtils.setField(service, "environment", environment);
IllegalStateException exception = assertThrows(IllegalStateException.class, service::init);
assertEquals("Encryption key must be set in production", exception.getMessage());
}
@Test
@DisplayName("初始化 - 生产环境空白密钥禁止")
void shouldFailInit_WhenBlankKeyInProd() {
ApiKeyEncryptionService service = new ApiKeyEncryptionService();
ReflectionTestUtils.setField(service, "encryptionKey", " ");
MockEnvironment environment = new MockEnvironment();
environment.setActiveProfiles("prod");
ReflectionTestUtils.setField(service, "environment", environment);
IllegalStateException exception = assertThrows(IllegalStateException.class, service::init);
assertEquals("Encryption key must be set in production", exception.getMessage());
}
@Test
@DisplayName("初始化 - 非生产环境无environment对象")
void shouldSucceedInit_WhenEnvironmentIsNull() {
ApiKeyEncryptionService service = new ApiKeyEncryptionService();
ReflectionTestUtils.setField(service, "encryptionKey", "default-32-byte-key-for-dev-only!");
ReflectionTestUtils.setField(service, "environment", null);
assertDoesNotThrow(() -> service.init());
// 应该能够正常加密解密
String encrypted = service.encrypt(TEST_PLAIN_TEXT);
String decrypted = service.decrypt(encrypted);
assertEquals(TEST_PLAIN_TEXT, decrypted);
}
@Test
@DisplayName("初始化 - 非生产环境允许默认密钥")
void shouldSucceedInit_WhenDefaultKeyInNonProd() {
ApiKeyEncryptionService service = new ApiKeyEncryptionService();
ReflectionTestUtils.setField(service, "encryptionKey", "default-32-byte-key-for-dev-only!");
MockEnvironment environment = new MockEnvironment();
environment.setActiveProfiles("dev");
ReflectionTestUtils.setField(service, "environment", environment);
assertDoesNotThrow(() -> service.init());
// 应该能够正常加密解密
String encrypted = service.encrypt(TEST_PLAIN_TEXT);
String decrypted = service.decrypt(encrypted);
assertEquals(TEST_PLAIN_TEXT, decrypted);
}
} }