再次调整项目结构

This commit is contained in:
YunaiV
2021-04-28 00:01:14 +08:00
parent 135acaf7ba
commit 533cd1d33d
696 changed files with 1353 additions and 1294 deletions

View File

@@ -0,0 +1,48 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.config.RedisTestConfiguration;
import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB + Redis 的单元测试
*
* 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
public class BaseDbAndRedisUnitTest {
@Import({
// DB 配置类
DataSourceConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
MybatisConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
// Redis 配置类
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
RedisAutoConfiguration.class, // Spring Redis 自动配置类
RedisConfig.class, // 自己的 Redis 配置类
RedissonAutoConfiguration.class, // Redisson 自动高配置类
})
public static class Application {
}
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB 的单元测试
*
* 注意Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
public class BaseDbUnitTest {
@Import({
// DB 配置类
DataSourceConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
MybatisConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
})
public static class Application {
}
}

View File

@@ -0,0 +1,13 @@
package cn.iocoder.dashboard;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* 纯 Mockito 的单元测试
*
* @author 芋道源码
*/
@ExtendWith(MockitoExtension.class)
public class BaseMockitoUnitTest {
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.config.RedisTestConfiguration;
import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
/**
* 依赖内存 Redis 的单元测试
*
* 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
public class BaseRedisUnitTest {
@Import({
// Redis 配置类
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
RedisAutoConfiguration.class, // Spring Redis 自动配置类
RedisConfig.class, // 自己的 Redis 配置类
RedissonAutoConfiguration.class, // Redisson 自动高配置类
})
public static class Application {
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.dashboard.config;
import org.mockito.Mockito;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzTestConfiguration {
@Bean
public Scheduler scheduler() {
return Mockito.mock(Scheduler.class);
}
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.dashboard.config;
import com.github.fppt.jedismock.RedisServer;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.io.IOException;
@Configuration(proxyBeanMethods = false)
@Lazy(false) // 禁止延迟加载
@EnableConfigurationProperties(RedisProperties.class)
public class RedisTestConfiguration {
/**
* 创建模拟的 Redis Server 服务器
*/
@Bean
public RedisServer redisServer(RedisProperties properties) throws IOException {
RedisServer redisServer = new RedisServer(properties.getPort());
// TODO 芋艿:一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样就导致端口被占用无法启动。。。
try {
redisServer.start();
} catch (Exception ignore) {}
return redisServer;
}
}

View File

@@ -0,0 +1 @@
package cn.iocoder.dashboard.framework;

View File

@@ -0,0 +1,224 @@
package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.dashboard.BaseMockitoUnitTest;
import cn.iocoder.dashboard.common.core.KeyValue;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.dashboard.util.collection.MapUtils;
import cn.iocoder.dashboard.util.date.DateUtils;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.List;
import java.util.function.Function;
import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.json.JsonUtils.toJsonString;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when;
/**
* {@link AliyunSmsClient} 的单元测试
*
* @author 芋道源码
*/
public class AliyunSmsClientTest extends BaseMockitoUnitTest {
private final SmsChannelProperties properties = new SmsChannelProperties()
.setApiKey(randomString()) // 随机一个 apiKey避免构建报错
.setApiSecret(randomString()) // 随机一个 apiSecret避免构建报错
.setSignature("芋道源码");
@InjectMocks
private final AliyunSmsClient smsClient = new AliyunSmsClient(properties);
@Mock
private IAcsClient client;
@Test
public void testDoInit() {
// 准备参数
// mock 方法
// 调用
smsClient.doInit();
// 断言
assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "acsClient"));
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() throws ClientException {
// 准备参数
Long sendLogId = randomLongId();
String mobile = randomString();
String apiTemplateId = randomString();
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
new KeyValue<>("code", 1234), new KeyValue<>("op", "login"));
// mock 方法
SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("OK"));
when(client.getAcsResponse(argThat((ArgumentMatcher<SendSmsRequest>) acsRequest -> {
assertEquals(mobile, acsRequest.getPhoneNumbers());
assertEquals(properties.getSignature(), acsRequest.getSignName());
assertEquals(apiTemplateId, acsRequest.getTemplateCode());
assertEquals(toJsonString(MapUtils.convertMap(templateParams)), acsRequest.getTemplateParam());
assertEquals(sendLogId.toString(), acsRequest.getOutId());
return true;
}))).thenReturn(response);
// 调用
SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
apiTemplateId, templateParams);
// 断言
assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getBizId(), result.getData().getSerialNo());
}
@Test
public void testDoTParseSmsReceiveStatus() throws Throwable {
// 准备参数
String text = "[\n" +
" {\n" +
" \"phone_number\" : \"13900000001\",\n" +
" \"send_time\" : \"2017-01-01 11:12:13\",\n" +
" \"report_time\" : \"2017-02-02 22:23:24\",\n" +
" \"success\" : true,\n" +
" \"err_code\" : \"DELIVERED\",\n" +
" \"err_msg\" : \"用户接收成功\",\n" +
" \"sms_size\" : \"1\",\n" +
" \"biz_id\" : \"12345\",\n" +
" \"out_id\" : \"67890\"\n" +
" }\n" +
"]";
// mock 方法
// 调用
List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
// 断言
assertEquals(1, statuses.size());
assertTrue(statuses.get(0).getSuccess());
assertEquals("DELIVERED", statuses.get(0).getErrorCode());
assertEquals("用户接收成功", statuses.get(0).getErrorMsg());
assertEquals("13900000001", statuses.get(0).getMobile());
assertEquals(DateUtils.buildTime(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime());
assertEquals("12345", statuses.get(0).getSerialNo());
assertEquals(67890L, statuses.get(0).getLogId());
}
@Test
public void testDoGetSmsTemplate() throws ClientException {
// 准备参数
String apiTemplateId = randomString();
// mock 方法
QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
o.setCode("OK");
o.setTemplateStatus(1); // 设置模板通过
});
when(client.getAcsResponse(argThat((ArgumentMatcher<QuerySmsTemplateRequest>) acsRequest -> {
assertEquals(apiTemplateId, acsRequest.getTemplateCode());
return true;
}))).thenReturn(response);
// 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId);
// 断言
assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getTemplateCode(), result.getData().getId());
assertEquals(response.getTemplateContent(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(response.getReason(), result.getData().getAuditReason());
}
@Test
public void testConvertSmsTemplateAuditStatus() {
assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
smsClient.convertSmsTemplateAuditStatus(0));
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
smsClient.convertSmsTemplateAuditStatus(1));
assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
smsClient.convertSmsTemplateAuditStatus(2));
assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3),
"未知审核状态(3)");
}
@Test
@SuppressWarnings("unchecked")
public void testInvoke_throwable() throws ClientException {
// 准备参数
QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
// mock 方法
ClientException ex = new ClientException("isv.INVALID_PARAMETERS", "参数不正确", randomString());
when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex);
// 调用,并断言异常
SmsCommonResult<?> result = smsClient.invoke(request,null);
// 断言
assertEquals(ex.getErrCode(), result.getApiCode());
assertEquals(ex.getErrMsg(), result.getApiMsg());
assertEquals(SMS_API_PARAM_ERROR.getCode(), result.getCode());
assertEquals(SMS_API_PARAM_ERROR.getMsg(), result.getMsg());
assertEquals(ex.getRequestId(), result.getApiRequestId());
}
@Test
public void testInvoke_success() throws ClientException {
// 准备参数
QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
Function<QuerySmsTemplateResponse, SmsTemplateRespDTO> responseConsumer = response -> {
SmsTemplateRespDTO data = new SmsTemplateRespDTO();
data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
data.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(response.getReason());
return data;
};
// mock 方法
QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
o.setCode("OK");
o.setTemplateStatus(1); // 设置模板通过
});
when(client.getAcsResponse(any(AcsRequest.class))).thenReturn(response);
// 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.invoke(request, responseConsumer);
// 断言
assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getTemplateCode(), result.getData().getId());
assertEquals(response.getTemplateContent(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(response.getReason(), result.getData().getAuditReason());
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
import cn.iocoder.dashboard.BaseMockitoUnitTest;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link AliyunSmsCodeMapping} 的单元测试
*
* @author 芋道源码
*/
public class AliyunSmsCodeMappingTest extends BaseMockitoUnitTest {
@InjectMocks
private AliyunSmsCodeMapping codeMapping;
@Test
public void testApply() {
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("OK"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("MissingAccessKeyId"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_NOT_EXISTS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_ABNORMAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("isv.DAY_LIMIT_CONTROL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("isv.SMS_CONTENT_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGN_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SIGN_NAME_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("isp.RAM_PERMISSION_DENY"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.OUT_OF_SERVICE"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.AMOUNT_NOT_ENOUGH"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("isv.SMS_TEMPLATE_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGNATURE_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_PARAMETERS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_JSON_PARAM"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("isv.MOBILE_NUMBER_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("isv.TEMPLATE_MISSING_PARAMETERS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("isv.BUSINESS_LIMIT_CONTROL"));
}
}

View File

@@ -0,0 +1,202 @@
package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.dashboard.BaseMockitoUnitTest;
import cn.iocoder.dashboard.common.core.KeyValue;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.dashboard.util.date.DateUtils;
import com.google.common.collect.Lists;
import com.yunpian.sdk.YunpianClient;
import com.yunpian.sdk.api.SmsApi;
import com.yunpian.sdk.api.TplApi;
import com.yunpian.sdk.constant.YunpianConstant;
import com.yunpian.sdk.model.Result;
import com.yunpian.sdk.model.SmsSingleSend;
import com.yunpian.sdk.model.Template;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static com.yunpian.sdk.constant.Code.OK;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* 对 {@link YunpianSmsClient} 的单元测试
*
* @author 芋道源码
*/
public class YunpianSmsClientTest extends BaseMockitoUnitTest {
private final SmsChannelProperties properties = new SmsChannelProperties()
.setApiKey(randomString()); // 随机一个 apiKey避免构建报错
@InjectMocks
private final YunpianSmsClient smsClient = new YunpianSmsClient(properties);
@Mock
private YunpianClient client;
@Test
public void testDoInit() {
// 准备参数
// mock 方法
// 调用
smsClient.doInit();
// 断言
assertNotEquals(client, ReflectUtil.getFieldValue(smsClient, "client"));
verify(client, times(1)).close();
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() throws Throwable {
// 准备参数
Long sendLogId = randomLongId();
String mobile = randomString();
String apiTemplateId = randomString();
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
new KeyValue<>("code", 1234), new KeyValue<>("op", "login"));
// mock sms 方法
SmsApi smsApi = mock(SmsApi.class);
when(client.sms()).thenReturn(smsApi);
// mock tpl_single_send 方法
Map<String, String> request = new HashMap<>();
request.put(YunpianConstant.MOBILE, mobile);
request.put(YunpianConstant.TPL_ID, apiTemplateId);
request.put(YunpianConstant.TPL_VALUE, "#code#=1234&#op#=login");
request.put(YunpianConstant.UID, String.valueOf(sendLogId));
request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl());
Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class,
o -> o.setCode(OK)); // API 发送成功的 code
when(smsApi.tpl_single_send(eq(request))).thenReturn(responseResult);
// 调用
SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
apiTemplateId, templateParams);
// 断言
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertNull(result.getApiRequestId());
// 断言结果
assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo());
}
@Test
public void testDoParseSmsReceiveStatus() throws Throwable {
// 准备参数
String text = "[{\"sid\":9527,\"uid\":1024,\"user_receive_time\":\"2014-03-17 22:55:21\",\"error_msg\":\"\",\"mobile\":\"15205201314\",\"report_status\":\"SUCCESS\"}]";
// mock 方法
// 调用
// 断言
// 调用
List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
// 断言
assertEquals(1, statuses.size());
assertTrue(statuses.get(0).getSuccess());
assertEquals("", statuses.get(0).getErrorCode());
assertNull(statuses.get(0).getErrorMsg());
assertEquals("15205201314", statuses.get(0).getMobile());
assertEquals(DateUtils.buildTime(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime());
assertEquals("9527", statuses.get(0).getSerialNo());
assertEquals(1024L, statuses.get(0).getLogId());
}
@Test
@SuppressWarnings("unchecked")
public void testDoGetSmsTemplate() throws Throwable {
// 准备参数
String apiTemplateId = randomString();
// mock tpl 方法
TplApi tplApi = mock(TplApi.class);
when(client.tpl()).thenReturn(tplApi);
// mock get 方法
Map<String, String> request = new HashMap<>();
request.put(YunpianConstant.APIKEY, properties.getApiKey());
request.put(YunpianConstant.TPL_ID, apiTemplateId);
Result<List<Template>> responseResult = randomPojo(Result.class, List.class, o -> {
o.setCode(OK); // API 发送成功的 code
o.setData(randomPojoList(Template.class, t -> t.setCheck_status("SUCCESS")));
});
when(tplApi.get(eq(request))).thenReturn(responseResult);
// 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId);
// 断言
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertNull(result.getApiRequestId());
// 断言结果
Template template = responseResult.getData().get(0);
assertEquals(template.getTpl_id().toString(), result.getData().getId());
assertEquals(template.getTpl_content(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(template.getReason(), result.getData().getAuditReason());
}
@Test
public void testConvertSmsTemplateAuditStatus() {
assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
smsClient.convertSmsTemplateAuditStatus("CHECKING"));
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
smsClient.convertSmsTemplateAuditStatus("SUCCESS"));
assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
smsClient.convertSmsTemplateAuditStatus("FAIL"));
assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("test"),
"未知审核状态(test)");
}
@Test
public void testInvoke_throwable() {
// 准备参数
Supplier<Result<Object>> requestConsumer =
() -> new Result<>().setThrowable(new NullPointerException());
// mock 方法
// 调用,并断言异常
assertThrows(NullPointerException.class,
() -> smsClient.invoke(requestConsumer, null));
}
@Test
@SuppressWarnings("unchecked")
public void testInvoke_success() throws Throwable {
// 准备参数
Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class, o -> o.setCode(OK));
Supplier<Result<SmsSingleSend>> requestConsumer = () -> responseResult;
Function<SmsSingleSend, SmsSendRespDTO> responseConsumer =
smsSingleSend -> new SmsSendRespDTO().setSerialNo(String.valueOf(responseResult.getData().getSid()));
// mock 方法
// 调用
SmsCommonResult<SmsSendRespDTO> result = smsClient.invoke(requestConsumer, responseConsumer);
// 断言
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertNull(result.getApiRequestId());
assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo());
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
import cn.iocoder.dashboard.BaseMockitoUnitTest;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import static com.yunpian.sdk.constant.Code.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link YunpianSmsCodeMapping} 的单元测试
*
* @author 芋道源码
*/
class YunpianSmsCodeMappingTest extends BaseMockitoUnitTest {
@InjectMocks
private YunpianSmsCodeMapping codeMapping;
@Test
public void testApply() {
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(String.valueOf(OK)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply(String.valueOf(ARGUMENT_MISSING)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply(String.valueOf(BAD_ARGUMENT_FORMAT)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply(String.valueOf(MONEY_NOT_ENOUGH)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_FOUND)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_VALID)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DUP_IN_SHORT_TIME)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(TOO_MANY_TIME_IN_5)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DAY_LIMIT_PER_MOBILE)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(HOUR_LIMIT_PER_MOBILE)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply(String.valueOf(BLACK_PHONE_FILTER)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_MATCH)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_VALID)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(BAD_SIGN_FORMAT)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply(String.valueOf(BAD_API_KEY)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply(String.valueOf(API_NOT_ALLOWED)));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply(String.valueOf(IP_NOT_ALLOWED)));
}
}

View File

