再次调整项目结构

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());
}
}

View File

@@ -0,0 +1,82 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 定时任务相关配置 ####################
--- #################### 配置中心相关配置 ####################
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
# Resilience4j 配置项
resilience4j:
ratelimiter:
instances:
backendA:
limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
register-health-indicator: true # 是否注册到健康监测
--- #################### 监控相关配置 ####################
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
info:
version: 1.0.0
base-package: cn.iocoder.dashboard
web:
api-prefix: /api
controller-package: ${yudao.info.base-package}
security:
token-header: Authorization
token-secret: abcdefghijklmnopqrstuvwxyz
token-timeout: 1d
session-timeout: 30m
mock-enable: true
mock-secret: test
swagger:
enable: false # 单元测试,禁用 Swagger
captcha:
timeout: 5m
width: 160
height: 60
file:
base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/
codegen:
base-package: ${yudao.info.base-package}.modules
db-schemas: ${spring.datasource.name}
xss:
enable: false
exclude-urls: # 如下两个 url仅仅是为了演示去掉配置也没关系
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,25 @@
-- inf 开头的 DB
DELETE FROM "inf_config";
DELETE FROM "inf_file";
DELETE FROM "inf_job";
DELETE FROM "inf_job_log";
DELETE FROM "inf_api_access_log";
DELETE FROM "inf_api_error_log";
-- sys 开头的 DB
DELETE FROM "sys_dept";
DELETE FROM "sys_dict_data";
DELETE FROM "sys_role";
DELETE FROM "sys_role_menu";
DELETE FROM "sys_menu";
DELETE FROM "sys_user_role";
DELETE FROM "sys_dict_type";
DELETE FROM "sys_user_session";
DELETE FROM "sys_post";
DELETE FROM "sys_login_log";
DELETE FROM "sys_operate_log";
DELETE FROM "sys_user";
DELETE FROM "sys_sms_channel";
DELETE FROM "sys_sms_template";
DELETE FROM "sys_sms_log";
DELETE FROM "sys_error_code";

View File

