package com.tcm.training.admin.app.auth.service;
|
|
import com.tcm.training.admin.app.auth.config.AppSmsAuthProperties;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.ValueOperations;
|
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
/**
|
* APP 短信验证码场景化调试配置测试。
|
*/
|
@ExtendWith(MockitoExtension.class)
|
class AppSmsCodeServiceTest {
|
|
@Mock
|
private StringRedisTemplate stringRedisTemplate;
|
|
@Mock
|
private ValueOperations<String, String> valueOperations;
|
|
@Mock
|
private AppSmsSender appSmsSender;
|
|
private AppSmsAuthProperties appSmsAuthProperties;
|
|
private AppSmsCodeService appSmsCodeService;
|
|
/**
|
* 每个用例前重新构造短信验证码服务。
|
*/
|
@BeforeEach
|
void setUp() {
|
appSmsAuthProperties = new AppSmsAuthProperties();
|
appSmsAuthProperties.getLogin().setEnabled(true);
|
appSmsAuthProperties.getLogin().setCode("666666");
|
appSmsAuthProperties.getRegister().setEnabled(false);
|
appSmsAuthProperties.getRegister().setCode("");
|
appSmsAuthProperties.getPasswordReset().setEnabled(false);
|
appSmsAuthProperties.getPasswordReset().setCode("");
|
appSmsAuthProperties.getPhoneChange().setEnabled(false);
|
appSmsAuthProperties.getPhoneChange().setCode("");
|
|
appSmsCodeService = new AppSmsCodeService(
|
stringRedisTemplate,
|
appSmsSender,
|
appSmsAuthProperties
|
);
|
}
|
|
/**
|
* 仅开启登录调试验证码时,666666 应允许短信登录直接通过。
|
*/
|
@Test
|
void validateLoginCodeShouldAllowConfiguredDebugCode() {
|
assertDoesNotThrow(() -> appSmsCodeService.validateLoginCode("13800138000", "666666"));
|
verify(stringRedisTemplate, never()).opsForValue();
|
}
|
|
/**
|
* 未开启注册调试验证码时,666666 不应绕过真实验证码校验。
|
*/
|
@Test
|
void validateRegisterCodeShouldRejectDebugCodeWhenSceneDisabled() {
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
when(valueOperations.get(anyString())).thenReturn(null);
|
|
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
() -> appSmsCodeService.validateRegisterCode("13800138000", "666666"));
|
|
assertEquals("验证码已失效,请重新获取", exception.getMessage());
|
}
|
|
/**
|
* 验证码校验成功后不应删除缓存,保证 5 分钟有效期内可重复提交。
|
*/
|
@Test
|
void validateLoginCodeShouldKeepCachedCodeAfterSuccess() {
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
when(valueOperations.get("app:sms:login:13800138000")).thenReturn("123456");
|
|
assertDoesNotThrow(() -> appSmsCodeService.validateLoginCode("13800138000", "123456"));
|
|
verify(stringRedisTemplate, never()).delete(anyString());
|
}
|
|
/**
|
* 同一验证码在缓存 TTL 内多次提交都应校验通过。
|
*/
|
@Test
|
void validateLoginCodeShouldAllowRepeatedSubmissionsWithinTtl() {
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
when(valueOperations.get("app:sms:login:13800138000")).thenReturn("123456");
|
|
assertDoesNotThrow(() -> appSmsCodeService.validateLoginCode("13800138000", "123456"));
|
assertDoesNotThrow(() -> appSmsCodeService.validateLoginCode("13800138000", "123456"));
|
|
verify(stringRedisTemplate, never()).delete(anyString());
|
}
|
|
/**
|
* 缓存中存在验证码但输入不一致时仍应提示验证码错误。
|
*/
|
@Test
|
void validateLoginCodeShouldRejectMismatchedCode() {
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
when(valueOperations.get("app:sms:login:13800138000")).thenReturn("123456");
|
|
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
() -> appSmsCodeService.validateLoginCode("13800138000", "654321"));
|
|
assertEquals("验证码错误", exception.getMessage());
|
verify(stringRedisTemplate, never()).delete(anyString());
|
}
|
|
/**
|
* 短信发送成功后才应写入验证码缓存。
|
*/
|
@Test
|
void createLoginCodeShouldCacheCodeAfterSendSucceeds() {
|
when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
|
|
appSmsCodeService.createLoginCode("13800138000");
|
|
verify(appSmsSender).sendLoginCode(eq("13800138000"), anyString());
|
verify(valueOperations).set(anyString(), anyString(), anyLong(), eq(java.util.concurrent.TimeUnit.SECONDS));
|
}
|
|
/**
|
* 短信发送失败时不应写入验证码缓存。
|
*/
|
@Test
|
void createLoginCodeShouldNotCacheCodeWhenSendFails() {
|
org.mockito.Mockito.doThrow(new IllegalStateException("短信发送失败,请稍后重试"))
|
.when(appSmsSender).sendLoginCode(eq("13800138000"), anyString());
|
|
IllegalStateException exception = assertThrows(IllegalStateException.class,
|
() -> appSmsCodeService.createLoginCode("13800138000"));
|
|
assertEquals("短信发送失败,请稍后重试", exception.getMessage());
|
verify(valueOperations, never()).set(anyString(), anyString(), anyLong(), eq(java.util.concurrent.TimeUnit.SECONDS));
|
}
|
}
|