@@ -0,0 +1,256 @@
package cn.iocoder.dashboard.modules.infra.service.config;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO;
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigExportReqVO;
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigPageReqVO;
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigUpdateReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper;
import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer;
import cn.iocoder.dashboard.modules.infra.service.config.impl.InfConfigServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* {@link InfConfigServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(InfConfigServiceImpl.class)
public class InfConfigServiceTest extends BaseDbUnitTest {
@Resource
private InfConfigServiceImpl configService;
@Resource
private InfConfigMapper configMapper;
@MockBean
private InfConfigProducer configProducer;
@Test
public void testCreateConfig_success() {
// 准备参数
InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class);
// 调用
Long configId = configService.createConfig(reqVO);
// 断言
assertNotNull(configId);
// 校验记录的属性是否正确
InfConfigDO config = configMapper.selectById(configId);
assertPojoEquals(reqVO, config);
assertEquals(InfConfigTypeEnum.CUSTOM.getType(), config.getType());
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testUpdateConfig_success() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO();
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class, o -> {
o.setId(dbConfig.getId()); // 设置更新的 ID
});
// 调用
configService.updateConfig(reqVO);
// 校验是否更新正确
InfConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, config);
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testDeleteConfig_success() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO(o -> {
o.setType(InfConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型
});
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbConfig.getId();
// 调用
configService.deleteConfig(id);
// 校验数据不存在了
assertNull(configMapper.selectById(id));
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testDeleteConfig_canNotDeleteSystemType() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO(o -> {
o.setType(InfConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除
});
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbConfig.getId();
// 调用, 并断言异常
assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);
}
@Test
public void testCheckConfigExists_success() {
// mock 数据
InfConfigDO dbConfigDO = randomInfConfigDO();
configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据
// 调用成功
configService.checkConfigExists(dbConfigDO.getId());
}
@Test
public void testCheckConfigExist_notExists() {
assertServiceException(() -> configService.checkConfigExists(randomLongId()), CONFIG_NOT_EXISTS);
}
@Test
public void testCheckConfigKeyUnique_success() {
// 调用,成功
configService.checkConfigKeyUnique(randomLongId(), randomString());
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForCreate() {
// 准备参数
String key = randomString();
// mock 数据
configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(null, key),
CONFIG_KEY_DUPLICATE);
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String key = randomString();
// mock 数据
configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(id, key),
CONFIG_KEY_DUPLICATE);
}
@Test
public void testGetConfigPage() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setKey("yunai");
o.setType(InfConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
configMapper.insert(dbConfig);
// 测试 name 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
InfConfigPageReqVO reqVO = new InfConfigPageReqVO();
reqVO.setName("");
reqVO.setKey("nai");
reqVO.setType(InfConfigTypeEnum.SYSTEM.getType());
reqVO.setBeginTime(buildTime(2021, 1, 15));
reqVO.setEndTime(buildTime(2021, 2, 15));
// 调用
PageResult<InfConfigDO> pageResult = configService.getConfigPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbConfig, pageResult.getList().get(0));
}
@Test
public void testGetConfigList() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setKey("yunai");
o.setType(InfConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
configMapper.insert(dbConfig);
// 测试 name 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
InfConfigExportReqVO reqVO = new InfConfigExportReqVO();
reqVO.setName("");
reqVO.setKey("nai");
reqVO.setType(InfConfigTypeEnum.SYSTEM.getType());
reqVO.setBeginTime(buildTime(2021, 1, 15));
reqVO.setEndTime(buildTime(2021, 2, 15));
// 调用
List<InfConfigDO> list = configService.getConfigList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbConfig, list.get(0));
}
@Test
public void testGetConfigByKey() {
// mock 数据
InfConfigDO dbConfig = randomInfConfigDO();
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
String key = dbConfig.getKey();
// 调用
InfConfigDO config = configService.getConfigByKey(key);
// 断言
assertNotNull(config);
assertPojoEquals(dbConfig, config);
}
// ========== 随机对象 ==========
@SafeVarargs
private static InfConfigDO randomInfConfigDO(Consumer<InfConfigDO>... consumers) {
Consumer<InfConfigDO> consumer = (o) -> {
o.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围
};
return randomPojo(InfConfigDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,126 @@
package cn.iocoder.dashboard.modules.infra.service.file;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.file.config.FileProperties;
import cn.iocoder.dashboard.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.file.InfFileMapper;
import cn.iocoder.dashboard.modules.infra.service.file.impl.InfFileServiceImpl;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
@Import({InfFileServiceImpl.class, FileProperties.class})
public class InfFileServiceTest extends BaseDbUnitTest {
@Resource
private InfFileServiceImpl fileService;
@Resource
private FileProperties fileProperties;
@Resource
private InfFileMapper fileMapper;
@Test
public void testCreateFile_success() {
// 准备参数
String path = randomString();
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用
String url = fileService.createFile(path, content);
// 断言
assertEquals(fileProperties.getBasePath() + path, url);
// 校验数据
InfFileDO file = fileMapper.selectById(path);
assertEquals(path, file.getId());
assertEquals("jpg", file.getType());
assertArrayEquals(content, file.getContent());
}
@Test
public void testCreateFile_exists() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);
// 准备参数
String path = dbFile.getId(); // 模拟已存在
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用,并断言异常
assertServiceException(() -> fileService.createFile(path, content), FILE_PATH_EXISTS);
}
@Test
public void testDeleteFile_success() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
// 准备参数
String id = dbFile.getId();
// 调用
fileService.deleteFile(id);
// 校验数据不存在了
assertNull(fileMapper.selectById(id));
}
@Test
public void testDeleteFile_notExists() {
// 准备参数
String id = randomString();
// 调用, 并断言异常
assertServiceException(() -> fileService.deleteFile(id), FILE_NOT_EXISTS);
}
@Test
public void testGetFilePage() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class, o -> { // 等会查询到
o.setId("yudao");
o.setType("jpg");
o.setCreateTime(buildTime(2021, 1, 15));
});
fileMapper.insert(dbFile);
// 测试 id 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> o.setId("tudou")));
// 测试 type 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> {
o.setId("yudao02");
o.setType("png");
}));
// 测试 createTime 不匹配
fileMapper.insert(ObjectUtils.clone(dbFile, o -> {
o.setId("yudao03");
o.setCreateTime(buildTime(2020, 1, 15));
}));
// 准备参数
InfFilePageReqVO reqVO = new InfFilePageReqVO();
reqVO.setId("yudao");
reqVO.setType("jp");
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
PageResult<InfFileDO> pageResult = fileService.getFilePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbFile, pageResult.getList().get(0), "content");
}
}

View File

@@ -0,0 +1,173 @@
package cn.iocoder.dashboard.modules.infra.service.job;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.log.InfJobLogExportReqVO;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.log.InfJobLogPageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobLogDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.job.InfJobLogMapper;
import cn.iocoder.dashboard.modules.infra.enums.job.InfJobLogStatusEnum;
import cn.iocoder.dashboard.modules.infra.service.job.impl.InfJobLogServiceImpl;
import cn.iocoder.dashboard.util.object.ObjectUtils;
/**
* {@link InfJobLogServiceImpl} 的单元测试
*
* @author neilz
*/
@Import(InfJobLogServiceImpl.class)
public class InfJobLogServiceTest extends BaseDbUnitTest {
@Resource
private InfJobLogServiceImpl jobLogService;
@Resource
private InfJobLogMapper jobLogMapper;
@Test
public void testCreateJobLog_success() {
// 准备参数
InfJobLogDO reqVO = randomPojo(InfJobLogDO.class, o -> {
o.setExecuteIndex(1);
});
// 调用
Long jobLogId = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex());
// 断言
assertNotNull(jobLogId);
// 校验记录的属性是否正确
InfJobLogDO job = jobLogMapper.selectById(jobLogId);
assertEquals(InfJobLogStatusEnum.RUNNING.getStatus(), job.getStatus());
}
@Test
public void testUpdateJobLogResultAsync_success() {
// 准备参数
InfJobLogDO reqVO = randomPojo(InfJobLogDO.class, o -> {
o.setExecuteIndex(1);
});
InfJobLogDO log = InfJobLogDO.builder().jobId(reqVO.getJobId()).handlerName(reqVO.getHandlerName()).handlerParam(reqVO.getHandlerParam()).executeIndex(reqVO.getExecuteIndex())
.beginTime(reqVO.getBeginTime()).status(InfJobLogStatusEnum.RUNNING.getStatus()).build();
jobLogMapper.insert(log);
// 调用
jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), true,reqVO.getResult());
// 校验记录的属性是否正确
InfJobLogDO job = jobLogMapper.selectById(log.getId());
assertEquals(InfJobLogStatusEnum.SUCCESS.getStatus(), job.getStatus());
// 调用
jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), false,reqVO.getResult());
// 校验记录的属性是否正确
InfJobLogDO job2 = jobLogMapper.selectById(log.getId());
assertEquals(InfJobLogStatusEnum.FAILURE.getStatus(), job2.getStatus());
}
@Test
public void testGetJobLogListByIds_success() {
// mock 数据
InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setStatus(randomEle(InfJobLogStatusEnum.values()).getStatus()); // 保证 status 的范围
});
InfJobLogDO cloneJobLog = ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString()));
jobLogMapper.insert(dbJobLog);
// 测试 handlerName 不匹配
jobLogMapper.insert(cloneJobLog);
// 准备参数
ArrayList ids = new ArrayList<>();
ids.add(dbJobLog.getId());
ids.add(cloneJobLog.getId());
// 调用
List<InfJobLogDO> list = jobLogService.getJobLogList(ids);
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbJobLog, list.get(0));
}
@Test
public void testGetJobPage_success() {
// mock 数据
InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setHandlerName("handlerName 单元测试");
o.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
o.setBeginTime(buildTime(2021, 1, 8));
o.setEndTime(buildTime(2021, 1, 8));
});
jobLogMapper.insert(dbJobLog);
// 测试 jobId 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setJobId(randomLongId())));
// 测试 handlerName 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString())));
// 测试 beginTime 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
// 测试 endTime 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
// 测试 status 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setStatus(InfJobLogStatusEnum.FAILURE.getStatus())));
// 准备参数
InfJobLogPageReqVO reqVo = new InfJobLogPageReqVO();
reqVo.setJobId(dbJobLog.getJobId());
reqVo.setHandlerName("单元");
reqVo.setBeginTime(dbJobLog.getBeginTime());
reqVo.setEndTime(dbJobLog.getEndTime());
reqVo.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
// 调用
PageResult<InfJobLogDO> pageResult = jobLogService.getJobLogPage(reqVo);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbJobLog, pageResult.getList().get(0));
}
@Test
public void testGetJobListForExport_success() {
// mock 数据
InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setHandlerName("handlerName 单元测试");
o.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
o.setBeginTime(buildTime(2021, 1, 8));
o.setEndTime(buildTime(2021, 1, 8));
});
jobLogMapper.insert(dbJobLog);
// 测试 jobId 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setJobId(randomLongId())));
// 测试 handlerName 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString())));
// 测试 beginTime 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
// 测试 endTime 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
// 测试 status 不匹配
jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setStatus(InfJobLogStatusEnum.FAILURE.getStatus())));
// 准备参数
InfJobLogExportReqVO reqVo = new InfJobLogExportReqVO();
reqVo.setJobId(dbJobLog.getJobId());
reqVo.setHandlerName("单元");
reqVo.setBeginTime(dbJobLog.getBeginTime());
reqVo.setEndTime(dbJobLog.getEndTime());
reqVo.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
// 调用
List<InfJobLogDO> list = jobLogService.getJobLogList(reqVo);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbJobLog, list.get(0));
}
}

View File

@@ -0,0 +1,309 @@
package cn.iocoder.dashboard.modules.infra.service.job;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CHANGE_STATUS_EQUALS;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CHANGE_STATUS_INVALID;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CRON_EXPRESSION_VALID;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_HANDLER_EXISTS;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_NOT_EXISTS;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_UPDATE_ONLY_NORMAL_STATUS;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.quartz.SchedulerException;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.quartz.core.scheduler.SchedulerManager;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobCreateReqVO;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExportReqVO;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobUpdateReqVO;
import cn.iocoder.dashboard.modules.infra.convert.job.InfJobConvert;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.job.InfJobMapper;
import cn.iocoder.dashboard.modules.infra.enums.job.InfJobStatusEnum;
import cn.iocoder.dashboard.modules.infra.service.job.impl.InfJobServiceImpl;
import cn.iocoder.dashboard.util.object.ObjectUtils;
/**
* {@link InfJobServiceImpl} 的单元测试
*
* @author neilz
*/
@Import(InfJobServiceImpl.class)
public class InfJobServiceTest extends BaseDbUnitTest {
@Resource
private InfJobServiceImpl jobService;
@Resource
private InfJobMapper jobMapper;
@MockBean
private SchedulerManager schedulerManager;
@Test
public void testCreateJob_cronExpressionValid() {
// 准备参数。Cron 表达式为 String 类型,默认随机字符串。
InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class);
// 调用,并断言异常
assertServiceException(() -> jobService.createJob(reqVO), JOB_CRON_EXPRESSION_VALID);
}
@Test
public void testCreateJob_jobHandlerExists() throws SchedulerException {
// 准备参数 指定 Cron 表达式
InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用
jobService.createJob(reqVO);
// 调用,并断言异常
assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS);
}
@Test
public void testCreateJob_success() throws SchedulerException {
// 准备参数 指定 Cron 表达式
InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用
Long jobId = jobService.createJob(reqVO);
// 断言
assertNotNull(jobId);
// 校验记录的属性是否正确
InfJobDO job = jobMapper.selectById(jobId);
assertPojoEquals(reqVO, job);
assertEquals(InfJobStatusEnum.NORMAL.getStatus(), job.getStatus());
// 校验调用
verify(schedulerManager, times(1)).addJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()), eq(job.getCronExpression()),
eq(reqVO.getRetryCount()), eq(reqVO.getRetryInterval()));
}
@Test
public void testUpdateJob_jobNotExists(){
// 准备参数
InfJobUpdateReqVO reqVO = randomPojo(InfJobUpdateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用,并断言异常
assertServiceException(() -> jobService.updateJob(reqVO), JOB_NOT_EXISTS);
}
@Test
public void testUpdateJob_onlyNormalStatus(){
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.INIT.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 准备参数
InfJobUpdateReqVO updateReqVO = randomPojo(InfJobUpdateReqVO.class, o -> {
o.setId(job.getId());
o.setName(createReqVO.getName());
o.setCronExpression(createReqVO.getCronExpression());
});
// 调用,并断言异常
assertServiceException(() -> jobService.updateJob(updateReqVO), JOB_UPDATE_ONLY_NORMAL_STATUS);
}
@Test
public void testUpdateJob_success() throws SchedulerException {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 准备参数
InfJobUpdateReqVO updateReqVO = randomPojo(InfJobUpdateReqVO.class, o -> {
o.setId(job.getId());
o.setName(createReqVO.getName());
o.setCronExpression(createReqVO.getCronExpression());
});
// 调用
jobService.updateJob(updateReqVO);
// 校验记录的属性是否正确
InfJobDO updateJob = jobMapper.selectById(updateReqVO.getId());
assertPojoEquals(updateReqVO, updateJob);
// 校验调用
verify(schedulerManager, times(1)).updateJob(eq(job.getHandlerName()), eq(updateReqVO.getHandlerParam()), eq(updateReqVO.getCronExpression()),
eq(updateReqVO.getRetryCount()), eq(updateReqVO.getRetryInterval()));
}
@Test
public void testUpdateJobStatus_changeStatusInvalid() {
// 调用,并断言异常
assertServiceException(() -> jobService.updateJobStatus(1l, InfJobStatusEnum.INIT.getStatus()), JOB_CHANGE_STATUS_INVALID);
}
@Test
public void testUpdateJobStatus_changeStatusEquals() {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用,并断言异常
assertServiceException(() -> jobService.updateJobStatus(job.getId(), job.getStatus()), JOB_CHANGE_STATUS_EQUALS);
}
@Test
public void testUpdateJobStatus_NormalToStop_success() throws SchedulerException {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.updateJobStatus(job.getId(), InfJobStatusEnum.STOP.getStatus());
// 校验记录的属性是否正确
InfJobDO updateJob = jobMapper.selectById(job.getId());
assertEquals(InfJobStatusEnum.STOP.getStatus(), updateJob.getStatus());
// 校验调用
verify(schedulerManager, times(1)).pauseJob(eq(job.getHandlerName()));
}
@Test
public void testUpdateJobStatus_StopToNormal_success() throws SchedulerException {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.STOP.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.updateJobStatus(job.getId(), InfJobStatusEnum.NORMAL.getStatus());
// 校验记录的属性是否正确
InfJobDO updateJob = jobMapper.selectById(job.getId());
assertEquals(InfJobStatusEnum.NORMAL.getStatus(), updateJob.getStatus());
// 校验调用
verify(schedulerManager, times(1)).resumeJob(eq(job.getHandlerName()));
}
@Test
public void testTriggerJob_success() throws SchedulerException {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.triggerJob(job.getId());
// 校验调用
verify(schedulerManager, times(1)).triggerJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()));
}
@Test
public void testDeleteJob_success() throws SchedulerException {
// mock 数据
InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用 UPDATE inf_job SET deleted=1 WHERE id=? AND deleted=0
jobService.deleteJob(job.getId());
// 校验数据不存在了 WHERE id=? AND deleted=0 查询为空正常
assertNull(jobMapper.selectById(job.getId()));
// 校验调用
verify(schedulerManager, times(1)).deleteJob(eq(job.getHandlerName()));
}
@Test
public void testGetJobListByIds_success() {
// mock 数据
InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
o.setStatus(randomEle(InfJobStatusEnum.values()).getStatus()); // 保证 status 的范围
});
InfJobDO cloneJob = ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString()));
jobMapper.insert(dbJob);
// 测试 handlerName 不匹配
jobMapper.insert(cloneJob);
// 准备参数
ArrayList ids = new ArrayList<>();
ids.add(dbJob.getId());
ids.add(cloneJob.getId());
// 调用
List<InfJobDO> list = jobService.getJobList(ids);
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbJob, list.get(0));
}
@Test
public void testGetJobPage_success() {
// mock 数据
InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
o.setName("定时任务测试");
o.setHandlerName("handlerName 单元测试");
o.setStatus(InfJobStatusEnum.INIT.getStatus());
});
jobMapper.insert(dbJob);
// 测试 name 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setName("土豆")));
// 测试 status 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setStatus(InfJobStatusEnum.NORMAL.getStatus())));
// 测试 handlerName 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString())));
// 准备参数
InfJobPageReqVO reqVo = new InfJobPageReqVO();
reqVo.setName("定时");
reqVo.setStatus(InfJobStatusEnum.INIT.getStatus());
reqVo.setHandlerName("单元");
// 调用
PageResult<InfJobDO> pageResult = jobService.getJobPage(reqVo);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbJob, pageResult.getList().get(0));
}
@Test
public void testGetJobListForExport_success() {
// mock 数据
InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
o.setName("定时任务测试");
o.setHandlerName("handlerName 单元测试");
o.setStatus(InfJobStatusEnum.INIT.getStatus());
});
jobMapper.insert(dbJob);
// 测试 name 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setName("土豆")));
// 测试 status 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setStatus(InfJobStatusEnum.NORMAL.getStatus())));
// 测试 handlerName 不匹配
jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString())));
// 准备参数
InfJobExportReqVO reqVo = new InfJobExportReqVO();
reqVo.setName("定时");
reqVo.setStatus(InfJobStatusEnum.INIT.getStatus());
reqVo.setHandlerName("单元");
// 调用
List<InfJobDO> list = jobService.getJobList(reqVo);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbJob, list.get(0));
}
private static void fillJobMonitorTimeoutEmpty(InfJobDO job) {
if (job.getMonitorTimeout() == null) {
job.setMonitorTimeout(0);
}
}
}

View File