@@ -0,0 +1,428 @@
-- inf 开头的 DB
CREATE TABLE IF NOT EXISTS "inf_config" (
"id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"group" varchar(50) NOT NULL,
"type" tinyint NOT NULL,
"name" varchar(100) NOT NULL DEFAULT '',
"key" varchar(100) NOT NULL DEFAULT '',
"value" varchar(500) NOT NULL DEFAULT '',
"sensitive" bit NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '参数配置表';
CREATE TABLE IF NOT EXISTS "inf_file" (
"id" varchar(188) NOT NULL,
"type" varchar(63) DEFAULT NULL,
"content" blob NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '文件表';
CREATE TABLE IF NOT EXISTS "inf_job" (
"id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号',
"name" varchar(32) NOT NULL COMMENT '任务名称',
"status" tinyint(4) NOT NULL COMMENT '任务状态',
"handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
"handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
"cron_expression" varchar(32) NOT NULL COMMENT 'CRON 表达式',
"retry_count" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数',
"retry_interval" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔',
"monitor_timeout" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间',
"creator" varchar(64) DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit NOT NULL DEFAULT FALSE COMMENT '是否删除',
PRIMARY KEY ("id")
) COMMENT='定时任务表';
CREATE TABLE IF NOT EXISTS "inf_job_log" (
"id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号',
"job_id" bigint(20) NOT NULL COMMENT '任务编号',
"handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
"handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
"execute_index" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行',
"begin_time" datetime NOT NULL COMMENT '开始执行时间',
"end_time" datetime DEFAULT NULL COMMENT '结束执行时间',
"duration" int(11) DEFAULT NULL COMMENT '执行时长',
"status" tinyint(4) NOT NULL COMMENT '任务状态',
"result" varchar(4000) DEFAULT '' COMMENT '结果数据',
"creator" varchar(64) DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除',
PRIMARY KEY ("id")
)COMMENT='定时任务日志表';
-- sys 开头的 DB
CREATE TABLE IF NOT EXISTS "sys_dept" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL DEFAULT '',
"parent_id" bigint NOT NULL DEFAULT '0',
"sort" int NOT NULL DEFAULT '0',
"leader" varchar(20) DEFAULT NULL,
"phone" varchar(11) DEFAULT NULL,
"email" varchar(50) DEFAULT NULL,
"status" tinyint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '部门表';
CREATE TABLE IF NOT EXISTS "sys_dict_data" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"sort" int NOT NULL DEFAULT '0',
"label" varchar(100) NOT NULL DEFAULT '',
"value" varchar(100) NOT NULL DEFAULT '',
"dict_type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '字典数据表';
CREATE TABLE IF NOT EXISTS "sys_role" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL,
"code" varchar(100) NOT NULL,
"sort" int NOT NULL,
"data_scope" tinyint NOT NULL DEFAULT '1',
"data_scope_dept_ids" varchar(500) NOT NULL DEFAULT '',
"status" tinyint NOT NULL,
"type" tinyint NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '角色信息表';
CREATE TABLE IF NOT EXISTS "sys_role_menu" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"role_id" bigint NOT NULL,
"menu_id" bigint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '角色和菜单关联表';
CREATE TABLE IF NOT EXISTS "sys_menu" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(50) NOT NULL,
"permission" varchar(100) NOT NULL DEFAULT '',
"menu_type" tinyint NOT NULL,
"sort" int NOT NULL DEFAULT '0',
"parent_id" bigint NOT NULL DEFAULT '0',
"path" varchar(200) DEFAULT '',
"icon" varchar(100) DEFAULT '#',
"component" varchar(255) DEFAULT NULL,
"status" tinyint NOT NULL DEFAULT '0',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '菜单权限表';
CREATE TABLE IF NOT EXISTS "sys_user_role" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"role_id" bigint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp DEFAULT NULL,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp DEFAULT NULL,
"deleted" bit DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '用户和角色关联表';
CREATE TABLE IF NOT EXISTS "sys_dict_type" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(100) NOT NULL DEFAULT '',
"type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '字典类型表';
CREATE TABLE IF NOT EXISTS `sys_user_session` (
`id` varchar(32) NOT NULL,
`user_id` bigint DEFAULT NULL,
`username` varchar(50) NOT NULL DEFAULT '',
`user_ip` varchar(50) DEFAULT NULL,
`user_agent` varchar(512) DEFAULT NULL,
`session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '' ,
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY (`id`)
) COMMENT '用户在线 Session';
CREATE TABLE IF NOT EXISTS "sys_post" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(64) NOT NULL,
"name" varchar(50) NOT NULL,
"sort" integer NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '岗位信息表';
CREATE TABLE IF NOT EXISTS "sys_notice" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"title" varchar(50) NOT NULL COMMENT '公告标题',
"content" text NOT NULL COMMENT '公告内容',
"notice_type" tinyint NOT NULL COMMENT '公告类型1通知 2公告',
"status" tinyint NOT NULL DEFAULT '0' COMMENT '公告状态0正常 1关闭',
"creator" varchar(64) DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY("id")
) COMMENT '通知公告表';
CREATE TABLE IF NOT EXISTS `sys_login_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`log_type` bigint(4) NOT NULL,
`trace_id` varchar(64) NOT NULL DEFAULT '',
`username` varchar(50) NOT NULL DEFAULT '',
`result` tinyint(4) NOT NULL,
`user_ip` varchar(50) NOT NULL,
`user_agent` varchar(512) NOT NULL,
`creator` varchar(64) DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) COMMENT ='系统访问记录';
CREATE TABLE IF NOT EXISTS `sys_operate_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`trace_id` varchar(64) NOT NULL DEFAULT '',
`user_id` bigint(20) NOT NULL,
`module` varchar(50) NOT NULL,
`name` varchar(50) NOT NULL,
`operate_type` bigint(4) NOT NULL DEFAULT '0',
`content` varchar(2000) NOT NULL DEFAULT '',
`exts` varchar(512) NOT NULL DEFAULT '',
`request_method` varchar(16) DEFAULT '',
`request_url` varchar(255) DEFAULT '',
`user_ip` varchar(50) DEFAULT NULL,
`user_agent` varchar(200) DEFAULT NULL,
`java_method` varchar(512) NOT NULL DEFAULT '',
`java_method_args` varchar(8000) DEFAULT '',
`start_time` datetime NOT NULL,
`duration` int(11) NOT NULL,
`result_code` int(11) NOT NULL DEFAULT '0',
`result_msg` varchar(512) DEFAULT '',
`result_data` varchar(4000) DEFAULT '',
`creator` varchar(64) DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) COMMENT ='操作日志记录';
CREATE TABLE IF NOT EXISTS "sys_user" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"username" varchar(30) not null,
"password" varchar(100) not null default '',
"nickname" varchar(30) not null,
"remark" varchar(500) default null,
"dept_id" bigint default null,
"post_ids" varchar(255) default null,
"email" varchar(50) default '',
"mobile" varchar(11) default '',
"sex" tinyint default '0',
"avatar" varchar(100) default '',
"status" tinyint not null default '0',
"login_ip" varchar(50) default '',
"login_date" timestamp default null,
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
primary key ("id")
) comment '用户信息表';
CREATE TABLE IF NOT EXISTS "inf_api_access_log" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"trace_id" varchar(64) not null default '',
"user_id" bigint not null default '0',
"user_type" tinyint not null default '0',
"application_name" varchar(50) not null,
"request_method" varchar(16) not null default '',
"request_url" varchar(255) not null default '',
"request_params" varchar(8000) not null default '',
"user_ip" varchar(50) not null,
"user_agent" varchar(512) not null,
"begin_time" timestamp not null,
"end_time" timestamp not null,
"duration" integer not null,
"result_code" integer not null default '0',
"result_msg" varchar(512) default '',
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
primary key ("id")
) COMMENT 'API 访问日志表';
CREATE TABLE IF NOT EXISTS "inf_api_error_log" (
"id" integer not null GENERATED BY DEFAULT AS IDENTITY,
"trace_id" varchar(64) not null,
"user_id" bigint not null default '0',
"user_type" tinyint not null default '0',
"application_name" varchar(50) not null,
"request_method" varchar(16) not null,
"request_url" varchar(255) not null,
"request_params" varchar(8000) not null,
"user_ip" varchar(50) not null,
"user_agent" varchar(512) not null,
"exception_time" timestamp not null,
"exception_name" varchar(128) not null default '',
"exception_message" clob not null,
"exception_root_cause_message" clob not null,
"exception_stack_trace" clob not null,
"exception_class_name" varchar(512) not null,
"exception_file_name" varchar(512) not null,
"exception_method_name" varchar(512) not null,
"exception_line_number" integer not null,
"process_status" tinyint not null,
"process_time" timestamp default null,
"process_user_id" bigint default '0',
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
primary key ("id")
) COMMENT '系统异常日志';
CREATE TABLE IF NOT EXISTS "sys_sms_channel" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"signature" varchar(10) NOT NULL,
"code" varchar(63) NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(255) DEFAULT NULL,
"api_key" varchar(63) NOT NULL,
"api_secret" varchar(63) DEFAULT NULL,
"callback_url" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信渠道';
CREATE TABLE IF NOT EXISTS "sys_sms_template" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL,
"status" tinyint NOT NULL,
"code" varchar(63) NOT NULL,
"name" varchar(63) NOT NULL,
"content" varchar(255) NOT NULL,
"params" varchar(255) NOT NULL,
"remark" varchar(255) DEFAULT NULL,
"api_template_id" varchar(63) NOT NULL,
"channel_id" bigint NOT NULL,
"channel_code" varchar(63) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信模板';
CREATE TABLE IF NOT EXISTS "sys_sms_log" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"channel_id" bigint NOT NULL,
"channel_code" varchar(63) NOT NULL,
"template_id" bigint NOT NULL,
"template_code" varchar(63) NOT NULL,
"template_type" tinyint NOT NULL,
"template_content" varchar(255) NOT NULL,
"template_params" varchar(255) NOT NULL,
"api_template_id" varchar(63) NOT NULL,
"mobile" varchar(11) NOT NULL,
"user_id" bigint DEFAULT '0',
"user_type" tinyint DEFAULT '0',
"send_status" tinyint NOT NULL DEFAULT '0',
"send_time" timestamp DEFAULT NULL,
"send_code" int DEFAULT NULL,
"send_msg" varchar(255) DEFAULT NULL,
"api_send_code" varchar(63) DEFAULT NULL,
"api_send_msg" varchar(255) DEFAULT NULL,
"api_request_id" varchar(255) DEFAULT NULL,
"api_serial_no" varchar(255) DEFAULT NULL,
"receive_status" tinyint NOT NULL DEFAULT '0',
"receive_time" timestamp DEFAULT NULL,
"api_receive_code" varchar(63) DEFAULT NULL,
"api_receive_msg" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信日志';
CREATE TABLE IF NOT EXISTS "sys_error_code" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL DEFAULT '0',
"application_name" varchar(50) NOT NULL,
"code" int NOT NULL DEFAULT '0',
"message" varchar(512) NOT NULL DEFAULT '',
"memo" varchar(512) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '错误码表';