@@ -0,0 +1,176 @@
package cn.iocoder.dashboard.modules.infra.service.logger;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.UserTypeEnum;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO;
import cn.iocoder.dashboard.modules.infra.controller.logger.vo.apiaccesslog.InfApiAccessLogExportReqVO;
import cn.iocoder.dashboard.modules.infra.controller.logger.vo.apiaccesslog.InfApiAccessLogPageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.logger.InfApiAccessLogDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.logger.InfApiAccessLogMapper;
import cn.iocoder.dashboard.modules.infra.service.logger.impl.InfApiAccessLogServiceImpl;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* {@link InfApiAccessLogServiceImpl} 单元测试
*/
@Import(InfApiAccessLogServiceImpl.class)
public class InfApiAccessLogServiceImplTest extends BaseDbUnitTest {
@Resource
private InfApiAccessLogService infApiAccessLogServiceImpl;
@Resource
private InfApiAccessLogMapper infApiAccessLogMapper;
@Test
public void testCreateApiAccessLogAsync() throws Exception {
ApiAccessLogCreateDTO createDTO = RandomUtils.randomPojo(
ApiAccessLogCreateDTO.class,
dto -> dto.setUserType(RandomUtil.randomEle(UserTypeEnum.values()).getValue())
);
// 执行service方法
Future<Boolean> future = infApiAccessLogServiceImpl.createApiAccessLogAsync(createDTO);
// 等异步执行完
future.get();
InfApiAccessLogDO infApiAccessLogDO = infApiAccessLogMapper.selectOne(null);
// 断言
assertNotNull(infApiAccessLogDO);
// 断言,忽略基本字段
assertPojoEquals(createDTO, infApiAccessLogDO);
}
@Test
public void testGetApiAccessLogPage() {
// 构造测试数据
long userId = 2233L;
int userType = UserTypeEnum.ADMIN.getValue();
String applicationName = "yudao-test";
String requestUrl = "foo";
Date beginTime = buildTime(2021, 3, 13);
int duration = 1000;
int resultCode = GlobalErrorCodeConstants.SUCCESS.getCode();
InfApiAccessLogDO infApiAccessLogDO = RandomUtils.randomPojo(InfApiAccessLogDO.class, dto -> {
dto.setUserId(userId);
dto.setUserType(userType);
dto.setApplicationName(applicationName);
dto.setRequestUrl(requestUrl);
dto.setBeginTime(beginTime);
dto.setDuration(duration);
dto.setResultCode(resultCode);
});
infApiAccessLogMapper.insert(infApiAccessLogDO);
// 下面几个都是不匹配的数据
// userId 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setUserId(3344L)));
// userType
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setUserType(UserTypeEnum.MEMBER.getValue())));
// applicationName 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setApplicationName("test")));
// requestUrl 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setRequestUrl("bar")));
// 构造一个早期时间 2021-02-06 00:00:00
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setBeginTime(buildTime(2021, 2, 6))));
// duration 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setDuration(100)));
// resultCode 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setResultCode(2)));
// 构造调用参数
InfApiAccessLogPageReqVO reqVO = new InfApiAccessLogPageReqVO();
reqVO.setUserId(userId);
reqVO.setUserType(userType);
reqVO.setApplicationName(applicationName);
reqVO.setRequestUrl(requestUrl);
reqVO.setBeginBeginTime(buildTime(2021, 3, 12));
reqVO.setEndBeginTime(buildTime(2021, 3, 14));
reqVO.setDuration(duration);
reqVO.setResultCode(resultCode);
// 调用service方法
PageResult<InfApiAccessLogDO> pageResult = infApiAccessLogServiceImpl.getApiAccessLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(infApiAccessLogDO, pageResult.getList().get(0));
}
@Test
public void testGetApiAccessLogList() {
// 构造测试数据
long userId = 2233L;
int userType = UserTypeEnum.ADMIN.getValue();
String applicationName = "yudao-test";
String requestUrl = "foo";
Date beginTime = buildTime(2021, 3, 13);
int duration = 1000;
int resultCode = GlobalErrorCodeConstants.SUCCESS.getCode();
InfApiAccessLogDO infApiAccessLogDO = RandomUtils.randomPojo(InfApiAccessLogDO.class, dto -> {
dto.setUserId(userId);
dto.setUserType(userType);
dto.setApplicationName(applicationName);
dto.setRequestUrl(requestUrl);
dto.setBeginTime(beginTime);
dto.setDuration(duration);
dto.setResultCode(resultCode);
});
infApiAccessLogMapper.insert(infApiAccessLogDO);
// 下面几个都是不匹配的数据
// userId 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setUserId(3344L)));
// userType
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setUserType(UserTypeEnum.MEMBER.getValue())));
// applicationName 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setApplicationName("test")));
// requestUrl 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setRequestUrl("bar")));
// 构造一个早期时间 2021-02-06 00:00:00
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setBeginTime(buildTime(2021, 2, 6))));
// duration 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setDuration(100)));
// resultCode 不同的
infApiAccessLogMapper.insert(ObjectUtils.clone(infApiAccessLogDO, logDO -> logDO.setResultCode(2)));
// 构造调用参数
InfApiAccessLogExportReqVO reqVO = new InfApiAccessLogExportReqVO();
reqVO.setUserId(userId);
reqVO.setUserType(userType);
reqVO.setApplicationName(applicationName);
reqVO.setRequestUrl(requestUrl);
reqVO.setBeginBeginTime(buildTime(2021, 3, 12));
reqVO.setEndBeginTime(buildTime(2021, 3, 14));
reqVO.setDuration(duration);
reqVO.setResultCode(resultCode);
// 调用service方法
List<InfApiAccessLogDO> list = infApiAccessLogServiceImpl.getApiAccessLogList(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, list.size());
assertPojoEquals(infApiAccessLogDO, list.get(0));
}
}

View File

@@ -0,0 +1,207 @@
package cn.iocoder.dashboard.modules.infra.service.logger;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.UserTypeEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiErrorLogCreateDTO;
import cn.iocoder.dashboard.modules.infra.controller.logger.vo.apierrorlog.InfApiErrorLogExportReqVO;
import cn.iocoder.dashboard.modules.infra.controller.logger.vo.apierrorlog.InfApiErrorLogPageReqVO;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.logger.InfApiErrorLogDO;
import cn.iocoder.dashboard.modules.infra.dal.mysql.logger.InfApiErrorLogMapper;
import cn.iocoder.dashboard.modules.infra.enums.logger.InfApiErrorLogProcessStatusEnum;
import cn.iocoder.dashboard.modules.infra.service.logger.impl.InfApiErrorLogServiceImpl;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.API_ERROR_LOG_NOT_FOUND;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.API_ERROR_LOG_PROCESSED;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* {@link InfApiErrorLogServiceImpl} 单元测试
*/
@Import(InfApiErrorLogServiceImpl.class)
public class InfApiErrorLogServiceImplTest extends BaseDbUnitTest {
@Resource
private InfApiErrorLogService infApiErrorLogServiceImpl;
@Resource
private InfApiErrorLogMapper infApiErrorLogMapper;
@Test
public void testCreateApiErrorLogAsync() throws Exception {
ApiErrorLogCreateDTO createDTO = RandomUtils.randomPojo(
ApiErrorLogCreateDTO.class,
dto -> dto.setUserType(RandomUtil.randomEle(UserTypeEnum.values()).getValue())
);
// 执行service方法
Future<Boolean> future = infApiErrorLogServiceImpl.createApiErrorLogAsync(createDTO);
// 等异步执行完
future.get();
InfApiErrorLogDO infApiErrorLogDO = infApiErrorLogMapper.selectOne(null);
// 断言
assertNotNull(infApiErrorLogDO);
// 断言,忽略基本字段
assertPojoEquals(createDTO, infApiErrorLogDO);
}
@Test
public void testGetApiErrorLogPage() {
// 构造测试数据
long userId = 2233L;
int userType = UserTypeEnum.ADMIN.getValue();
String applicationName = "yudao-test";
String requestUrl = "foo";
Date beginTime = buildTime(2021, 3, 13);
int progressStatus = InfApiErrorLogProcessStatusEnum.INIT.getStatus();
InfApiErrorLogDO infApiErrorLogDO = RandomUtils.randomPojo(InfApiErrorLogDO.class, logDO -> {
logDO.setUserId(userId);
logDO.setUserType(userType);
logDO.setApplicationName(applicationName);
logDO.setRequestUrl(requestUrl);
logDO.setExceptionTime(beginTime);
logDO.setProcessStatus(progressStatus);
});
infApiErrorLogMapper.insert(infApiErrorLogDO);
// 下面几个都是不匹配的数据
// userId 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setUserId(3344L)));
// userType
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setUserType(UserTypeEnum.MEMBER.getValue())));
// applicationName 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setApplicationName("test")));
// requestUrl 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setRequestUrl("bar")));
// 构造一个早期时间 2021-02-06 00:00:00
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setExceptionTime(buildTime(2021, 2, 6))));
// progressStatus 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setProcessStatus(InfApiErrorLogProcessStatusEnum.DONE.getStatus())));
// 构造调用参数
InfApiErrorLogPageReqVO reqVO = new InfApiErrorLogPageReqVO();
reqVO.setUserId(userId);
reqVO.setUserType(userType);
reqVO.setApplicationName(applicationName);
reqVO.setRequestUrl(requestUrl);
reqVO.setBeginExceptionTime(buildTime(2021, 3, 12));
reqVO.setEndExceptionTime(buildTime(2021, 3, 14));
reqVO.setProcessStatus(progressStatus);
// 调用service方法
PageResult<InfApiErrorLogDO> pageResult = infApiErrorLogServiceImpl.getApiErrorLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(infApiErrorLogDO, pageResult.getList().get(0));
}
@Test
public void testGetApiErrorLogList() {
// 构造测试数据
long userId = 2233L;
int userType = UserTypeEnum.ADMIN.getValue();
String applicationName = "yudao-test";
String requestUrl = "foo";
Date beginTime = buildTime(2021, 3, 13);
int progressStatus = InfApiErrorLogProcessStatusEnum.INIT.getStatus();
InfApiErrorLogDO infApiErrorLogDO = RandomUtils.randomPojo(InfApiErrorLogDO.class, logDO -> {
logDO.setUserId(userId);
logDO.setUserType(userType);
logDO.setApplicationName(applicationName);
logDO.setRequestUrl(requestUrl);
logDO.setExceptionTime(beginTime);
logDO.setProcessStatus(progressStatus);
});
infApiErrorLogMapper.insert(infApiErrorLogDO);
// 下面几个都是不匹配的数据
// userId 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setUserId(3344L)));
// userType
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setUserType(UserTypeEnum.MEMBER.getValue())));
// applicationName 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setApplicationName("test")));
// requestUrl 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setRequestUrl("bar")));
// 构造一个早期时间 2021-02-06 00:00:00
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setExceptionTime(buildTime(2021, 2, 6))));
// progressStatus 不同的
infApiErrorLogMapper.insert(ObjectUtils.clone(infApiErrorLogDO, logDO -> logDO.setProcessStatus(InfApiErrorLogProcessStatusEnum.DONE.getStatus())));
// 构造调用参数
InfApiErrorLogExportReqVO reqVO = new InfApiErrorLogExportReqVO();
reqVO.setUserId(userId);
reqVO.setUserType(userType);
reqVO.setApplicationName(applicationName);
reqVO.setRequestUrl(requestUrl);
reqVO.setBeginExceptionTime(buildTime(2021, 3, 12));
reqVO.setEndExceptionTime(buildTime(2021, 3, 14));
reqVO.setProcessStatus(progressStatus);
// 调用service方法
List<InfApiErrorLogDO> list = infApiErrorLogServiceImpl.getApiErrorLogList(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, list.size());
assertPojoEquals(infApiErrorLogDO, list.get(0));
}
@Test
public void testUpdateApiErrorLogProcess() {
// 先构造两条数据第一条用于抛出异常第二条用于正常的执行update操作
Long processUserId = 2233L;
InfApiErrorLogDO first = RandomUtils.randomPojo(InfApiErrorLogDO.class, logDO -> {
logDO.setProcessUserId(processUserId);
logDO.setUserType(UserTypeEnum.ADMIN.getValue());
logDO.setProcessStatus(InfApiErrorLogProcessStatusEnum.DONE.getStatus());
});
infApiErrorLogMapper.insert(first);
InfApiErrorLogDO second = RandomUtils.randomPojo(InfApiErrorLogDO.class, logDO -> {
logDO.setProcessUserId(1122L);
logDO.setUserType(UserTypeEnum.ADMIN.getValue());
logDO.setProcessStatus(InfApiErrorLogProcessStatusEnum.INIT.getStatus());
});
infApiErrorLogMapper.insert(second);
Long firstId = first.getId();
Long secondId = second.getId();
// 执行正常的 update 操作
infApiErrorLogServiceImpl.updateApiErrorLogProcess(secondId, InfApiErrorLogProcessStatusEnum.DONE.getStatus(), processUserId);
InfApiErrorLogDO secondSelect = infApiErrorLogMapper.selectOne("id", secondId);
// id 为 0 查询不到,应该抛出异常 API_ERROR_LOG_NOT_FOUND
assertServiceException(() -> infApiErrorLogServiceImpl.updateApiErrorLogProcess(0L, InfApiErrorLogProcessStatusEnum.DONE.getStatus(), processUserId), API_ERROR_LOG_NOT_FOUND);
// id 为 first 的 progressStatus 为 DONE ,应该抛出 API_ERROR_LOG_PROCESSED
assertServiceException(() -> infApiErrorLogServiceImpl.updateApiErrorLogProcess(firstId, InfApiErrorLogProcessStatusEnum.DONE.getStatus(), processUserId), API_ERROR_LOG_PROCESSED);
// 验证 progressStatus 是否修改成功
assertEquals(InfApiErrorLogProcessStatusEnum.DONE.getStatus(), secondSelect.getProcessStatus());
// 验证 progressUserId 是否修改成功
assertEquals(processUserId, secondSelect.getProcessUserId());
}
}

View File

@@ -0,0 +1,269 @@
package cn.iocoder.dashboard.modules.system.service.auth;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.framework.security.core.LoginUser;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl;
import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
import cn.iocoder.dashboard.util.AssertUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import java.util.Set;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysAuthServiceImpl} 的单元测试
*
* @author 芋道源码
*/
@Import(SysAuthServiceImpl.class)
public class SysAuthServiceImplTest extends BaseDbUnitTest {
@Resource
private SysAuthServiceImpl authService;
@MockBean
private SysUserService userService;
@MockBean
private SysPermissionService permissionService;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private Authentication authentication;
@MockBean
private SysCaptchaService captchaService;
@MockBean
private SysLoginLogService loginLogService;
@MockBean
private SysUserSessionService userSessionService;
@Test
public void testLoadUserByUsername_success() {
// 准备参数
String username = randomString();
// mock 方法
SysUserDO user = randomUserDO(o -> o.setUsername(username));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// 调用
LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
// 校验
AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
assertNull(loginUser.getRoleIds()); // 此时不会加载角色,所以是空的
}
@Test
public void testLoadUserByUsername_userNotFound() {
// 准备参数
String username = randomString();
// mock 方法
// 调用, 并断言异常
assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
() -> authService.loadUserByUsername(username),
username); // 异常提示为 username
}
@Test
public void testMockLogin_success() {
// 准备参数
Long userId = randomLongId();
// mock 方法 01
SysUserDO user = randomUserDO(o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user);
// mock 方法 02
Set<Long> roleIds = randomSet(Long.class);
when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus()))))
.thenReturn(roleIds);
// 调用
LoginUser loginUser = authService.mockLogin(userId);
// 断言
AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
assertEquals(roleIds, loginUser.getRoleIds());
}
@Test
public void testMockLogin_userNotFound() {
// 准备参数
Long userId = randomLongId();
// mock 方法
// 调用, 并断言异常
assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
() -> authService.mockLogin(userId),
String.valueOf(userId)); // 异常提示为 userId
}
@Test
public void testLogin_captchaNotFound() {
// 准备参数
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
String userIp = randomString();
String userAgent = randomString();
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
// 校验调用参数
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
);
}
@Test
public void testLogin_captchaCodeError() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
String code = randomString();
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
// mock 验证码不正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
// 校验调用参数
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testLogin_badCredentials() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new BadCredentialsException("测试账号或密码不正确"));
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.BAD_CREDENTIALS.getResult()))
);
}
@Test
public void testLogin_userDisabled() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new DisabledException("测试用户被禁用"));
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.USER_DISABLED.getResult()))
);
}
@Test
public void testLogin_unknownError() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new AuthenticationException("测试未知异常") {});
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.UNKNOWN_ERROR.getResult()))
);
}
@Test
public void testLogin_success() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
Long userId = randomLongId();
Set<Long> userRoleIds = randomSet(Long.class);
String sessionId = randomString();
SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
LoginUser loginUser = randomPojo(LoginUser.class, o -> {
o.setId(userId);
o.setRoleIds(userRoleIds);
});
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock authentication
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(loginUser);
// mock 获得 User 拥有的角色编号数组
when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds);
// mock 缓存登陆用户到 Redis
when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(sessionId);
// 调用, 并断言异常
String login = authService.login(reqVO, userIp, userAgent);
assertEquals(sessionId, login);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(SysLoginResultEnum.SUCCESS.getResult()))
);
}
@Test
public void testLogout_success() {
// 准备参数
String token = randomString();
LoginUser loginUser = randomPojo(LoginUser.class);
// mock
when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
// 调用
authService.logout(token);
// 校验调用参数
verify(userSessionService, times(1)).deleteUserSession(token);
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGOUT_SELF.getType())
&& o.getResult().equals(SysLoginResultEnum.SUCCESS.getResult()))
);
}
}

View File

@@ -0,0 +1,227 @@
package cn.iocoder.dashboard.modules.system.service.auth;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.RandomUtils.randomDate;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static cn.iocoder.dashboard.util.date.DateUtils.addTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.dashboard.BaseDbAndRedisUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
import cn.iocoder.dashboard.framework.security.core.LoginUser;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper;
import cn.iocoder.dashboard.modules.system.dal.redis.auth.SysLoginUserRedisDAO;
import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum;
import cn.iocoder.dashboard.modules.system.service.auth.impl.SysUserSessionServiceImpl;
import cn.iocoder.dashboard.modules.system.service.dept.impl.SysDeptServiceImpl;
import cn.iocoder.dashboard.modules.system.service.logger.impl.SysLoginLogServiceImpl;
import cn.iocoder.dashboard.modules.system.service.user.SysUserServiceImpl;
import cn.iocoder.dashboard.util.AssertUtils;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
/**
* SysUserSessionServiceImpl Tester.
*
* @author Lyon
* @version 1.0
* @since <pre>3月 8, 2021</pre>
*/
@Import({SysUserSessionServiceImpl.class, SysLoginUserRedisDAO.class})
public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private SysUserSessionServiceImpl sysUserSessionService;
@Resource
private SysUserSessionMapper sysUserSessionMapper;
@Resource
private SysLoginUserRedisDAO sysLoginUserRedisDAO;
@Resource
private SysUserMapper sysUserMapper;
@MockBean
private SecurityProperties securityProperties;
@MockBean
private SysDeptServiceImpl sysDeptService;
@MockBean
private SysUserServiceImpl sysUserService;
@MockBean
private SysLoginLogServiceImpl sysLoginLogService;
@Test
public void testCreateUserSession_success() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
LoginUser loginUser = randomPojo(LoginUser.class);
// mock
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// 调用
String sessionId = sysUserSessionService.createUserSession(loginUser, userIp, userAgent);
// 校验记录的属性是否正确
SysUserSessionDO sysUserSessionDO = sysUserSessionMapper.selectById(sessionId);
assertEquals(sysUserSessionDO.getId(), sessionId);
assertEquals(sysUserSessionDO.getUserId(), loginUser.getId());
assertEquals(sysUserSessionDO.getUserIp(), userIp);
assertEquals(sysUserSessionDO.getUserAgent(), userAgent);
assertEquals(sysUserSessionDO.getUsername(), loginUser.getUsername());
LoginUser redisLoginUser = sysLoginUserRedisDAO.get(sessionId);
AssertUtils.assertPojoEquals(redisLoginUser, loginUser, "username","password");
}
@Test
public void testCreateRefreshUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
Long timeLong = randomLongId();
String userName = randomString();
Date date = randomDate();
LoginUser loginUser = randomPojo(LoginUser.class);
// mock
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
loginUser.setUpdateTime(date);
sysLoginUserRedisDAO.set(sessionId, loginUser);
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).username(userName)
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
sysUserSessionMapper.insert(userSession);
SysUserSessionDO insertDO = sysUserSessionMapper.selectById(sessionId);
// 调用
sysUserSessionService.refreshUserSession(sessionId, loginUser);
// 校验记录 redis
LoginUser redisLoginUser = sysLoginUserRedisDAO.get(sessionId);
assertNotEquals(redisLoginUser.getUpdateTime(), date);
// 校验记录 SysUserSessionDO
SysUserSessionDO updateDO = sysUserSessionMapper.selectById(sessionId);
assertEquals(updateDO.getUsername(), loginUser.getUsername());
assertNotEquals(updateDO.getUpdateTime(), insertDO.getUpdateTime());
assertNotEquals(updateDO.getSessionTimeout(), addTime(Duration.ofMillis(timeLong)));
}
@Test
public void testDeleteUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
Long timeLong = randomLongId();
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 存入 Redis
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
sysLoginUserRedisDAO.set(sessionId, loginUser);
// mock 存入 db
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
sysUserSessionMapper.insert(userSession);
// 校验数据存在
assertNotNull(sysLoginUserRedisDAO.get(sessionId));
assertNotNull(sysUserSessionMapper.selectById(sessionId));
// 调用
sysUserSessionService.deleteUserSession(sessionId);
// 校验数据不存在了
assertNull(sysLoginUserRedisDAO.get(sessionId));
assertNull(sysUserSessionMapper.selectById(sessionId));
}
@Test
public void testGetUserSessionPage_success() {
// mock 数据
String userIp = randomString();
SysUserDO dbUser1 = randomPojo(SysUserDO.class, o -> {
o.setUsername("testUsername1");
o.setSex(randomEle(SysSexEnum.values()).getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
SysUserDO dbUser2 = randomPojo(SysUserDO.class, o -> {
o.setUsername("testUsername2");
o.setSex(randomEle(SysSexEnum.values()).getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
SysUserSessionDO dbSession = randomPojo(SysUserSessionDO.class, o -> {
o.setUserId(dbUser1.getId());
o.setUserIp(userIp);
});
sysUserMapper.insert(dbUser1);
sysUserMapper.insert(dbUser2);
sysUserSessionMapper.insert(dbSession);
sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
o.setId(randomString());
o.setUserId(dbUser2.getId());
}));
// 测试 userId 不匹配
sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
o.setId(randomString());
o.setUserId(123456l);
}));
// 测试 userIp 不匹配
sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
o.setId(randomString());
o.setUserIp("testUserIp");
}));
// 准备参数
SysUserSessionPageReqVO reqVo = new SysUserSessionPageReqVO();
reqVo.setUserIp(userIp);
// 调用
PageResult<SysUserSessionDO> pageResult = sysUserSessionService.getUserSessionPage(reqVo);
// 断言
assertEquals(3, pageResult.getTotal());
assertEquals(3, pageResult.getList().size());
assertPojoEquals(dbSession, pageResult.getList().get(0));
}
@Test
public void testClearSessionTimeout_success() throws Exception {
// 准备超时数据 120 条, 在线用户 1 条
int expectedTimeoutCount = 120, expectedTotal = 1;
// 准备数据
List<SysUserSessionDO> prepareData = Stream
.iterate(0, i -> i)
.limit(expectedTimeoutCount)
.map(i -> RandomUtils.randomPojo(SysUserSessionDO.class, o -> o.setSessionTimeout(DateUtil.offsetSecond(new Date(), -1))))
.collect(Collectors.toList());
SysUserSessionDO sessionDO = RandomUtils.randomPojo(SysUserSessionDO.class, o -> o.setSessionTimeout(DateUtil.offsetMinute(new Date(), 30)));
prepareData.add(sessionDO);
prepareData.forEach(sysUserSessionMapper::insert);
//清空超时数据
long actualTimeoutCount = sysUserSessionService.clearSessionTimeout();
//校验
assertEquals(expectedTimeoutCount, actualTimeoutCount);
List<SysUserSessionDO> userSessionDOS = sysUserSessionMapper.selectList();
assertEquals(expectedTotal, userSessionDOS.size());
AssertUtils.assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime");
}
}

View File

@@ -0,0 +1,66 @@
package cn.iocoder.dashboard.modules.system.service.common;
import cn.iocoder.dashboard.BaseRedisUnitTest;
import cn.iocoder.dashboard.framework.captcha.config.CaptchaProperties;
import cn.iocoder.dashboard.modules.system.controller.common.vo.SysCaptchaImageRespVO;
import cn.iocoder.dashboard.modules.system.dal.redis.common.SysCaptchaRedisDAO;
import cn.iocoder.dashboard.modules.system.service.common.impl.SysCaptchaServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.dashboard.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
@Import({SysCaptchaServiceImpl.class, CaptchaProperties.class, SysCaptchaRedisDAO.class})
public class SysCaptchaServiceTest extends BaseRedisUnitTest {
@Resource
private SysCaptchaServiceImpl captchaService;
@Resource
private SysCaptchaRedisDAO captchaRedisDAO;
@Resource
private CaptchaProperties captchaProperties;
@Test
public void testGetCaptchaImage() {
// 调用
SysCaptchaImageRespVO respVO = captchaService.getCaptchaImage();
// 断言
assertNotNull(respVO.getUuid());
assertNotNull(respVO.getImg());
String captchaCode = captchaRedisDAO.get(respVO.getUuid());
assertNotNull(captchaCode);
}
@Test
public void testGetCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
String resultCode = captchaService.getCaptchaCode(uuid);
// 断言
assertEquals(code, resultCode);
}
@Test
public void testDeleteCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
captchaService.deleteCaptchaCode(uuid);
// 断言
assertNull(captchaRedisDAO.get(uuid));
}
}

View File

@@ -0,0 +1,274 @@
package cn.iocoder.dashboard.modules.system.service.dept;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptListReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dept.SysDeptMapper;
import cn.iocoder.dashboard.modules.system.enums.dept.DeptIdEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.dept.SysDeptProducer;
import cn.iocoder.dashboard.modules.system.service.dept.impl.SysDeptServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.Multimap;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* {@link SysDeptServiceImpl} 的单元测试类
*
* @author niudehua
*/
@Import(SysDeptServiceImpl.class)
class SysDeptServiceTest extends BaseDbUnitTest {
@Resource
private SysDeptServiceImpl deptService;
@Resource
private SysDeptMapper deptMapper;
@MockBean
private SysDeptProducer deptProducer;
@Test
@SuppressWarnings("unchecked")
void testInitLocalCache() {
// mock 数据
SysDeptDO deptDO1 = randomDeptDO();
deptMapper.insert(deptDO1);
SysDeptDO deptDO2 = randomDeptDO();
deptMapper.insert(deptDO2);
// 调用
deptService.initLocalCache();
// 断言 deptCache 缓存
Map<Long, SysDeptDO> deptCache = (Map<Long, SysDeptDO>) getFieldValue(deptService, "deptCache");
assertEquals(2, deptCache.size());
assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId()));
assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId()));
// 断言 parentDeptCache 缓存
Multimap<Long, SysDeptDO> parentDeptCache = (Multimap<Long, SysDeptDO>) getFieldValue(deptService, "parentDeptCache");
assertEquals(2, parentDeptCache.size());
assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId()));
assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(deptService, "maxUpdateTime");
assertEquals(ObjectUtils.max(deptDO1.getUpdateTime(), deptDO2.getUpdateTime()), maxUpdateTime);
}
@Test
void testListDepts() {
// mock 数据
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { // 等会查询到
o.setName("开发部");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
deptMapper.insert(dept);
// 测试 name 不匹配
deptMapper.insert(ObjectUtils.clone(dept, o -> o.setName("")));
// 测试 status 不匹配
deptMapper.insert(ObjectUtils.clone(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SysDeptListReqVO reqVO = new SysDeptListReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<SysDeptDO> sysDeptDOS = deptService.getSimpleDepts(reqVO);
// 断言
assertEquals(1, sysDeptDOS.size());
assertPojoEquals(dept, sysDeptDOS.get(0));
}
@Test
void testCreateDept_success() {
// 准备参数
SysDeptCreateReqVO reqVO = randomPojo(SysDeptCreateReqVO.class,
o -> {
o.setParentId(DeptIdEnum.ROOT.getId());
o.setStatus(randomCommonStatus());
});
// 调用
Long deptId = deptService.createDept(reqVO);
// 断言
assertNotNull(deptId);
// 校验记录的属性是否正确
SysDeptDO deptDO = deptMapper.selectById(deptId);
assertPojoEquals(reqVO, deptDO);
// 校验调用
verify(deptProducer, times(1)).sendDeptRefreshMessage();
}
@Test
void testUpdateDept_success() {
// mock 数据
SysDeptDO dbDeptDO = randomPojo(SysDeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDeptUpdateReqVO reqVO = randomPojo(SysDeptUpdateReqVO.class, o -> {
// 设置更新的 ID
o.setParentId(DeptIdEnum.ROOT.getId());
o.setId(dbDeptDO.getId());
o.setStatus(randomCommonStatus());
});
// 调用
deptService.updateDept(reqVO);
// 校验是否更新正确
SysDeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, deptDO);
}
@Test
void testDeleteDept_success() {
// mock 数据
SysDeptDO dbDeptDO = randomPojo(SysDeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDeptDO.getId();
// 调用
deptService.deleteDept(id);
// 校验数据不存在了
assertNull(deptMapper.selectById(id));
}
@Test
void testCheckDept_nameDuplicateForUpdate() {
// mock 数据
SysDeptDO deptDO = randomDeptDO();
// 设置根节点部门
deptDO.setParentId(DeptIdEnum.ROOT.getId());
deptMapper.insert(deptDO);
// mock 数据 稍后模拟重复它的 name
SysDeptDO nameDeptDO = randomDeptDO();
// 设置根节点部门
nameDeptDO.setParentId(DeptIdEnum.ROOT.getId());
deptMapper.insert(nameDeptDO);
// 准备参数
SysDeptUpdateReqVO reqVO = randomPojo(SysDeptUpdateReqVO.class,
o -> {
// 设置根节点部门
o.setParentId(DeptIdEnum.ROOT.getId());
// 设置更新的 ID
o.setId(deptDO.getId());
// 模拟 name 重复
o.setName(nameDeptDO.getName());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_NAME_DUPLICATE);
}
@Test
void testCheckDept_parentNotExitsForCreate() {
SysDeptCreateReqVO reqVO = randomPojo(SysDeptCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// 调用,并断言异常
assertServiceException(() -> deptService.createDept(reqVO), DEPT_PARENT_NOT_EXITS);
}
@Test
void testCheckDept_notFoundForDelete() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDept(id), DEPT_NOT_FOUND);
}
@Test
void testCheckDept_exitsChildrenForDelete() {
// mock 数据
SysDeptDO parentDept = randomPojo(SysDeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDeptDO childrenDeptDO = randomPojo(SysDeptDO.class, o -> {
o.setParentId(parentDept.getId());
o.setStatus(randomCommonStatus());
});
// 插入子部门
deptMapper.insert(childrenDeptDO);
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);
}
@Test
void testCheckDept_parentErrorForUpdate() {
// mock 数据
SysDeptDO dbDeptDO = randomPojo(SysDeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);
// 准备参数
SysDeptUpdateReqVO reqVO = randomPojo(SysDeptUpdateReqVO.class,
o -> {
// 设置自己为父部门
o.setParentId(dbDeptDO.getId());
// 设置更新的 ID
o.setId(dbDeptDO.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_ERROR);
}
@Test
void testCheckDept_notEnableForCreate() {
// mock 数据
SysDeptDO deptDO = randomPojo(SysDeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
deptMapper.insert(deptDO);
// 准备参数
SysDeptCreateReqVO reqVO = randomPojo(SysDeptCreateReqVO.class,
o -> {
// 设置未启用的部门为副部门
o.setParentId(deptDO.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.createDept(reqVO), DEPT_NOT_ENABLE);
}
@Test
void testCheckDept_parentIsChildForUpdate() {
// mock 数据
SysDeptDO parentDept = randomPojo(SysDeptDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
deptMapper.insert(parentDept);
SysDeptDO childDept = randomPojo(SysDeptDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setParentId(parentDept.getId());
});
deptMapper.insert(childDept);
// 初始化本地缓存
deptService.initLocalCache();
// 准备参数
SysDeptUpdateReqVO reqVO = randomPojo(SysDeptUpdateReqVO.class,
o -> {
// 设置自己的子部门为父部门
o.setParentId(childDept.getId());
// 设置更新的 ID
o.setId(parentDept.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_IS_CHILD);
}
@SafeVarargs
private static SysDeptDO randomDeptDO(Consumer<SysDeptDO>... consumers) {
Consumer<SysDeptDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(SysDeptDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,200 @@
package cn.iocoder.dashboard.modules.system.service.dept;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dept.SysPostMapper;
import cn.iocoder.dashboard.modules.system.service.dept.impl.SysPostServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SysPostServiceImpl} 的单元测试类
*
* @author niudehua
*/
@Import(SysPostServiceImpl.class)
class SysPostServiceTest extends BaseDbUnitTest {
@Resource
private SysPostServiceImpl postService;
@Resource
private SysPostMapper postMapper;
@Test
void testPagePosts() {
// mock 数据
SysPostDO postDO = randomPojo(SysPostDO.class, o -> {
o.setName("码仔");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
postMapper.insert(postDO);
// 测试 name 不匹配
postMapper.insert(ObjectUtils.clone(postDO, o -> o.setName("程序员")));
// 测试 status 不匹配
postMapper.insert(ObjectUtils.clone(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SysPostPageReqVO reqVO = new SysPostPageReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<SysPostDO> pageResult = postService.getPostPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(postDO, pageResult.getList().get(0));
}
@Test
void testListPosts() {
// mock 数据
SysPostDO postDO = randomPojo(SysPostDO.class, o -> {
o.setName("码仔");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
postMapper.insert(postDO);
// 测试 name 不匹配
postMapper.insert(ObjectUtils.clone(postDO, o -> o.setName("程序员")));
// 测试 status 不匹配
postMapper.insert(ObjectUtils.clone(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SysPostExportReqVO reqVO = new SysPostExportReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<SysPostDO> list = postService.getPosts(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(postDO, list.get(0));
}
@Test
void testGetPost() {
// mock 数据
SysPostDO dbPostDO = randomPostDO();
postMapper.insert(dbPostDO);
// 准备参数
Long id = dbPostDO.getId();
// 调用
SysPostDO post = postService.getPost(id);
// 断言
assertNotNull(post);
assertPojoEquals(dbPostDO, post);
}
@Test
void testCreatePost_success() {
// 准备参数
SysPostCreateReqVO reqVO = randomPojo(SysPostCreateReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()));
// 调用
Long postId = postService.createPost(reqVO);
// 断言
assertNotNull(postId);
// 校验记录的属性是否正确
SysPostDO post = postMapper.selectById(postId);
assertPojoEquals(reqVO, post);
}
@Test
void testUpdatePost_success() {
// mock 数据
SysPostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
SysPostUpdateReqVO reqVO = randomPojo(SysPostUpdateReqVO.class,
o -> {
// 设置更新的 ID
o.setId(postDO.getId());
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
postService.updatePost(reqVO);
// 校验是否更新正确
SysPostDO post = postMapper.selectById(reqVO.getId());// 获取最新的
assertPojoEquals(reqVO, post);
}
@Test
void testDeletePost_success() {
// mock 数据
SysPostDO postDO = randomPostDO();
postMapper.insert(postDO);
// 准备参数
Long id = postDO.getId();
// 调用
postService.deletePost(id);
assertNull(postMapper.selectById(id));
}
@Test
void testCheckPost_notFoundForDelete() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND);
}
@Test
void testCheckPost_nameDuplicateForCreate() {
// mock 数据
SysPostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
SysPostCreateReqVO reqVO = randomPojo(SysPostCreateReqVO.class,
// 模拟 name 重复
o -> o.setName(postDO.getName()));
assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE);
}
@Test
void testCheckPost_codeDuplicateForUpdate() {
// mock 数据
SysPostDO postDO = randomPostDO();
postMapper.insert(postDO);
// mock 数据 稍后模拟重复它的 code
SysPostDO codePostDO = randomPostDO();
postMapper.insert(codePostDO);
// 准备参数
SysPostUpdateReqVO reqVO = randomPojo(SysPostUpdateReqVO.class,
o -> {
// 设置更新的 ID
o.setId(postDO.getId());
// 模拟 code 重复
o.setCode(codePostDO.getCode());
});
// 调用, 并断言异常
assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE);
}
@SafeVarargs
private static SysPostDO randomPostDO(Consumer<SysPostDO>... consumers) {
Consumer<SysPostDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(SysPostDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,302 @@
package cn.iocoder.dashboard.modules.system.service.dict;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictDataDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper;
import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer;
import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.ImmutableTable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysDictDataServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysDictDataServiceImpl.class)
public class SysDictDataServiceTest extends BaseDbUnitTest {
@Resource
private SysDictDataServiceImpl dictDataService;
@Resource
private SysDictDataMapper dictDataMapper;
@MockBean
private SysDictTypeService dictTypeService;
@MockBean
private SysDictDataProducer dictDataProducer;
/**
* 测试加载到新的字典数据的情况
*/
@Test
@SuppressWarnings("unchecked")
public void testInitLocalCache() {
// mock 数据
SysDictDataDO dictData01 = randomDictDataDO();
dictDataMapper.insert(dictData01);
SysDictDataDO dictData02 = randomDictDataDO();
dictDataMapper.insert(dictData02);
// 调用
dictDataService.initLocalCache();
// 断言 labelDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> labelDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "labelDictDataCache");
assertEquals(2, labelDictDataCache.size());
assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel()));
assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel()));
// 断言 valueDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> valueDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "valueDictDataCache");
assertEquals(2, valueDictDataCache.size());
assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue()));
assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime");
assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime);
}
@Test
public void testGetDictDataPage() {
// mock 数据
SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class, o -> { // 等会查询到
o.setLabel("芋艿");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SysDictDataPageReqVO reqVO = new SysDictDataPageReqVO();
reqVO.setLabel("");
reqVO.setDictType("yu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<SysDictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictData, pageResult.getList().get(0));
}
@Test
public void testGetDictDataList() {
// mock 数据
SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class, o -> { // 等会查询到
o.setLabel("芋艿");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SysDictDataExportReqVO reqVO = new SysDictDataExportReqVO();
reqVO.setLabel("");
reqVO.setDictType("yu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<SysDictDataDO> list = dictDataService.getDictDatas(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbDictData, list.get(0));
}
@Test
public void testCreateDictData_success() {
// 准备参数
SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
Long dictDataId = dictDataService.createDictData(reqVO);
// 断言
assertNotNull(dictDataId);
// 校验记录的属性是否正确
SysDictDataDO dictData = dictDataMapper.selectById(dictDataId);
assertPojoEquals(reqVO, dictData);
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testUpdateDictData_success() {
// mock 数据
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> {
o.setId(dbDictData.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// mock 方法,字典类型
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
dictDataService.updateDictData(reqVO);
// 校验是否更新正确
SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictData);
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testDeleteDictData_success() {
// mock 数据
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictData.getId();
// 调用
dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testCheckDictDataExists_success() {
// mock 数据
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 调用成功
dictDataService.checkDictDataExists(dbDictData.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_success() {
// mock 方法,数据类型被禁用
String type = randomString();
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
// 调用, 成功
dictDataService.checkDictTypeValid(type);
}
@Test
public void testCheckDictTypeValid_notExists() {
assertServiceException(() -> dictDataService.checkDictTypeValid(randomString()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_notEnable() {
// mock 方法,数据类型被禁用
String dictType = randomString();
when(dictTypeService.getDictType(eq(dictType))).thenReturn(
randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.checkDictTypeValid(dictType), DICT_TYPE_NOT_ENABLE);
}
@Test
public void testCheckDictDataValueUnique_success() {
// 调用,成功
dictDataService.checkDictDataValueUnique(randomLongId(), randomString(), randomString());
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForCreate() {
// 准备参数
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(null, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(id, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) {
Consumer<SysDictDataDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers));
}
/**
* 生成一个有效的字典类型
*
* @param type 字典类型
* @return SysDictTypeDO 对象
*/
private static SysDictTypeDO randomDictTypeDO(String type) {
return randomPojo(SysDictTypeDO.class, o -> {
o.setType(type);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
});
}
}

View File

@@ -0,0 +1,279 @@
package cn.iocoder.dashboard.modules.system.service.dict;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictTypeMapper;
import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictTypeServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link SysDictTypeServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysDictTypeServiceImpl.class)
public class SysDictTypeServiceTest extends BaseDbUnitTest {
@Resource
private SysDictTypeServiceImpl dictTypeService;
@Resource
private SysDictTypeMapper dictTypeMapper;
@MockBean
private SysDictDataService dictDataService;
@Test
public void testGetDictTypePage() {
// mock 数据
SysDictTypeDO dbDictType = randomPojo(SysDictTypeDO.class, o -> { // 等会查询到
o.setName("yunai");
o.setType("芋艿");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2021, 1, 15));
});
dictTypeMapper.insert(dbDictType);
// 测试 name 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setName("tudou")));
// 测试 type 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setType("土豆")));
// 测试 status 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
SysDictTypePageReqVO reqVO = new SysDictTypePageReqVO();
reqVO.setName("nai");
reqVO.setType("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
PageResult<SysDictTypeDO> pageResult = dictTypeService.getDictTypePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictType, pageResult.getList().get(0));
}
@Test
public void testGetDictTypeList() {
// mock 数据
SysDictTypeDO dbDictType = randomPojo(SysDictTypeDO.class, o -> { // 等会查询到
o.setName("yunai");
o.setType("芋艿");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2021, 1, 15));
});
dictTypeMapper.insert(dbDictType);
// 测试 name 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setName("tudou")));
// 测试 type 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setType("土豆")));
// 测试 status 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
SysDictTypeExportReqVO reqVO = new SysDictTypeExportReqVO();
reqVO.setName("nai");
reqVO.setType("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
List<SysDictTypeDO> list = dictTypeService.getDictTypeList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbDictType, list.get(0));
}
@Test
public void testGetDictType() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);
// 准备参数
String type = dbDictType.getType();
// 调用
SysDictTypeDO dictType = dictTypeService.getDictType(type);
// 断言
assertNotNull(dictType);
assertPojoEquals(dbDictType, dictType);
}
@Test
public void testCreateDictType_success() {
// 准备参数
SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()));
// 调用
Long dictTypeId = dictTypeService.createDictType(reqVO);
// 断言
assertNotNull(dictTypeId);
// 校验记录的属性是否正确
SysDictTypeDO dictType = dictTypeMapper.selectById(dictTypeId);
assertPojoEquals(reqVO, dictType);
}
@Test
public void testUpdateDictType_success() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class, o -> {
o.setId(dbDictType.getId()); // 设置更新的 ID
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
dictTypeService.updateDictType(reqVO);
// 校验是否更新正确
SysDictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictType);
}
@Test
public void testDeleteDictType_success() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// 调用
dictTypeService.deleteDictType(id);
// 校验数据不存在了
assertNull(dictTypeMapper.selectById(id));
}
@Test
public void testDeleteDictType_hasChildren() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// mock 方法
when(dictDataService.countByDictType(eq(dbDictType.getType()))).thenReturn(1);
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN);
}
@Test
public void testCheckDictDataExists_success() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 调用成功
dictTypeService.checkDictTypeExists(dbDictType.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictTypeService.checkDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForCreate() {
// 准备参数
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(null, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(id, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypNameUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeNameUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForCreate() {
// 准备参数
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name),
DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name),
DICT_TYPE_NAME_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysDictTypeDO randomDictTypeDO(Consumer<SysDictTypeDO>... consumers) {
Consumer<SysDictTypeDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(SysDictTypeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,305 @@
package cn.iocoder.dashboard.modules.system.service.errorcode;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.errorcode.SysErrorCodeMapper;
import cn.iocoder.dashboard.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
import cn.iocoder.dashboard.modules.system.service.errorcode.impl.SysErrorCodeServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.slf4j.Logger;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.ERROR_CODE_DUPLICATE;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.ERROR_CODE_NOT_EXISTS;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SysErrorCodeServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysErrorCodeServiceImpl.class)
public class SysErrorCodeServiceTest extends BaseDbUnitTest {
@Resource
private SysErrorCodeServiceImpl errorCodeService;
@Resource
private SysErrorCodeMapper errorCodeMapper;
@Mock
private Logger log;
@Test
public void testCreateErrorCode_success() {
// 准备参数
SysErrorCodeCreateReqVO reqVO = randomPojo(SysErrorCodeCreateReqVO.class);
// 调用
Long errorCodeId = errorCodeService.createErrorCode(reqVO);
// 断言
assertNotNull(errorCodeId);
// 校验记录的属性是否正确
SysErrorCodeDO errorCode = errorCodeMapper.selectById(errorCodeId);
assertPojoEquals(reqVO, errorCode);
assertEquals(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testUpdateErrorCode_success() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
SysErrorCodeUpdateReqVO reqVO = randomPojo(SysErrorCodeUpdateReqVO.class, o -> {
o.setId(dbErrorCode.getId()); // 设置更新的 ID
});
// 调用
errorCodeService.updateErrorCode(reqVO);
// 校验是否更新正确
SysErrorCodeDO errorCode = errorCodeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, errorCode);
assertEquals(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testDeleteErrorCode_success() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbErrorCode.getId();
// 调用
errorCodeService.deleteErrorCode(id);
// 校验数据不存在了
assertNull(errorCodeMapper.selectById(id));
}
@Test
public void testGetErrorCodePage() {
// mock 数据
SysErrorCodeDO dbErrorCode = initGetErrorCodePage();
// 准备参数
SysErrorCodePageReqVO reqVO = new SysErrorCodePageReqVO();
reqVO.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
reqVO.setApplicationName("yudao");
reqVO.setCode(1);
reqVO.setMessage("yu");
reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
reqVO.setEndCreateTime(buildTime(2020, 11, 30));
// 调用
PageResult<SysErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbErrorCode, pageResult.getList().get(0));
}
/**
* 初始化 getErrorCodePage 方法的测试数据
*/
private SysErrorCodeDO initGetErrorCodePage() {
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> { // 等会查询到
o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
o.setApplicationName("yudaoyuanma");
o.setCode(1);
o.setMessage("yudao");
o.setCreateTime(buildTime(2020, 11, 11));
});
errorCodeMapper.insert(dbErrorCode);
// 测试 type 不匹配
errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType())));
// 测试 applicationName 不匹配
errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setApplicationName("yunai")));
// 测试 code 不匹配
errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setCode(2)));
// 测试 message 不匹配
errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setMessage("nai")));
// 测试 createTime 不匹配
errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setCreateTime(buildTime(2020, 12, 12))));
return dbErrorCode;
}
@Test
public void testGetErrorCodeList() {
// mock 数据
SysErrorCodeDO dbErrorCode = initGetErrorCodePage();
// 准备参数
SysErrorCodeExportReqVO reqVO = new SysErrorCodeExportReqVO();
reqVO.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
reqVO.setApplicationName("yudao");
reqVO.setCode(1);
reqVO.setMessage("yu");
reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
reqVO.setEndCreateTime(buildTime(2020, 11, 30));
// 调用
List<SysErrorCodeDO> list = errorCodeService.getErrorCodeList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbErrorCode, list.get(0));
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForCreate() {
// 准备参数
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, null),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, id),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateErrorCodeExists_notExists() {
assertServiceException(() -> errorCodeService.validateErrorCodeExists(null),
ERROR_CODE_NOT_EXISTS);
}
/**
* 情况 1错误码不存在的情况
*/
@Test
public void testAutoGenerateErrorCodes_01() {
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class);
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言
SysErrorCodeDO errorCode = errorCodeMapper.selectOne(null);
assertPojoEquals(generateReqDTO, errorCode);
assertEquals(SysErrorCodeTypeEnum.AUTO_GENERATION.getType(), errorCode.getType());
}
/**
* 情况 2.1,错误码存在,但是是 SysErrorCodeTypeEnum.MANUAL_OPERATION 类型
*/
@Test
public void testAutoGenerateErrorCodes_021() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.2,错误码存在,但是是 applicationName 不匹配
*/
@Test
public void testAutoGenerateErrorCodes_022() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(randomString()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3,错误码存在,但是是 message 相同
*/
@Test
public void testAutoGenerateErrorCodes_023() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName())
.setMessage(dbErrorCode.getMessage()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3,错误码存在,但是是 message 不同,则进行更新
*/
@Test
public void testAutoGenerateErrorCodes_024() {
// mock 数据
SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,匹配
SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(generateReqDTO, errorCode);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysErrorCodeDO randomInfErrorCodeDO(Consumer<SysErrorCodeDO>... consumers) {
Consumer<SysErrorCodeDO> consumer = (o) -> {
o.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围
};
return randomPojo(SysErrorCodeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,151 @@
package cn.iocoder.dashboard.modules.system.service.logger;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogPageReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.logger.SysLoginLogDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.logger.SysLoginLogMapper;
import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.dashboard.modules.system.service.logger.impl.SysLoginLogServiceImpl;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Import(SysLoginLogServiceImpl.class)
public class SysLoginLogServiceImplTest extends BaseDbUnitTest {
@Resource
private SysLoginLogServiceImpl sysLoginLogService;
@Resource
private SysLoginLogMapper loginLogMapper;
@Test
public void testCreateLoginLog() {
String traceId = TracerUtils.getTraceId();
SysLoginLogCreateReqVO reqVO = RandomUtils.randomPojo(SysLoginLogCreateReqVO.class, vo -> {
// 指定随机的范围,避免超出范围入库失败
vo.setLogType(RandomUtil.randomEle(SysLoginLogTypeEnum.values()).getType());
vo.setResult(RandomUtil.randomEle(SysLoginResultEnum.values()).getResult());
// 使用TracerUtils生成的TraceId
vo.setTraceId(traceId);
});
// 执行service方法
sysLoginLogService.createLoginLog(reqVO);
// 断言,忽略基本字段
SysLoginLogDO sysLoginLogDO = loginLogMapper.selectOne(null);
assertPojoEquals(reqVO, sysLoginLogDO);
}
@Test
public void testGetLoginLogPage() {
// 构造测试数据
// 登录成功的
SysLoginLogDO loginLogDO = RandomUtils.randomPojo(SysLoginLogDO.class, logDO -> {
logDO.setLogType(RandomUtil.randomEle(SysLoginLogTypeEnum.values()).getType());
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setUserIp("192.168.199.16");
logDO.setUsername("wangkai");
logDO.setCreateTime(buildTime(2021, 3, 6));
logDO.setResult(SysLoginResultEnum.SUCCESS.getResult());
});
loginLogMapper.insert(loginLogDO);
// 下面几个都是不匹配的数据
// 登录失败的
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setResult(SysLoginResultEnum.CAPTCHA_CODE_ERROR.getResult())));
// 不同ip段的
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setUserIp("192.168.128.18")));
// 不同username
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setUsername("yunai")));
// 构造一个早期时间 2021-02-06 00:00:00
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
SysLoginLogPageReqVO reqVO = new SysLoginLogPageReqVO();
reqVO.setUsername("wangkai");
reqVO.setUserIp("192.168.199");
reqVO.setStatus(true);
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
// 调用service方法
PageResult<SysLoginLogDO> pageResult = sysLoginLogService.getLoginLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(loginLogDO, pageResult.getList().get(0));
}
@Test
public void testGetLoginLogList() {
// 构造测试数据
// 登录成功的
SysLoginLogDO loginLogDO = RandomUtils.randomPojo(SysLoginLogDO.class, logDO -> {
logDO.setLogType(RandomUtil.randomEle(SysLoginLogTypeEnum.values()).getType());
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setUserIp("192.168.111.16");
logDO.setUsername("wangxiaokai");
logDO.setCreateTime(buildTime(2021, 3, 6));
logDO.setResult(SysLoginResultEnum.SUCCESS.getResult());
});
loginLogMapper.insert(loginLogDO);
// 下面几个都是不匹配的数据
// 登录失败的
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setResult(SysLoginResultEnum.CAPTCHA_CODE_ERROR.getResult())));
// 不同ip段的
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setUserIp("192.168.128.18")));
// 不同username
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setUsername("yunai")));
// 构造一个早期时间 2021-02-06 00:00:00
loginLogMapper.insert(ObjectUtils.clone(loginLogDO, logDO -> logDO.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
SysLoginLogExportReqVO reqVO = new SysLoginLogExportReqVO();
reqVO.setUsername("wangxiaokai");
reqVO.setUserIp("192.168.111");
reqVO.setStatus(true);
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
// 调用service方法
List<SysLoginLogDO> loginLogList = sysLoginLogService.getLoginLogList(reqVO);
// 断言
assertEquals(1, loginLogList.size());
assertPojoEquals(loginLogDO, loginLogList.get(0));
}
}

View File

@@ -0,0 +1,166 @@
package cn.iocoder.dashboard.modules.system.service.logger;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum;
import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogPageReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.logger.SysOperateLogDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.logger.SysOperateLogMapper;
import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum;
import cn.iocoder.dashboard.modules.system.service.logger.impl.SysOperateLogServiceImpl;
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@Import({SysOperateLogServiceImpl.class})
public class SysOperateLogServiceImplTest extends BaseDbUnitTest {
@Resource
private SysOperateLogService operateLogServiceImpl;
@Resource
private SysOperateLogMapper operateLogMapper;
@MockBean
private SysUserService userService;
@Test
public void testCreateOperateLogAsync() throws InterruptedException, ExecutionException {
String traceId = TracerUtils.getTraceId();
SysOperateLogCreateReqVO reqVO = RandomUtils.randomPojo(SysOperateLogCreateReqVO.class, o -> {
o.setTraceId(traceId);
o.setUserId(randomLongId());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
// 执行service方法
Future<Boolean> future = operateLogServiceImpl.createOperateLogAsync(reqVO);
future.get();
// 断言插入是否正确
SysOperateLogDO sysOperateLogDO = operateLogMapper.selectOne("trace_id", traceId);
assertPojoEquals(reqVO, sysOperateLogDO);
}
@Test
public void testGetOperateLogPage() {
// 构造测试数据
// 先构造用户
SysUserDO user = RandomUtils.randomPojo(SysUserDO.class, o -> {
o.setNickname("wangkai");
o.setSex(SysSexEnum.MALE.getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user));
Long userId = user.getId();
// 构造操作日志
SysOperateLogDO sysOperateLogDO = RandomUtils.randomPojo(SysOperateLogDO.class, o -> {
o.setUserId(userId);
o.setModule("order");
o.setType(OperateTypeEnum.CREATE.getType());
o.setStartTime(buildTime(2021, 3, 6));
o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
operateLogMapper.insert(sysOperateLogDO);
// 下面几个是不匹配的数据
// 随机 userId
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setUserId(userId + 1)));
// module 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setModule("user")));
// type 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setType(OperateTypeEnum.IMPORT.getType())));
// createTime 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setStartTime(buildTime(2021, 2, 6))));
// resultCode 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setResultCode(GlobalErrorCodeConstants.BAD_REQUEST.getCode())));
// 构造调用参数
SysOperateLogPageReqVO reqVO = new SysOperateLogPageReqVO();
reqVO.setUserNickname("wangkai");
reqVO.setModule("order");
reqVO.setType(OperateTypeEnum.CREATE.getType());
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
reqVO.setSuccess(true);
// 调用service方法
PageResult<SysOperateLogDO> pageResult = operateLogServiceImpl.getOperateLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(sysOperateLogDO, pageResult.getList().get(0));
}
@Test
public void testGetOperateLogs() {
// 构造测试数据
// 先构造用户
SysUserDO user = RandomUtils.randomPojo(SysUserDO.class, o -> {
o.setNickname("wangkai");
o.setSex(SysSexEnum.MALE.getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user));
Long userId = user.getId();
// 构造操作日志
SysOperateLogDO sysOperateLogDO = RandomUtils.randomPojo(SysOperateLogDO.class, o -> {
o.setUserId(userId);
o.setModule("order");
o.setType(OperateTypeEnum.CREATE.getType());
o.setStartTime(buildTime(2021, 3, 6));
o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
operateLogMapper.insert(sysOperateLogDO);
// 下面几个是不匹配的数据
// 随机 userId
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setUserId(userId + 1)));
// module 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setModule("user")));
// type 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setType(OperateTypeEnum.IMPORT.getType())));
// createTime 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setStartTime(buildTime(2021, 2, 6))));
// resultCode 不同
operateLogMapper.insert(ObjectUtils.clone(sysOperateLogDO, logDO -> logDO.setResultCode(GlobalErrorCodeConstants.BAD_REQUEST.getCode())));
// 构造调用参数
SysOperateLogExportReqVO reqVO = new SysOperateLogExportReqVO();
reqVO.setUserNickname("wangkai");
reqVO.setModule("order");
reqVO.setType(OperateTypeEnum.CREATE.getType());
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
reqVO.setSuccess(true);
// 调用 service 方法
List<SysOperateLogDO> list = operateLogServiceImpl.getOperateLogs(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, list.size());
assertPojoEquals(sysOperateLogDO, list.get(0));
}
}

View File

@@ -0,0 +1,164 @@
package cn.iocoder.dashboard.modules.system.service.notice;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.notice.vo.SysNoticeCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.notice.vo.SysNoticePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.notice.vo.SysNoticeUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.notice.SysNoticeDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.notice.SysNoticeMapper;
import cn.iocoder.dashboard.modules.system.enums.notice.SysNoticeTypeEnum;
import cn.iocoder.dashboard.modules.system.service.notice.impl.SysNoticeServiceImpl;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.NOTICE_NOT_FOUND;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
@Import(SysNoticeServiceImpl.class)
class SysNoticeServiceImplTest extends BaseDbUnitTest {
@Resource
private SysNoticeServiceImpl sysNoticeService;
@Resource
private SysNoticeMapper sysNoticeMapper;
@Test
public void testPageNotices_success() {
// 插入前置数据
SysNoticeDO dbNotice = randomPojo(SysNoticeDO.class, o -> {
o.setTitle("尼古拉斯赵四来啦!");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setType(randomEle(SysNoticeTypeEnum.values()).getType());
});
sysNoticeMapper.insert(dbNotice);
// 测试 title 不匹配
sysNoticeMapper.insert(ObjectUtils.clone(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!")));
// 测试 status 不匹配
sysNoticeMapper.insert(ObjectUtils.clone(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 查询
SysNoticePageReqVO reqVO = new SysNoticePageReqVO();
reqVO.setTitle("尼古拉斯赵四来啦!");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
PageResult<SysNoticeDO> pageResult = sysNoticeService.pageNotices(reqVO);
// 验证查询结果经过筛选
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotice, pageResult.getList().get(0));
}
@Test
public void testGetNotice_success() {
// 插入前置数据
SysNoticeDO dbNotice = randomSysNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 查询
SysNoticeDO notice = sysNoticeService.getNotice(dbNotice.getId());
// 验证插入与读取对象是否一致
assertNotNull(notice);
assertPojoEquals(dbNotice, notice);
}
@Test
public void testCreateNotice_success() {
// 准备参数
SysNoticeCreateReqVO reqVO = randomSysNoticeCreateReqVO();
// 校验插入是否成功
Long noticeId = sysNoticeService.createNotice(reqVO);
assertNotNull(noticeId);
// 校验插入属性是否正确
SysNoticeDO notice = sysNoticeMapper.selectById(noticeId);
assertPojoEquals(reqVO, notice);
}
@Test
public void testUpdateNotice_success() {
// 插入前置数据
SysNoticeDO dbNoticeDO = randomSysNoticeDO();
sysNoticeMapper.insert(dbNoticeDO);
// 准备更新参数
SysNoticeUpdateReqVO reqVO = randomSysNoticeUpdateReqVO(o -> o.setId(dbNoticeDO.getId()));
// 更新
sysNoticeService.updateNotice(reqVO);
// 检验是否更新成功
SysNoticeDO notice = sysNoticeMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, notice);
}
@Test
public void testDeleteNotice_success() {
// 插入前置数据
SysNoticeDO dbNotice = randomSysNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 删除
sysNoticeService.deleteNotice(dbNotice.getId());
// 检查是否删除成功
assertNull(sysNoticeMapper.selectById(dbNotice.getId()));
}
@Test
public void checkNoticeExists_success() {
// 插入前置数据
SysNoticeDO dbNotice = randomSysNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 成功调用
sysNoticeService.checkNoticeExists(dbNotice.getId());
}
@Test
public void checkNoticeExists_noExists() {
assertServiceException(() -> sysNoticeService.checkNoticeExists(randomLongId()), NOTICE_NOT_FOUND);
}
@SafeVarargs
private static SysNoticeDO randomSysNoticeDO(Consumer<SysNoticeDO>... consumers) {
SysNoticeDO notice = randomPojo(SysNoticeDO.class, consumers);
notice.setType(randomEle(SysNoticeTypeEnum.values()).getType());
notice.setStatus(CommonStatusEnum.ENABLE.getStatus());
return notice;
}
@SafeVarargs
private static SysNoticeUpdateReqVO randomSysNoticeUpdateReqVO(Consumer<SysNoticeUpdateReqVO>... consumers) {
SysNoticeUpdateReqVO reqVO = randomPojo(SysNoticeUpdateReqVO.class, consumers);
reqVO.setType(randomEle(SysNoticeTypeEnum.values()).getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
return reqVO;
}
private static SysNoticeCreateReqVO randomSysNoticeCreateReqVO() {
SysNoticeCreateReqVO reqVO = randomPojo(SysNoticeCreateReqVO.class);
reqVO.setType(randomEle(SysNoticeTypeEnum.values()).getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
return reqVO;
}
}

View File

@@ -0,0 +1,366 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysMenuDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysMenuMapper;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer;
import cn.iocoder.dashboard.modules.system.service.permission.impl.SysMenuServiceImpl;
import cn.iocoder.dashboard.util.sping.SpringAopUtils;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.Multimap;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
@Import(SysMenuServiceImpl.class)
public class SysMenuServiceTest extends BaseDbUnitTest {
@Resource
private SysMenuServiceImpl sysMenuService;
@MockBean
private SysPermissionService sysPermissionService;
@MockBean
private SysMenuProducer sysMenuProducer;
@Resource
private SysMenuMapper menuMapper;
@Test
public void testInitLocalCache_success() throws Exception {
SysMenuDO menuDO1 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
menuMapper.insert(menuDO1);
SysMenuDO menuDO2 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
menuMapper.insert(menuDO2);
// 调用
sysMenuService.initLocalCache();
// 获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
Map<Long, SysMenuDO> menuCache =
(Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache");
Assert.isTrue(menuCache.size() == 2);
assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId()));
assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId()));
Multimap<String, SysMenuDO> permissionMenuCache =
(Multimap<String, SysMenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache");
Assert.isTrue(permissionMenuCache.size() == 2);
assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission()));
assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission()));
Date maxUpdateTime = (Date) BeanUtil.getFieldValue(target, "maxUpdateTime");
assertEquals(ObjectUtils.max(menuDO1.getUpdateTime(), menuDO2.getUpdateTime()), maxUpdateTime);
}
@Test
public void testCreateMenu_success() {
//构造父目录
SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
//调用
SysMenuCreateReqVO vo = randomPojo(SysMenuCreateReqVO.class, o -> {
o.setParentId(parentId);
o.setName("testSonName");
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(RandomUtils.randomCommonStatus());
});
Long menuId = sysMenuService.createMenu(vo);
//断言
Assertions.assertNotNull(menuId);
// 校验记录的属性是否正确
SysMenuDO ret = menuMapper.selectById(menuId);
assertPojoEquals(vo, ret);
// 校验调用
verify(sysMenuProducer).sendMenuRefreshMessage();
}
@Test
public void testUpdateMenu_success() {
//构造父子目录
SysMenuDO sonMenuDO = initParentAndSonMenuDO();
Long sonId = sonMenuDO.getId();
Long parentId = sonMenuDO.getParentId();
//调用
SysMenuUpdateReqVO vo = RandomUtils.randomPojo(SysMenuUpdateReqVO.class, o -> {
o.setId(sonId);
o.setParentId(parentId);
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(RandomUtils.randomCommonStatus());
o.setName("pppppp"); //修改名字
});
sysMenuService.updateMenu(vo);
//断言
// 校验记录的属性是否正确
SysMenuDO ret = menuMapper.selectById(sonId);
assertPojoEquals(vo, ret);
// 校验调用
verify(sysMenuProducer).sendMenuRefreshMessage();
}
@Test
public void testUpdateMenu_sonIdNotExist() {
Long sonId = 99999L;
Long parentId = 10000L;
//调用
SysMenuUpdateReqVO vo = RandomUtils.randomPojo(SysMenuUpdateReqVO.class, o -> {
o.setId(sonId);
o.setParentId(parentId);
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(RandomUtils.randomCommonStatus());
});
//断言
assertServiceException(() -> sysMenuService.updateMenu(vo), MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_success() {
SysMenuDO sonMenuDO = initParentAndSonMenuDO();
Long sonId = sonMenuDO.getId();
//调用
sysMenuService.deleteMenu(sonId);
//断言
SysMenuDO menuDO = menuMapper.selectById(sonId);
Assert.isNull(menuDO);
verify(sysPermissionService).processMenuDeleted(sonId);
verify(sysMenuProducer).sendMenuRefreshMessage();
}
@Test
public void testDeleteMenu_menuNotExist() {
Long sonId = 99999L;
assertServiceException(() -> sysMenuService.deleteMenu(sonId), MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_existChildren() {
SysMenuDO sonMenu = initParentAndSonMenuDO();
Long parentId = sonMenu.getParentId();
assertServiceException(() -> sysMenuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN);
}
@Test
public void testGetMenus_success() {
Map<Long, SysMenuDO> idMenuMap = new HashMap<>();
SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
SysMenuDO sonMenu = createMenuDO(MenuTypeEnum.MENU, "son", menuDO.getId());
menuMapper.insert(sonMenu);
idMenuMap.put(sonMenu.getId(), sonMenu);
//调用
List<SysMenuDO> menuDOS = sysMenuService.getMenus();
//断言
Assert.isTrue(menuDOS.size() == idMenuMap.size());
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
}
@Test
public void testGetMenusReqVo_success() {
Map<Long, SysMenuDO> idMenuMap = new HashMap<>();
//用于验证可以模糊搜索名称包含"name"状态为1的menu
SysMenuDO menu = createMenuDO(MenuTypeEnum.MENU, "name2", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
menu = createMenuDO(MenuTypeEnum.MENU, "11name111", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
//以下是不符合搜索条件的的menu
menu = createMenuDO(MenuTypeEnum.MENU, "xxxxxx", 0L, 1);
menuMapper.insert(menu);
menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 2);
menuMapper.insert(menu);
//调用
SysMenuListReqVO reqVO = new SysMenuListReqVO();
reqVO.setStatus(1);
reqVO.setName("name");
List<SysMenuDO> menuDOS = sysMenuService.getMenus(reqVO);
//断言
Assert.isTrue(menuDOS.size() == idMenuMap.size());
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
}
@Test
public void testListMenusFromCache_success() throws Exception {
Map<Long, SysMenuDO> mockCacheMap = new HashMap<>();
//获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, SysMenuDO> idMenuMap = new HashMap<>();
//用于验证搜索类型为MENU,状态为1的menu
SysMenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
//以下是不符合搜索条件的menu
menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
mockCacheMap.put(menuDO.getId(), menuDO);
List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()),
Collections.singletonList(CommonStatusEnum.DISABLE.getStatus()));
Assert.isTrue(menuDOS.size() == idMenuMap.size());
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
}
@Test
public void testListMenusFromCache2_success() throws Exception {
Map<Long, SysMenuDO> mockCacheMap = new HashMap<>();
//获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, SysMenuDO> idMenuMap = new HashMap<>();
//验证搜索id为1, 类型为MENU, 状态为1 的menu
SysMenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
//以下是不符合搜索条件的menu
menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
mockCacheMap.put(menuDO.getId(), menuDO);
List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(1L),
Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1));
Assert.isTrue(menuDOS.size() == idMenuMap.size());
menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu));
}
@Test
public void testCheckParentResource_success() {
SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
sysMenuService.checkParentResource(parentId, null);
}
@Test
public void testCheckParentResource_canNotSetSelfToBeParent() {
assertServiceException(() -> sysMenuService.checkParentResource(1L, 1L), MENU_PARENT_ERROR);
}
@Test
public void testCheckParentResource_parentNotExist() {
assertServiceException(() -> sysMenuService.checkParentResource(randomLongId(), null), MENU_PARENT_NOT_EXISTS);
}
@Test
public void testCheckParentResource_parentTypeError() {
SysMenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
assertServiceException(() -> sysMenuService.checkParentResource(parentId, null), MENU_PARENT_NOT_DIR_OR_MENU);
}
@Test
public void testCheckResource_success() {
SysMenuDO sonMenu = initParentAndSonMenuDO();
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
String otherSonMenuName = randomString();
sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId);
}
@Test
public void testCheckResource_sonMenuNameDuplicate(){
SysMenuDO sonMenu=initParentAndSonMenuDO();
Long parentId=sonMenu.getParentId();
Long otherSonMenuId=randomLongId();
String otherSonMenuName=sonMenu.getName(); //相同名称
assertServiceException(() -> sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId), MENU_NAME_DUPLICATE);
}
/**
* 构造父子目录,返回子目录
*
* @return
*/
private SysMenuDO initParentAndSonMenuDO() {
//构造父子目录
SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
SysMenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", parentId);
menuMapper.insert(sonMenuDO);
return sonMenuDO;
}
private SysMenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId) {
return createMenuDO(typeEnum, menuName, parentId, RandomUtils.randomCommonStatus());
}
private SysMenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) {
return createMenuDO(null, typeEnum, menuName, parentId, status);
}
private SysMenuDO createMenuDO(Long id, MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) {
return RandomUtils.randomPojo(SysMenuDO.class, o -> {
o.setId(id);
o.setParentId(parentId);
o.setType(typeEnum.getType());
o.setStatus(status);
o.setName(menuName);
});
}
}

View File

@@ -0,0 +1,109 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleMenuDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysUserRoleDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper;
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer;
import cn.iocoder.dashboard.modules.system.service.permission.impl.SysPermissionServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
@Import(SysPermissionServiceImpl.class)
public class SysPermissionServiceTest extends BaseDbUnitTest {
@Resource
private SysPermissionServiceImpl permissionService;
@Resource
private SysRoleMenuMapper roleMenuMapper;
@Resource
private SysUserRoleMapper userRoleMapper;
@MockBean
private SysRoleService roleService;
@MockBean
private SysMenuService menuService;
@MockBean
private SysPermissionProducer permissionProducer;
@Test
public void testProcessRoleDeleted() {
// 准备参数
Long roleId = randomLongId();
// mock 数据 UserRole
SysUserRoleDO userRoleDO01 = randomPojo(SysUserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除
userRoleMapper.insert(userRoleDO01);
SysUserRoleDO userRoleDO02 = randomPojo(SysUserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// mock 数据 RoleMenu
SysRoleMenuDO roleMenuDO01 = randomPojo(SysRoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
SysRoleMenuDO roleMenuDO02 = randomPojo(SysRoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processRoleDeleted(roleId);
// 断言数据 RoleMenuDO
List<SysRoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
// 断言数据 UserRoleDO
List<SysUserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
// 断言调用
verify(permissionProducer).sendRoleMenuRefreshMessage();
}
@Test
public void testProcessMenuDeleted() {
// 准备参数
Long menuId = randomLongId();
// mock 数据
SysRoleMenuDO roleMenuDO01 = randomPojo(SysRoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
SysRoleMenuDO roleMenuDO02 = randomPojo(SysRoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processMenuDeleted(menuId);
// 断言数据
List<SysRoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
// 断言调用
verify(permissionProducer).sendRoleMenuRefreshMessage();
}
@Test
public void testProcessUserDeleted() {
// 准备参数
Long userId = randomLongId();
// mock 数据
SysUserRoleDO userRoleDO01 = randomPojo(SysUserRoleDO.class, o -> o.setUserId(userId)); // 被删除
userRoleMapper.insert(userRoleDO01);
SysUserRoleDO userRoleDO02 = randomPojo(SysUserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// 调用
permissionService.processUserDeleted(userId);
// 断言数据
List<SysUserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
}
}

View File

@@ -0,0 +1,302 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.security.core.enums.DataScopeEnum;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRoleCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRolePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRoleUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMapper;
import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer;
import cn.iocoder.dashboard.modules.system.service.permission.impl.SysRoleServiceImpl;
import cn.iocoder.dashboard.util.sping.SpringAopUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.object.ObjectUtils.max;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
@Import(SysRoleServiceImpl.class)
public class SysRoleServiceTest extends BaseDbUnitTest {
@Resource
private SysRoleServiceImpl sysRoleService;
@Resource
private SysRoleMapper roleMapper;
@MockBean
private SysPermissionService sysPermissionService;
@MockBean
private SysRoleProducer sysRoleProducer;
@Test
public void testInitLocalCache_success() throws Exception {
SysRoleDO roleDO1 = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO1);
SysRoleDO roleDO2 = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO2);
//调用
sysRoleService.initLocalCache();
//断言
//获取代理对象
SysRoleServiceImpl target = (SysRoleServiceImpl) SpringAopUtils.getTarget(sysRoleService);
Map<Long, SysRoleDO> roleCache = (Map<Long, SysRoleDO>) BeanUtil.getFieldValue(target, "roleCache");
assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId()));
assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId()));
Date maxUpdateTime = (Date) BeanUtil.getFieldValue(target, "maxUpdateTime");
assertEquals(max(roleDO1.getUpdateTime(), roleDO2.getUpdateTime()), maxUpdateTime);
}
@Test
public void testCreateRole_success() {
SysRoleCreateReqVO reqVO = randomPojo(SysRoleCreateReqVO.class, o -> {
o.setCode("role_code");
o.setName("role_name");
o.setRemark("remark");
o.setType(SysRoleTypeEnum.CUSTOM.getType());
o.setSort(1);
});
Long roleId = sysRoleService.createRole(reqVO);
//断言
assertNotNull(roleId);
SysRoleDO roleDO = roleMapper.selectById(roleId);
assertPojoEquals(reqVO, roleDO);
verify(sysRoleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRole_success() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
SysRoleUpdateReqVO reqVO = randomPojo(SysRoleUpdateReqVO.class, o -> {
o.setId(roleId);
o.setCode("role_code");
o.setName("update_name");
o.setType(SysRoleTypeEnum.SYSTEM.getType());
o.setSort(999);
});
sysRoleService.updateRole(reqVO);
//断言
SysRoleDO newRoleDO = roleMapper.selectById(roleId);
assertPojoEquals(reqVO, newRoleDO);
verify(sysRoleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRoleStatus_success() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, CommonStatusEnum.ENABLE.getStatus());
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
sysRoleService.updateRoleStatus(roleId, CommonStatusEnum.DISABLE.getStatus());
//断言
SysRoleDO newRoleDO = roleMapper.selectById(roleId);
assertEquals(CommonStatusEnum.DISABLE.getStatus(), newRoleDO.getStatus());
verify(sysRoleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRoleDataScope_success() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
Set<Long> deptIdSet = Arrays.asList(1L, 2L, 3L, 4L, 5L).stream().collect(Collectors.toSet());
sysRoleService.updateRoleDataScope(roleId, DataScopeEnum.DEPT_CUSTOM.getScore(), deptIdSet);
//断言
SysRoleDO newRoleDO = roleMapper.selectById(roleId);
assertEquals(DataScopeEnum.DEPT_CUSTOM.getScore(), newRoleDO.getDataScope());
Set<Long> newDeptIdSet = newRoleDO.getDataScopeDeptIds();
assertTrue(deptIdSet.size() == newDeptIdSet.size());
deptIdSet.stream().forEach(d -> assertTrue(newDeptIdSet.contains(d)));
verify(sysRoleProducer).sendRoleRefreshMessage();
}
@Test
public void testDeleteRole_success() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
sysRoleService.deleteRole(roleId);
//断言
SysRoleDO newRoleDO = roleMapper.selectById(roleId);
assertNull(newRoleDO);
verify(sysRoleProducer).sendRoleRefreshMessage();
}
@Test
public void testGetRoles_success() {
Map<Long, SysRoleDO> idRoleMap = new HashMap<>();
// 验证查询状态为1的角色
SysRoleDO roleDO1 = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);
roleMapper.insert(roleDO1);
idRoleMap.put(roleDO1.getId(), roleDO1);
SysRoleDO roleDO2 = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);
roleMapper.insert(roleDO2);
idRoleMap.put(roleDO2.getId(), roleDO2);
// 以下是排除的角色
SysRoleDO roleDO3 = createRoleDO("role3", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2);
roleMapper.insert(roleDO3);
//调用
List<SysRoleDO> roles = sysRoleService.getRoles(Arrays.asList(1));
//断言
assertEquals(2, roles.size());
roles.stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r));
}
@Test
public void testGetRolePage_success() {
Map<Long, SysRoleDO> idRoleMap = new HashMap<>();
// 验证名称包含"role", 状态为1,code为"code"的角色
// 第一页
SysRoleDO roleDO = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");
roleMapper.insert(roleDO);
idRoleMap.put(roleDO.getId(), roleDO);
// 第二页
roleDO = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");
roleMapper.insert(roleDO);
// 以下是排除的角色
roleDO = createRoleDO("role3", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2, "code");
roleMapper.insert(roleDO);
roleDO = createRoleDO("role4", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "xxxxx");
roleMapper.insert(roleDO);
//调用
SysRolePageReqVO reqVO = randomPojo(SysRolePageReqVO.class, o -> {
o.setName("role");
o.setCode("code");
o.setStatus(1);
o.setPageNo(1);
o.setPageSize(1);
o.setBeginTime(null);
o.setEndTime(null);
});
PageResult<SysRoleDO> result = sysRoleService.getRolePage(reqVO);
assertEquals(2, result.getTotal());
result.getList().stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r));
}
@Test
public void testCheckDuplicateRole_success() {
sysRoleService.checkDuplicateRole(randomString(), randomString(), null);
}
@Test
public void testCheckDuplicateRole_nameDuplicate() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
String duplicateName = "role_name";
assertServiceException(() -> sysRoleService.checkDuplicateRole(duplicateName, randomString(), null), ROLE_NAME_DUPLICATE, duplicateName);
}
@Test
public void testCheckDuplicateRole_codeDuplicate() {
SysRoleDO roleDO = randomPojo(SysRoleDO.class, o -> {
o.setName("role_999");
o.setCode("code");
o.setType(SysRoleTypeEnum.CUSTOM.getType());
o.setStatus(1);
o.setDataScope(DataScopeEnum.ALL.getScore());
});
roleMapper.insert(roleDO);
String randomName = randomString();
String duplicateCode = "code";
assertServiceException(() -> sysRoleService.checkDuplicateRole(randomName, duplicateCode, null), ROLE_CODE_DUPLICATE, duplicateCode);
}
@Test
public void testCheckUpdateRole_success() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
sysRoleService.checkUpdateRole(roleId);
}
@Test
public void testCheckUpdateRole_roleIdNotExist() {
assertServiceException(() -> sysRoleService.checkUpdateRole(randomLongId()), ROLE_NOT_EXISTS);
}
@Test
public void testCheckUpdateRole_systemRoleCanNotBeUpdate() {
SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.SYSTEM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
assertServiceException(() -> sysRoleService.checkUpdateRole(roleId), ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
}
private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status) {
return createRoleDO( name, typeEnum, scopeEnum, status, randomString());
}
private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) {
return createRoleDO(null, name, typeEnum, scopeEnum, status, code);
}
private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum) {
return createRoleDO(null, name, typeEnum, scopeEnum, randomCommonStatus(), randomString());
}
private SysRoleDO createRoleDO(Long id, String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) {
SysRoleDO roleDO = randomPojo(SysRoleDO.class, o -> {
o.setId(id);
o.setName(name);
o.setType(typeEnum.getType());
o.setStatus(status);
o.setDataScope(scopeEnum.getScore());
o.setCode(code);
});
return roleDO;
}
}

View File

@@ -0,0 +1,202 @@
package cn.iocoder.dashboard.modules.system.service.sms;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper;
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer;
import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsChannelServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
import static cn.iocoder.dashboard.util.AssertUtils.*;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static cn.iocoder.dashboard.util.object.ObjectUtils.max;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysSmsChannelServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysSmsChannelServiceImpl.class)
public class SysSmsChannelServiceTest extends BaseDbUnitTest {
@Resource
private SysSmsChannelServiceImpl smsChannelService;
@Resource
private SysSmsChannelMapper smsChannelMapper;
@MockBean
private SmsClientFactory smsClientFactory;
@MockBean
private SysSmsTemplateService smsTemplateService;
@MockBean
private SysSmsProducer smsProducer;
@Test
public void testInitLocalCache_success() {
// mock 数据
SysSmsChannelDO smsChannelDO01 = randomSmsChannelDO();
smsChannelMapper.insert(smsChannelDO01);
SysSmsChannelDO smsChannelDO02 = randomSmsChannelDO();
smsChannelMapper.insert(smsChannelDO02);
// 调用
smsChannelService.initSmsClients();
// 校验 maxUpdateTime 属性
Date maxUpdateTime = (Date) BeanUtil.getFieldValue(smsChannelService, "maxUpdateTime");
assertEquals(max(smsChannelDO01.getUpdateTime(), smsChannelDO02.getUpdateTime()), maxUpdateTime);
// 校验调用
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
argThat(properties -> isPojoEquals(smsChannelDO02, properties)));
}
@Test
public void testCreateSmsChannel_success() {
// 准备参数
SysSmsChannelCreateReqVO reqVO = randomPojo(SysSmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus()));
// 调用
Long smsChannelId = smsChannelService.createSmsChannel(reqVO);
// 断言
assertNotNull(smsChannelId);
// 校验记录的属性是否正确
SysSmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
assertPojoEquals(reqVO, smsChannel);
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testUpdateSmsChannel_success() {
// mock 数据
SysSmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
SysSmsChannelUpdateReqVO reqVO = randomPojo(SysSmsChannelUpdateReqVO.class, o -> {
o.setId(dbSmsChannel.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
o.setCallbackUrl(randomString());
});
// 调用
smsChannelService.updateSmsChannel(reqVO);
// 校验是否更新正确
SysSmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, smsChannel);
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testUpdateSmsChannel_notExists() {
// 准备参数
SysSmsChannelUpdateReqVO reqVO = randomPojo(SysSmsChannelUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteSmsChannel_success() {
// mock 数据
SysSmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsChannel.getId();
// 调用
smsChannelService.deleteSmsChannel(id);
// 校验数据不存在了
assertNull(smsChannelMapper.selectById(id));
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testDeleteSmsChannel_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteSmsChannel_hasChildren() {
// mock 数据
SysSmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsChannel.getId();
// mock 方法
when(smsTemplateService.countByChannelId(eq(id))).thenReturn(10);
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN);
}
@Test
public void testGetSmsChannelPage() {
// mock 数据
SysSmsChannelDO dbSmsChannel = randomPojo(SysSmsChannelDO.class, o -> { // 等会查询到
o.setSignature("芋道源码");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
});
smsChannelMapper.insert(dbSmsChannel);
// 测试 signature 不匹配
smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setSignature("源码")));
// 测试 status 不匹配
smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 准备参数
SysSmsChannelPageReqVO reqVO = new SysSmsChannelPageReqVO();
reqVO.setSignature("芋道");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2020, 12, 1));
reqVO.setEndCreateTime(buildTime(2020, 12, 24));
// 调用
PageResult<SysSmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysSmsChannelDO randomSmsChannelDO(Consumer<SysSmsChannelDO>... consumers) {
Consumer<SysSmsChannelDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(SysSmsChannelDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,248 @@
package cn.iocoder.dashboard.modules.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.UserTypeEnum;
import cn.iocoder.dashboard.common.pojo.CommonResult;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsLogMapper;
import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsReceiveStatusEnum;
import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum;
import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsLogServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBoolean;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SysSmsLogServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysSmsLogServiceImpl.class)
public class SysSmsLogServiceTest extends BaseDbUnitTest {
@Resource
private SysSmsLogServiceImpl smsLogService;
@Resource
private SysSmsLogMapper smsLogMapper;
@Test
public void testCreateSmsLog() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
Boolean isSend = randomBoolean();
SysSmsTemplateDO templateDO = randomPojo(SysSmsTemplateDO.class,
o -> o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()));
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend,
templateDO, templateContent, templateParams);
// 断言
SysSmsLogDO logDO = smsLogMapper.selectById(logId);
assertEquals(isSend ? SysSmsSendStatusEnum.INIT.getStatus() : SysSmsSendStatusEnum.IGNORE.getStatus(),
logDO.getSendStatus());
assertEquals(mobile, logDO.getMobile());
assertEquals(userType, logDO.getUserType());
assertEquals(userId, logDO.getUserId());
assertEquals(templateDO.getId(), logDO.getTemplateId());
assertEquals(templateDO.getCode(), logDO.getTemplateCode());
assertEquals(templateDO.getType(), logDO.getTemplateType());
assertEquals(templateDO.getChannelId(), logDO.getChannelId());
assertEquals(templateDO.getChannelCode(), logDO.getChannelCode());
assertEquals(templateContent, logDO.getTemplateContent());
assertEquals(templateParams, logDO.getTemplateParams());
assertEquals(SysSmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus());
}
@Test
public void testUpdateSmsSendResult() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus()));
smsLogMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Integer sendCode = randomInteger();
String sendMsg = randomString();
String apiSendCode = randomString();
String apiSendMsg = randomString();
String apiRequestId = randomString();
String apiSerialNo = randomString();
// 调用
smsLogService.updateSmsSendResult(id, sendCode, sendMsg,
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
// 断言
dbSmsLog = smsLogMapper.selectById(id);
assertEquals(CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS.getStatus()
: SysSmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
assertNotNull(dbSmsLog.getSendTime());
assertEquals(sendMsg, dbSmsLog.getSendMsg());
assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
assertEquals(apiRequestId, dbSmsLog.getApiRequestId());
assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo());
}
@Test
public void testUpdateSmsReceiveResult() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()));
smsLogMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Boolean success = randomBoolean();
Date receiveTime = randomDate();
String apiReceiveCode = randomString();
String apiReceiveMsg = randomString();
// 调用
smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg);
// 断言
dbSmsLog = smsLogMapper.selectById(id);
assertEquals(success ? SysSmsReceiveStatusEnum.SUCCESS.getStatus()
: SysSmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus());
assertEquals(receiveTime, dbSmsLog.getReceiveTime());
assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode());
assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg());
}
@Test
public void testGetSmsLogPage() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到
o.setChannelId(1L);
o.setTemplateId(10L);
o.setMobile("15601691300");
o.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus());
o.setSendTime(buildTime(2020, 11, 11));
o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus());
o.setReceiveTime(buildTime(2021, 11, 11));
});
smsLogMapper.insert(dbSmsLog);
// 测试 channelId 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setChannelId(2L)));
// 测试 templateId 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setTemplateId(20L)));
// 测试 mobile 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setMobile("18818260999")));
// 测试 sendStatus 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus())));
// 测试 sendTime 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12))));
// 测试 receiveStatus 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.SUCCESS.getStatus())));
// 测试 receiveTime 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12))));
// 准备参数
SysSmsLogPageReqVO reqVO = new SysSmsLogPageReqVO();
reqVO.setChannelId(1L);
reqVO.setTemplateId(10L);
reqVO.setMobile("156");
reqVO.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus());
reqVO.setBeginSendTime(buildTime(2020, 11, 1));
reqVO.setEndSendTime(buildTime(2020, 11, 30));
reqVO.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus());
reqVO.setBeginReceiveTime(buildTime(2021, 11, 1));
reqVO.setEndReceiveTime(buildTime(2021, 11, 30));
// 调用
PageResult<SysSmsLogDO> pageResult = smsLogService.getSmsLogPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsLog, pageResult.getList().get(0));
}
@Test
public void testGetSmsLogList() {
// mock 数据
SysSmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到
o.setChannelId(1L);
o.setTemplateId(10L);
o.setMobile("15601691300");
o.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus());
o.setSendTime(buildTime(2020, 11, 11));
o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus());
o.setReceiveTime(buildTime(2021, 11, 11));
});
smsLogMapper.insert(dbSmsLog);
// 测试 channelId 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setChannelId(2L)));
// 测试 templateId 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setTemplateId(20L)));
// 测试 mobile 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setMobile("18818260999")));
// 测试 sendStatus 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus())));
// 测试 sendTime 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12))));
// 测试 receiveStatus 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.SUCCESS.getStatus())));
// 测试 receiveTime 不匹配
smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12))));
// 准备参数
SysSmsLogExportReqVO reqVO = new SysSmsLogExportReqVO();
reqVO.setChannelId(1L);
reqVO.setTemplateId(10L);
reqVO.setMobile("156");
reqVO.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus());
reqVO.setBeginSendTime(buildTime(2020, 11, 1));
reqVO.setEndSendTime(buildTime(2020, 11, 30));
reqVO.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus());
reqVO.setBeginReceiveTime(buildTime(2021, 11, 1));
reqVO.setEndReceiveTime(buildTime(2021, 11, 30));
// 调用
List<SysSmsLogDO> list = smsLogService.getSmsLogList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbSmsLog, list.get(0));
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysSmsLogDO randomSmsLogDO(Consumer<SysSmsLogDO>... consumers) {
Consumer<SysSmsLogDO> consumer = (o) -> {
o.setTemplateParams(randomTemplateParams());
o.setTemplateType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围
o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围
o.setSendStatus(randomEle(SysSmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围
o.setReceiveStatus(randomEle(SysSmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围
};
return randomPojo(SysSmsLogDO.class, ArrayUtils.append(consumer, consumers));
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

View File

@@ -0,0 +1,201 @@
package cn.iocoder.dashboard.modules.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.dashboard.BaseMockitoUnitTest;
import cn.iocoder.dashboard.common.core.KeyValue;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.enums.UserTypeEnum;
import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer;
import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsServiceImpl;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysSmsServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
public class SysSmsServiceTest extends BaseMockitoUnitTest {
@InjectMocks
private SysSmsServiceImpl smsService;
@Mock
private SysSmsTemplateService smsTemplateService;
@Mock
private SysSmsLogService smsLogService;
@Mock
private SysSmsProducer smsProducer;
@Mock
private SmsClientFactory smsClientFactory;
/**
* 发送成功,当短信模板开启时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateEnable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsProducer, times(1)).sendSmsSendMessage(eq(smsLogId), eq(mobile),
eq(template.getChannelId()), eq(template.getApiTemplateId()),
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
}
/**
* 发送成功,当短信模板关闭时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateDisable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(),
anyLong(), any(), anyList());
}
@Test
public void testCheckSmsTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.checkSmsTemplateValid(templateCode),
SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testBuildTemplateParams_paramMiss() {
// 准备参数
SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.buildTemplateParams(template, templateParams),
SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code");
}
@Test
public void testCheckMobile_notExists() {
// 准备参数
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.checkMobile(null),
SMS_SEND_MOBILE_NOT_EXISTS);
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() {
// 准备参数
SysSmsSendMessage message = randomPojo(SysSmsSendMessage.class);
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
// mock SmsClient 的方法
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
eq(message.getTemplateParams()))).thenReturn(sendResult);
// 调用
smsService.doSendSms(message);
// 断言
verify(smsLogService, times(1)).updateSmsSendResult(eq(message.getLogId()),
eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()),
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo()));
}
@Test
public void testReceiveSmsStatus() throws Throwable {
// 准备参数
String channelCode = randomString();
String text = randomString();
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient);
// mock SmsClient 的方法
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
// 调用
smsService.receiveSmsStatus(channelCode, text);
// 断言
receiveResults.forEach(result -> {
smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode()));
});
}
}

View File

@@ -0,0 +1,380 @@
package cn.iocoder.dashboard.modules.system.service.sms;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper;
import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer;
import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsTemplateServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static cn.iocoder.dashboard.util.object.ObjectUtils.max;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link SysSmsTemplateServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(SysSmsTemplateServiceImpl.class)
public class SysSmsTemplateServiceTest extends BaseDbUnitTest {
@Resource
private SysSmsTemplateServiceImpl smsTemplateService;
@Resource
private SysSmsTemplateMapper smsTemplateMapper;
@MockBean
private SysSmsChannelService smsChannelService;
@MockBean
private SmsClientFactory smsClientFactory;
@MockBean
private SmsClient smsClient;
@MockBean
private SysSmsProducer smsProducer;
@Test
@SuppressWarnings("unchecked")
void testInitLocalCache() {
// mock 数据
SysSmsTemplateDO smsTemplate01 = randomSmsTemplateDO();
smsTemplateMapper.insert(smsTemplate01);
SysSmsTemplateDO smsTemplate02 = randomSmsTemplateDO();
smsTemplateMapper.insert(smsTemplate02);
// 调用
smsTemplateService.initLocalCache();
// 断言 deptCache 缓存
Map<String, SysSmsTemplateDO> smsTemplateCache = (Map<String, SysSmsTemplateDO>) getFieldValue(smsTemplateService, "smsTemplateCache");
assertEquals(2, smsTemplateCache.size());
assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode()));
assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(smsTemplateService, "maxUpdateTime");
assertEquals(max(smsTemplate01.getUpdateTime(), smsTemplate02.getUpdateTime()), maxUpdateTime);
}
@Test
public void testParseTemplateContentParams() {
// 准备参数
String content = "正在进行登录操作{operation},您的验证码是{code}";
// mock 方法
// 调用
List<String> params = smsTemplateService.parseTemplateContentParams(content);
// 断言
assertEquals(Lists.newArrayList("operation", "code"), params);
}
@Test
@SuppressWarnings("unchecked")
public void testCreateSmsTemplate_success() {
// 准备参数
SysSmsTemplateCreateReqVO reqVO = randomPojo(SysSmsTemplateCreateReqVO.class, o -> {
o.setContent("正在进行登录操作{operation},您的验证码是{code}");
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
});
// mock Channel 的方法
SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> {
o.setId(reqVO.getChannelId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功
when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
// 调用
Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO);
// 断言
assertNotNull(smsTemplateId);
// 校验记录的属性是否正确
SysSmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId);
assertPojoEquals(reqVO, smsTemplate);
assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
@SuppressWarnings("unchecked")
public void testUpdateSmsTemplate_success() {
// mock 数据
SysSmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
SysSmsTemplateUpdateReqVO reqVO = randomPojo(SysSmsTemplateUpdateReqVO.class, o -> {
o.setId(dbSmsTemplate.getId()); // 设置更新的 ID
o.setContent("正在进行登录操作{operation},您的验证码是{code}");
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
});
// mock 方法
SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> {
o.setId(reqVO.getChannelId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功
when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
// 调用
smsTemplateService.updateSmsTemplate(reqVO);
// 校验是否更新正确
SysSmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, smsTemplate);
assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
public void testUpdateSmsTemplate_notExists() {
// 准备参数
SysSmsTemplateUpdateReqVO reqVO = randomPojo(SysSmsTemplateUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testDeleteSmsTemplate_success() {
// mock 数据
SysSmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsTemplate.getId();
// 调用
smsTemplateService.deleteSmsTemplate(id);
// 校验数据不存在了
assertNull(smsTemplateMapper.selectById(id));
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
public void testDeleteSmsTemplate_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetSmsTemplatePage() {
// mock 数据
SysSmsTemplateDO dbSmsTemplate = randomPojo(SysSmsTemplateDO.class, o -> { // 等会查询到
o.setType(SysSmsTemplateTypeEnum.PROMOTION.getType());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCode("yudaoyuanma");
o.setContent("芋道源码");
o.setApiTemplateId("yunai");
o.setChannelId(1L);
o.setCreateTime(buildTime(2021, 11, 11));
});
smsTemplateMapper.insert(dbSmsTemplate);
// 测试 type 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setType(SysSmsTemplateTypeEnum.VERIFICATION_CODE.getType())));
// 测试 status 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 code 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCode("yuanma")));
// 测试 content 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setContent("源码")));
// 测试 apiTemplateId 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
// 测试 channelId 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setChannelId(2L)));
// 测试 createTime 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
SysSmsTemplatePageReqVO reqVO = new SysSmsTemplatePageReqVO();
reqVO.setType(SysSmsTemplateTypeEnum.PROMOTION.getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCode("yudao");
reqVO.setContent("芋道");
reqVO.setApiTemplateId("yu");
reqVO.setChannelId(1L);
reqVO.setBeginCreateTime(buildTime(2021, 11, 1));
reqVO.setEndCreateTime(buildTime(2021, 12, 1));
// 调用
PageResult<SysSmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0));
}
@Test
public void testGetSmsTemplateList() {
// mock 数据
SysSmsTemplateDO dbSmsTemplate = randomPojo(SysSmsTemplateDO.class, o -> { // 等会查询到
o.setType(SysSmsTemplateTypeEnum.PROMOTION.getType());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCode("yudaoyuanma");
o.setContent("芋道源码");
o.setApiTemplateId("yunai");
o.setChannelId(1L);
o.setCreateTime(buildTime(2021, 11, 11));
});
smsTemplateMapper.insert(dbSmsTemplate);
// 测试 type 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setType(SysSmsTemplateTypeEnum.VERIFICATION_CODE.getType())));
// 测试 status 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 code 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCode("yuanma")));
// 测试 content 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setContent("源码")));
// 测试 apiTemplateId 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
// 测试 channelId 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setChannelId(2L)));
// 测试 createTime 不匹配
smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
SysSmsTemplateExportReqVO reqVO = new SysSmsTemplateExportReqVO();
reqVO.setType(SysSmsTemplateTypeEnum.PROMOTION.getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCode("yudao");
reqVO.setContent("芋道");
reqVO.setApiTemplateId("yu");
reqVO.setChannelId(1L);
reqVO.setBeginCreateTime(buildTime(2021, 11, 1));
reqVO.setEndCreateTime(buildTime(2021, 12, 1));
// 调用
List<SysSmsTemplateDO> list = smsTemplateService.getSmsTemplateList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbSmsTemplate, list.get(0));
}
@Test
public void testCheckSmsChannel_success() {
// 准备参数
Long channelId = randomLongId();
// mock 方法
SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> {
o.setId(channelId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
// 调用
SysSmsChannelDO returnChannelDO = smsTemplateService.checkSmsChannel(channelId);
// 断言
assertPojoEquals(returnChannelDO, channelDO);
}
@Test
public void testCheckSmsChannel_notExists() {
// 准备参数
Long channelId = randomLongId();
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId),
SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testCheckSmsChannel_disable() {
// 准备参数
Long channelId = randomLongId();
// mock 方法
SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> {
o.setId(channelId);
o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败
});
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId),
SMS_CHANNEL_DISABLE);
}
@Test
public void testCheckDictDataValueUnique_success() {
// 调用,成功
smsTemplateService.checkSmsTemplateCodeDuplicate(randomLongId(), randomString());
}
@Test
public void testCheckSmsTemplateCodeDuplicate_valueDuplicateForCreate() {
// 准备参数
String code = randomString();
// mock 数据
smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(null, code),
SMS_TEMPLATE_CODE_DUPLICATE, code);
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String code = randomString();
// mock 数据
smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(id, code),
SMS_TEMPLATE_CODE_DUPLICATE, code);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysSmsTemplateDO randomSmsTemplateDO(Consumer<SysSmsTemplateDO>... consumers) {
Consumer<SysSmsTemplateDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
};
return randomPojo(SysSmsTemplateDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,590 @@
package cn.iocoder.dashboard.modules.system.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.infra.service.file.InfFileService;
import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO;
import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper;
import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum;
import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService;
import cn.iocoder.dashboard.modules.system.service.dept.SysPostService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBytes;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* {@link SysUserService} 的单元测试类
*
* @author zxl
*/
@Import(SysUserServiceImpl.class)
public class SysUserServiceImplTest extends BaseDbUnitTest {
@Resource
private SysUserServiceImpl userService;
@Resource
private SysUserMapper userMapper;
@MockBean
private SysDeptService deptService;
@MockBean
private SysPostService postService;
@MockBean
private SysPermissionService permissionService;
@MockBean
private PasswordEncoder passwordEncoder;
@MockBean
private InfFileService fileService;
@Test
public void testCreatUser_success() {
// 准备参数
SysUserCreateReqVO reqVO = randomPojo(SysUserCreateReqVO.class, o -> {
o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex());
o.setMobile(randomString());
});
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<SysPostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(SysPostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma");
// 调用
Long userId = userService.createUser(reqVO);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password");
assertEquals("yudaoyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
}
@Test
public void testUpdateUser_success() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
SysUserUpdateReqVO reqVO = randomPojo(SysUserUpdateReqVO.class, o -> {
o.setId(dbUser.getId());
o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex());
o.setMobile(randomString());
});
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<SysPostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(SysPostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// 调用
userService.updateUser(reqVO);
// 断言
SysUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserProfile_success() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
SysUserProfileUpdateReqVO reqVO = randomPojo(SysUserProfileUpdateReqVO.class, o -> {
o.setMobile(randomString());
o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex());
});
// 调用
userService.updateUserProfile(userId, reqVO);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserPassword_success() {
// mock 数据
SysUserDO dbUser = randomSysUserDO(o -> o.setPassword("encode:yudao"));
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
SysUserProfileUpdatePasswordReqVO reqVO = randomPojo(SysUserProfileUpdatePasswordReqVO.class, o -> {
o.setOldPassword("yudao");
o.setNewPassword("yuanma");
});
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true);
// 调用
userService.updateUserPassword(userId, reqVO);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertEquals("encode:yuanma", user.getPassword());
}
@Test
public void testUpdateUserAvatar_success() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
byte[] avatarFileBytes = randomBytes(10);
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
// mock 方法
String avatar = randomString();
when(fileService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar);
// 调用
userService.updateUserAvatar(userId, avatarFile);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertEquals(avatar, user.getAvatar());
}
@Test
public void testUpdateUserPassword02_success() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
String password = "yudao";
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
// 调用
userService.updateUserPassword(userId, password);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertEquals("encode:" + password, user.getPassword());
}
@Test
public void testUpdateUserStatus() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
Integer status = randomCommonStatus();
// 调用
userService.updateUserStatus(userId, status);
// 断言
SysUserDO user = userMapper.selectById(userId);
assertEquals(status, user.getStatus());
}
@Test
public void testDeleteUser_success(){
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用数据
userService.deleteUser(userId);
// 校验结果
assertNull(userService.getUser(userId));
// 校验调用次数
verify(permissionService, times(1)).processUserDeleted(eq(userId));
}
@Test
public void testGetUserPage() {
// mock 数据
SysUserDO dbUser = initGetUserPageData();
// 准备参数
SysUserPageReqVO reqVO = new SysUserPageReqVO();
reqVO.setUsername("yudao");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginTime(buildTime(2020, 12, 1));
reqVO.setEndTime(buildTime(2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<SysDeptDO> deptList = newArrayList(randomPojo(SysDeptDO.class, o -> o.setId(2L)));
when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList);
// 调用
PageResult<SysUserDO> pageResult = userService.getUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbUser, pageResult.getList().get(0));
}
@Test
public void testGetUsers() {
// mock 数据
SysUserDO dbUser = initGetUserPageData();
// 准备参数
SysUserExportReqVO reqVO = new SysUserExportReqVO();
reqVO.setUsername("yudao");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginTime(buildTime(2020, 12, 1));
reqVO.setEndTime(buildTime(2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<SysDeptDO> deptList = newArrayList(randomPojo(SysDeptDO.class, o -> o.setId(2L)));
when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList);
// 调用
List<SysUserDO> list = userService.getUsers(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbUser, list.get(0));
}
/**
* 初始化 getUserPage 方法的测试数据
*/
private SysUserDO initGetUserPageData() {
// mock 数据
SysUserDO dbUser = randomSysUserDO(o -> { // 等会查询到
o.setUsername("yudaoyuanma");
o.setMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
o.setDeptId(2L);
});
userMapper.insert(dbUser);
// 测试 username 不匹配
userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setUsername("yuanma")));
// 测试 mobile 不匹配
userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setMobile("18818260888")));
// 测试 status 不匹配
userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 测试 dept 不匹配
userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setDeptId(0L)));
return dbUser;
}
/**
* 情况一,校验不通过,导致插入失败
*/
@Test
public void testImportUsers_01() {
// 准备参数
SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class);
// mock 方法
// 调用
SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况二,不存在,进行插入
*/
@Test
public void testImportUsers_02() {
// 准备参数
SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围
});
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java");
// 调用
SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(1, respVO.getCreateUsernames().size());
SysUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals("java", user.getPassword());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(0, respVO.getFailureUsernames().size());
}
/**
* 情况三,存在,但是不强制更新
*/
@Test
public void testImportUsers_03() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), false);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况四,存在,强制更新
*/
@Test
public void testImportUsers_04() {
// mock 数据
SysUserDO dbUser = randomSysUserDO();
userMapper.insert(dbUser);
// 准备参数
SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(1, respVO.getUpdateUsernames().size());
SysUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals(0, respVO.getFailureUsernames().size());
}
@Test
public void testCheckUserExists_notExists() {
assertServiceException(() -> userService.checkUserExists(randomLongId()), USER_NOT_EXISTS);
}
@Test
public void testCheckUsernameUnique_usernameExistsForCreate() {
// 准备参数
String username = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.checkUsernameUnique(null, username),
USER_USERNAME_EXISTS);
}
@Test
public void testCheckUsernameUnique_usernameExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String username = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.checkUsernameUnique(id, username),
USER_USERNAME_EXISTS);
}
@Test
public void testCheckEmailUnique_emailExistsForCreate() {
// 准备参数
String email = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.checkEmailUnique(null, email),
USER_EMAIL_EXISTS);
}
@Test
public void testCheckEmailUnique_emailExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String email = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.checkEmailUnique(id, email),
USER_EMAIL_EXISTS);
}
@Test
public void testCheckMobileUnique_mobileExistsForCreate() {
// 准备参数
String mobile = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.checkMobileUnique(null, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testCheckMobileUnique_mobileExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String mobile = randomString();
// mock 数据
userMapper.insert(randomSysUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.checkMobileUnique(id, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testCheckDeptEnable_notFound() {
assertServiceException(() -> userService.checkDeptEnable(randomLongId()),
DEPT_NOT_FOUND);
}
@Test
public void testCheckDeptEnable_notEnable() {
// 准备参数
Long deptId = randomLongId();
// mock deptService 的方法
SysDeptDO dept = randomPojo(SysDeptDO.class, o -> {
o.setId(deptId);
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用,校验异常
assertServiceException(() -> userService.checkDeptEnable(deptId),
DEPT_NOT_ENABLE);
}
@Test
public void testCheckPostEnable_notFound() {
assertServiceException(() -> userService.checkPostEnable(randomSet(Long.class)),
POST_NOT_FOUND);
}
@Test
public void testCheckPostEnable_notEnable() {
// 准备参数
Set<Long> postIds = randomSet(Long.class);
// mock postService 的方法
List<SysPostDO> posts = CollectionUtils.convertList(postIds, postId ->
randomPojo(SysPostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
}));
when(postService.getPosts(eq(postIds), isNull())).thenReturn(posts);
// 调用,校验异常
assertServiceException(() -> userService.checkPostEnable(postIds),
POST_NOT_ENABLE, CollUtil.getFirst(posts).getName());
}
@Test
public void testCheckOldPassword_notExists() {
assertServiceException(() -> userService.checkOldPassword(randomLongId(), randomString()),
USER_NOT_EXISTS);
}
@Test
public void testCheckOldPassword_passwordFailed() {
// mock 数据
SysUserDO user = randomSysUserDO();
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String oldPassword = user.getPassword();
// 调用,校验异常
assertServiceException(() -> userService.checkOldPassword(id, oldPassword),
USER_PASSWORD_FAILED);
// 校验调用
verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword()));
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysUserDO randomSysUserDO(Consumer<SysUserDO>... consumers) {
Consumer<SysUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围
};
return randomPojo(SysUserDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -0,0 +1,97 @@
package cn.iocoder.dashboard.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.dashboard.common.exception.ErrorCode;
import cn.iocoder.dashboard.common.exception.ServiceException;
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* 单元测试assert 断言工具类
*
* @author 芋道源码
*/
public class AssertUtils {
/**
* 比对两个对象的属性是否一致
*
* 注意,如果 expected 存在的属性actual 不存在的时候,会进行忽略
*
* @param expected 期望对象
* @param actual 实际对象
* @param ignoreFields 忽略的属性数组
*/
public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) {
Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
Arrays.stream(expectedFields).forEach(expectedField -> {
// 如果是忽略的属性,则不进行比对
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {
return;
}
// 忽略不存在的属性
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName());
if (actualField == null) {
return;
}
// 比对
Assertions.assertEquals(
ReflectUtil.getFieldValue(expected, expectedField),
ReflectUtil.getFieldValue(actual, actualField),
String.format("Field(%s) 不匹配", expectedField.getName())
);
});
}
/**
* 比对两个对象的属性是否一致
*
* 注意,如果 expected 存在的属性actual 不存在的时候,会进行忽略
*
* @param expected 期望对象
* @param actual 实际对象
* @param ignoreFields 忽略的属性数组
* @return 是否一致
*/
public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) {
Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
return Arrays.stream(expectedFields).allMatch(expectedField -> {
// 如果是忽略的属性,则不进行比对
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) {
return true;
}
// 忽略不存在的属性
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName());
if (actualField == null) {
return true;
}
return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField),
ReflectUtil.getFieldValue(actual, actualField));
});
}
/**
* 执行方法,校验抛出的 Service 是否符合条件
*
* @param executable 业务异常
* @param errorCode 错误码对象
* @param messageParams 消息参数
*/
public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) {
// 调用方法
ServiceException serviceException = assertThrows(ServiceException.class, executable);
// 校验错误码
Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配");
String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams);
Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配");
}
}

View File

@@ -0,0 +1,106 @@
package cn.iocoder.dashboard.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import uk.co.jemos.podam.api.PodamFactory;
import uk.co.jemos.podam.api.PodamFactoryImpl;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 随机工具类
*
* @author 芋道源码
*/
public class RandomUtils {
private static final int RANDOM_STRING_LENGTH = 10;
private static final int RANDOM_DATE_MAX = 30;
private static final int RANDOM_COLLECTION_LENGTH = 5;
private static final PodamFactory PODAM_FACTORY = new PodamFactoryImpl();
static {
// 字符串
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class,
(dataProviderStrategy, attributeMetadata, map) -> randomString());
// Boolean
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> {
// 如果是 deleted 的字段,返回非删除
if (attributeMetadata.getAttributeName().equals("deleted")) {
return false;
}
return RandomUtil.randomBoolean();
});
}
public static String randomString() {
return RandomUtil.randomString(RANDOM_STRING_LENGTH);
}
public static Long randomLongId() {
return RandomUtil.randomLong(0, Long.MAX_VALUE);
}
public static Integer randomInteger() {
return RandomUtil.randomInt(0, Integer.MAX_VALUE);
}
public static Date randomDate() {
return RandomUtil.randomDay(0, RANDOM_DATE_MAX);
}
public static Short randomShort() {
return (short) RandomUtil.randomInt(0, Short.MAX_VALUE);
}
public static <T> Set<T> randomSet(Class<T> clazz) {
return Stream.iterate(0, i -> i).limit(RandomUtil.randomInt(0, RANDOM_DATE_MAX))
.map(i -> randomPojo(clazz)).collect(Collectors.toSet());
}
public static Integer randomCommonStatus() {
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
}
@SafeVarargs
public static SysUserDO randomUserDO(Consumer<SysUserDO>... consumers) {
return randomPojo(SysUserDO.class, consumers);
}
@SafeVarargs
public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
T pojo = PODAM_FACTORY.manufacturePojo(clazz);
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
if (ArrayUtil.isNotEmpty(consumers)) {
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo));
}
return pojo;
}
@SafeVarargs
public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) {
T pojo = PODAM_FACTORY.manufacturePojo(clazz, type);
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
if (ArrayUtil.isNotEmpty(consumers)) {
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo));
}
return pojo;
}
@SafeVarargs
public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) {
int size = RandomUtil.randomInt(0, RANDOM_COLLECTION_LENGTH);
return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers))
.collect(Collectors.toList());
}
}