初始化提交
This commit is contained in:
		| @ -0,0 +1,337 @@ | ||||
| package cn.iocoder.yudao.module.system.controller.admin.oauth2; | ||||
|  | ||||
| import cn.hutool.core.collection.ListUtil; | ||||
| import cn.hutool.core.date.LocalDateTimeUtil; | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.core.KeyValue; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.exception.ErrorCode; | ||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; | ||||
| import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; | ||||
| import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; | ||||
| import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; | ||||
| import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | ||||
| import static java.util.Arrays.asList; | ||||
| import static org.hamcrest.CoreMatchers.anyOf; | ||||
| import static org.hamcrest.CoreMatchers.is; | ||||
| import static org.hamcrest.MatcherAssert.assertThat; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.ArgumentMatchers.isNull; | ||||
| import static org.mockito.Mockito.mock; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2OpenController} 的单元测试 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private OAuth2OpenController oauth2OpenController; | ||||
|  | ||||
|     @Mock | ||||
|     private OAuth2GrantService oauth2GrantService; | ||||
|     @Mock | ||||
|     private OAuth2ClientService oauth2ClientService; | ||||
|     @Mock | ||||
|     private OAuth2ApproveService oauth2ApproveService; | ||||
|     @Mock | ||||
|     private OAuth2TokenService oauth2TokenService; | ||||
|  | ||||
|     @Test | ||||
|     public void testPostAccessToken_authorizationCode() { | ||||
|         // 准备参数 | ||||
|         String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType(); | ||||
|         String code = randomString(); | ||||
|         String redirectUri = randomString(); | ||||
|         String state = randomString(); | ||||
|         HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client); | ||||
|  | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); | ||||
|         when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"), | ||||
|                 eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | ||||
|                 code, redirectUri, state, null, null, null, null); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertPojoEquals(accessTokenDO, result.getData()); | ||||
|         assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L));  // 执行过程会过去几毫秒 | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testPostAccessToken_password() { | ||||
|         // 准备参数 | ||||
|         String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType(); | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|         String scope = "write read"; | ||||
|         HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), | ||||
|                 eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client); | ||||
|  | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); | ||||
|         when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"), | ||||
|                 eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | ||||
|                 null, null, null, username, password, scope, null); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertPojoEquals(accessTokenDO, result.getData()); | ||||
|         assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L));  // 执行过程会过去几毫秒 | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testPostAccessToken_refreshToken() { | ||||
|         // 准备参数 | ||||
|         String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType(); | ||||
|         String refreshToken = randomString(); | ||||
|         String password = randomString(); | ||||
|         HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), | ||||
|                 eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client); | ||||
|  | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); | ||||
|         when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | ||||
|                 null, null, null, null, password, null, refreshToken); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertPojoEquals(accessTokenDO, result.getData()); | ||||
|         assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L));  // 执行过程会过去几毫秒 | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testPostAccessToken_implicit() { | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2OpenController.postAccessToken(null, | ||||
|                         OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null, | ||||
|                         null, null, null, null), | ||||
|                 new ErrorCode(400, "Token 接口不支持 implicit 授权模式")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRevokeToken() { | ||||
|         // 准备参数 | ||||
|         HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); | ||||
|         String token = randomString(); | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id"); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"), | ||||
|                 eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client); | ||||
|         // mock 方法(移除) | ||||
|         when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<Boolean> result = oauth2OpenController.revokeToken(request, token); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertTrue(result.getData()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckToken() { | ||||
|         // 准备参数 | ||||
|         HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); | ||||
|         String token = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(LocalDateTimeUtil.of(1653485731195L)); | ||||
|         when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<OAuth2OpenCheckTokenRespVO> result = oauth2OpenController.checkToken(request, token); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertPojoEquals(accessTokenDO, result.getData()); | ||||
|         assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒 | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthorize() { | ||||
|         // 准备参数 | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all")); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client); | ||||
|         // mock 方法(approve) | ||||
|         List<OAuth2ApproveDO> approves = asList( | ||||
|                 randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true), | ||||
|                 randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false)); | ||||
|         when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<OAuth2OpenAuthorizeInfoRespVO> result = oauth2OpenController.authorize(clientId); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertPojoEquals(client, result.getData().getClient()); | ||||
|         assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0)); | ||||
|         assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1)); | ||||
|         assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testApproveOrDeny_grantTypeError() { | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null, | ||||
|                         null, null, null, null), | ||||
|                 new ErrorCode(400, "response_type 参数值只允许 code 和 token")); | ||||
|     } | ||||
|  | ||||
|     @Test // autoApprove = true,但是不通过 | ||||
|     public void testApproveOrDeny_autoApproveNo() { | ||||
|         // 准备参数 | ||||
|         String responseType = "code"; | ||||
|         String clientId = randomString(); | ||||
|         String scope = "{\"read\": true, \"write\": false}"; | ||||
|         String redirectUri = randomString(); | ||||
|         String state = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), | ||||
|                 eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | ||||
|                 scope, redirectUri, true, state); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertNull(result.getData()); | ||||
|     } | ||||
|  | ||||
|     @Test // autoApprove = false,但是不通过 | ||||
|     public void testApproveOrDeny_ApproveNo() { | ||||
|         // 准备参数 | ||||
|         String responseType = "token"; | ||||
|         String clientId = randomString(); | ||||
|         String scope = "{\"read\": true, \"write\": false}"; | ||||
|         String redirectUri = "https://www.iocoder.cn"; | ||||
|         String state = "test"; | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), | ||||
|                 eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | ||||
|                 scope, redirectUri, false, state); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData()); | ||||
|     } | ||||
|  | ||||
|     @Test // autoApprove = true,通过 + token | ||||
|     public void testApproveOrDeny_autoApproveWithToken() { | ||||
|         // 准备参数 | ||||
|         String responseType = "token"; | ||||
|         String clientId = randomString(); | ||||
|         String scope = "{\"read\": true, \"write\": false}"; | ||||
|         String redirectUri = "https://www.iocoder.cn"; | ||||
|         String state = "test"; | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), | ||||
|                 eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | ||||
|         // mock 方法(场景一) | ||||
|         when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | ||||
|                 eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true); | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setAccessToken("test_access_token").setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30010L, ChronoUnit.MILLIS)); | ||||
|         when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | ||||
|                 eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | ||||
|                 scope, redirectUri, true, state); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算 | ||||
|                 is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"), | ||||
|                 is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read") | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     @Test // autoApprove = false,通过 + code | ||||
|     public void testApproveOrDeny_approveWithCode() { | ||||
|         // 准备参数 | ||||
|         String responseType = "code"; | ||||
|         String clientId = randomString(); | ||||
|         String scope = "{\"read\": true, \"write\": false}"; | ||||
|         String redirectUri = "https://www.iocoder.cn"; | ||||
|         String state = "test"; | ||||
|         // mock 方法(client) | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), | ||||
|                 eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | ||||
|         // mock 方法(场景二) | ||||
|         when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId), | ||||
|                 eq(MapUtil.builder(new LinkedHashMap<String, Boolean>()).put("read", true).put("write", false).build()))) | ||||
|                 .thenReturn(true); | ||||
|         // mock 方法(访问令牌) | ||||
|         String authorizationCode = "test_code"; | ||||
|         when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | ||||
|                 eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode); | ||||
|  | ||||
|         // 调用 | ||||
|         CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | ||||
|                 scope, redirectUri, false, state); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.getCode()); | ||||
|         assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData()); | ||||
|     } | ||||
|  | ||||
|     private HttpServletRequest mockRequest(String clientId, String secret) { | ||||
|         HttpServletRequest request = mock(HttpServletRequest.class); | ||||
|         when(request.getParameter(eq("client_id"))).thenReturn(clientId); | ||||
|         when(request.getParameter(eq("client_secret"))).thenReturn(secret); | ||||
|         return request; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,187 @@ | ||||
| package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; | ||||
|  | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.iocoder.yudao.framework.common.core.KeyValue; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; | ||||
| 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.google.common.collect.Lists; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.ArgumentMatcher; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| 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 | ||||
|     public void tesSendSms_success() 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 方法 | ||||
|         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); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, | ||||
|                 apiTemplateId, templateParams); | ||||
|         // 断言 | ||||
|         assertTrue(result.getSuccess()); | ||||
|         assertEquals(response.getRequestId(), result.getApiRequestId()); | ||||
|         assertEquals(response.getCode(), result.getApiCode()); | ||||
|         assertEquals(response.getMessage(), result.getApiMsg()); | ||||
|         assertEquals(response.getBizId(), result.getSerialNo()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void tesSendSms_fail() 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 方法 | ||||
|         SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("ERROR")); | ||||
|         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); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); | ||||
|         // 断言 | ||||
|         assertFalse(result.getSuccess()); | ||||
|         assertEquals(response.getRequestId(), result.getApiRequestId()); | ||||
|         assertEquals(response.getCode(), result.getApiCode()); | ||||
|         assertEquals(response.getMessage(), result.getApiMsg()); | ||||
|         assertEquals(response.getBizId(), result.getSerialNo()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testParseSmsReceiveStatus() { | ||||
|         // 准备参数 | ||||
|         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.parseSmsReceiveStatus(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(LocalDateTime.of(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 testGetSmsTemplate() throws Throwable { | ||||
|         // 准备参数 | ||||
|         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); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); | ||||
|         // 断言 | ||||
|         assertEquals(response.getTemplateCode(), result.getId()); | ||||
|         assertEquals(response.getTemplateContent(), result.getContent()); | ||||
|         assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); | ||||
|         assertEquals(response.getReason(), result.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)"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,230 @@ | ||||
| package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; | ||||
|  | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.iocoder.yudao.framework.common.core.KeyValue; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; | ||||
| import com.google.common.collect.Lists; | ||||
| import com.tencentcloudapi.sms.v20210111.SmsClient; | ||||
| import com.tencentcloudapi.sms.v20210111.models.DescribeSmsTemplateListResponse; | ||||
| import com.tencentcloudapi.sms.v20210111.models.DescribeTemplateListStatus; | ||||
| import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; | ||||
| import com.tencentcloudapi.sms.v20210111.models.SendStatus; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.argThat; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link TencentSmsClient} 的单元测试 | ||||
|  * | ||||
|  * @author shiwp | ||||
|  */ | ||||
| public class TencentSmsClientTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     private final SmsChannelProperties properties = new SmsChannelProperties() | ||||
|             .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 | ||||
|             .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 | ||||
|             .setSignature("芋道源码"); | ||||
|  | ||||
|     @InjectMocks | ||||
|     private TencentSmsClient smsClient = new TencentSmsClient(properties); | ||||
|  | ||||
|     @Mock | ||||
|     private SmsClient client; | ||||
|  | ||||
|     @Test | ||||
|     public void testDoInit() { | ||||
|         // 准备参数 | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         smsClient.doInit(); | ||||
|         // 断言 | ||||
|         assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefresh() { | ||||
|         // 准备参数 | ||||
|         SmsChannelProperties p = new SmsChannelProperties() | ||||
|                 .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 | ||||
|                 .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 | ||||
|                 .setSignature("芋道源码"); | ||||
|         // 调用 | ||||
|         smsClient.refresh(p); | ||||
|         // 断言 | ||||
|         assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDoSendSms_success() throws Throwable { | ||||
|         // 准备参数 | ||||
|         Long sendLogId = randomLongId(); | ||||
|         String mobile = randomString(); | ||||
|         String apiTemplateId = randomString(); | ||||
|         List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | ||||
|                 new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); | ||||
|         String requestId = randomString(); | ||||
|         String serialNo = randomString(); | ||||
|         // mock 方法 | ||||
|         SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> { | ||||
|             o.setRequestId(requestId); | ||||
|             SendStatus[] sendStatuses = new SendStatus[1]; | ||||
|             o.setSendStatusSet(sendStatuses); | ||||
|             SendStatus sendStatus = new SendStatus(); | ||||
|             sendStatuses[0] = sendStatus; | ||||
|             sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS); | ||||
|             sendStatus.setMessage("send success"); | ||||
|             sendStatus.setSerialNo(serialNo); | ||||
|         }); | ||||
|         when(client.SendSms(argThat(request -> { | ||||
|             assertEquals(mobile, request.getPhoneNumberSet()[0]); | ||||
|             assertEquals(properties.getSignature(), request.getSignName()); | ||||
|             assertEquals(apiTemplateId, request.getTemplateId()); | ||||
|             assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)), | ||||
|                     toJsonString(request.getTemplateParamSet())); | ||||
|             assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId")); | ||||
|             return true; | ||||
|         }))).thenReturn(response); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); | ||||
|         // 断言 | ||||
|         assertTrue(result.getSuccess()); | ||||
|         assertEquals(response.getRequestId(), result.getApiRequestId()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDoSendSms_fail() throws Throwable { | ||||
|         // 准备参数 | ||||
|         Long sendLogId = randomLongId(); | ||||
|         String mobile = randomString(); | ||||
|         String apiTemplateId = randomString(); | ||||
|         List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | ||||
|                 new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); | ||||
|         String requestId = randomString(); | ||||
|         String serialNo = randomString(); | ||||
|         // mock 方法 | ||||
|         SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> { | ||||
|             o.setRequestId(requestId); | ||||
|             SendStatus[] sendStatuses = new SendStatus[1]; | ||||
|             o.setSendStatusSet(sendStatuses); | ||||
|             SendStatus sendStatus = new SendStatus(); | ||||
|             sendStatuses[0] = sendStatus; | ||||
|             sendStatus.setCode("ERROR"); | ||||
|             sendStatus.setMessage("send success"); | ||||
|             sendStatus.setSerialNo(serialNo); | ||||
|         }); | ||||
|         when(client.SendSms(argThat(request -> { | ||||
|             assertEquals(mobile, request.getPhoneNumberSet()[0]); | ||||
|             assertEquals(properties.getSignature(), request.getSignName()); | ||||
|             assertEquals(apiTemplateId, request.getTemplateId()); | ||||
|             assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)), | ||||
|                     toJsonString(request.getTemplateParamSet())); | ||||
|             assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId")); | ||||
|             return true; | ||||
|         }))).thenReturn(response); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); | ||||
|         // 断言 | ||||
|         assertFalse(result.getSuccess()); | ||||
|         assertEquals(response.getRequestId(), result.getApiRequestId()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg()); | ||||
|         assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testParseSmsReceiveStatus() { | ||||
|         // 准备参数 | ||||
|         String text = "[\n" + | ||||
|                 "    {\n" + | ||||
|                 "        \"user_receive_time\": \"2015-10-17 08:03:04\",\n" + | ||||
|                 "        \"nationcode\": \"86\",\n" + | ||||
|                 "        \"mobile\": \"13900000001\",\n" + | ||||
|                 "        \"report_status\": \"SUCCESS\",\n" + | ||||
|                 "        \"errmsg\": \"DELIVRD\",\n" + | ||||
|                 "        \"description\": \"用户短信送达成功\",\n" + | ||||
|                 "        \"sid\": \"12345\",\n" + | ||||
|                 "        \"ext\": {\"logId\":\"67890\"}\n" + | ||||
|                 "    }\n" + | ||||
|                 "]"; | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text); | ||||
|         // 断言 | ||||
|         assertEquals(1, statuses.size()); | ||||
|         assertTrue(statuses.get(0).getSuccess()); | ||||
|         assertEquals("DELIVRD", statuses.get(0).getErrorCode()); | ||||
|         assertEquals("用户短信送达成功", statuses.get(0).getErrorMsg()); | ||||
|         assertEquals("13900000001", statuses.get(0).getMobile()); | ||||
|         assertEquals(LocalDateTime.of(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime()); | ||||
|         assertEquals("12345", statuses.get(0).getSerialNo()); | ||||
|         assertEquals(67890L, statuses.get(0).getLogId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsTemplate() throws Throwable { | ||||
|         // 准备参数 | ||||
|         Long apiTemplateId = randomLongId(); | ||||
|         String requestId = randomString(); | ||||
|  | ||||
|         // mock 方法 | ||||
|         DescribeSmsTemplateListResponse response = randomPojo(DescribeSmsTemplateListResponse.class, o -> { | ||||
|             DescribeTemplateListStatus[] describeTemplateListStatuses = new DescribeTemplateListStatus[1]; | ||||
|             DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus(); | ||||
|             templateStatus.setTemplateId(apiTemplateId); | ||||
|             templateStatus.setStatusCode(0L);// 设置模板通过 | ||||
|             describeTemplateListStatuses[0] = templateStatus; | ||||
|             o.setDescribeTemplateStatusSet(describeTemplateListStatuses); | ||||
|             o.setRequestId(requestId); | ||||
|         }); | ||||
|         when(client.DescribeSmsTemplateList(argThat(request -> { | ||||
|             assertEquals(apiTemplateId, request.getTemplateIdSet()[0]); | ||||
|             return true; | ||||
|         }))).thenReturn(response); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString()); | ||||
|         // 断言 | ||||
|         assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId()); | ||||
|         assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent()); | ||||
|         assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); | ||||
|         assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testConvertSmsTemplateAuditStatus() { | ||||
|         assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), | ||||
|                 smsClient.convertSmsTemplateAuditStatus(0)); | ||||
|         assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), | ||||
|                 smsClient.convertSmsTemplateAuditStatus(1)); | ||||
|         assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), | ||||
|                 smsClient.convertSmsTemplateAuditStatus(-1)); | ||||
|         assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), | ||||
|                 "未知审核状态(3)"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,372 @@ | ||||
| package cn.iocoder.yudao.module.system.service.auth; | ||||
|  | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; | ||||
| import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | ||||
| import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | ||||
| import cn.iocoder.yudao.module.system.service.member.MemberService; | ||||
| import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; | ||||
| import cn.iocoder.yudao.module.system.service.social.SocialUserService; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| import com.xingyuv.captcha.model.common.ResponseModel; | ||||
| import com.xingyuv.captcha.service.CaptchaService; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| 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 javax.validation.ConstraintViolationException; | ||||
| import javax.validation.Validation; | ||||
| import javax.validation.Validator; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| @Import(AdminAuthServiceImpl.class) | ||||
| public class AdminAuthServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private AdminAuthServiceImpl authService; | ||||
|  | ||||
|     @MockBean | ||||
|     private AdminUserService userService; | ||||
|     @MockBean | ||||
|     private CaptchaService captchaService; | ||||
|     @MockBean | ||||
|     private LoginLogService loginLogService; | ||||
|     @MockBean | ||||
|     private SocialUserService socialUserService; | ||||
|     @MockBean | ||||
|     private SmsCodeApi smsCodeApi; | ||||
|     @MockBean | ||||
|     private OAuth2TokenService oauth2TokenService; | ||||
|     @MockBean | ||||
|     private MemberService memberService; | ||||
|     @MockBean | ||||
|     private Validator validator; | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void setUp() { | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", true); | ||||
|         // 注入一个 Validator 对象 | ||||
|         ReflectUtil.setFieldValue(authService, "validator", | ||||
|                 Validation.buildDefaultValidatorFactory().getValidator()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthenticate_success() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|         // mock user 数据 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||||
|                 .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(userService.getUserByUsername(eq(username))).thenReturn(user); | ||||
|         // mock password 匹配 | ||||
|         when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); | ||||
|  | ||||
|         // 调用 | ||||
|         AdminUserDO loginUser = authService.authenticate(username, password); | ||||
|         // 校验 | ||||
|         assertPojoEquals(user, loginUser); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthenticate_userNotFound() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> authService.authenticate(username, password), | ||||
|                 AUTH_LOGIN_BAD_CREDENTIALS); | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) | ||||
|                         && o.getUserId() == null) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthenticate_badCredentials() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|         // mock user 数据 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||||
|                 .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(userService.getUserByUsername(eq(username))).thenReturn(user); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> authService.authenticate(username, password), | ||||
|                 AUTH_LOGIN_BAD_CREDENTIALS); | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) | ||||
|                         && o.getUserId().equals(user.getId())) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthenticate_userDisabled() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|         // mock user 数据 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||||
|                 .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         when(userService.getUserByUsername(eq(username))).thenReturn(user); | ||||
|         // mock password 匹配 | ||||
|         when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> authService.authenticate(username, password), | ||||
|                 AUTH_LOGIN_USER_DISABLED); | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()) | ||||
|                         && o.getUserId().equals(user.getId())) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLogin_success() { | ||||
|         // 准备参数 | ||||
|         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> | ||||
|                 o.setUsername("test_username").setPassword("test_password") | ||||
|                         .setSocialType(randomEle(SocialTypeEnum.values()).getType())); | ||||
|  | ||||
|         // mock 验证码正确 | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", false); | ||||
|         // mock user 数据 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username") | ||||
|                 .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(userService.getUserByUsername(eq("test_username"))).thenReturn(user); | ||||
|         // mock password 匹配 | ||||
|         when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true); | ||||
|         // mock 缓存登录用户到 Redis | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||||
|                 .setUserType(UserTypeEnum.ADMIN.getValue())); | ||||
|         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) | ||||
|                 .thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并校验 | ||||
|         AuthLoginRespVO loginRespVO = authService.login(reqVO); | ||||
|         assertPojoEquals(accessTokenDO, loginRespVO); | ||||
|         // 校验调用参数 | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) | ||||
|                         && o.getUserId().equals(user.getId())) | ||||
|         ); | ||||
|         verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO( | ||||
|                 user.getId(), UserTypeEnum.ADMIN.getValue(), | ||||
|                 reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSmsCode() { | ||||
|         // 准备参数 | ||||
|         String mobile = randomString(); | ||||
|         Integer scene = randomEle(SmsSceneEnum.values()).getScene(); | ||||
|         AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene); | ||||
|         // mock 方法(用户信息) | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class); | ||||
|         when(userService.getUserByMobile(eq(mobile))).thenReturn(user); | ||||
|  | ||||
|         // 调用 | ||||
|         authService.sendSmsCode(reqVO); | ||||
|         // 断言 | ||||
|         verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> { | ||||
|             assertEquals(mobile, sendReqDTO.getMobile()); | ||||
|             assertEquals(scene, sendReqDTO.getScene()); | ||||
|             return true; | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSmsLogin_success() { | ||||
|         // 准备参数 | ||||
|         String mobile = randomString(); | ||||
|         String code = randomString(); | ||||
|         AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code); | ||||
|         // mock 方法(用户信息) | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L)); | ||||
|         when(userService.getUserByMobile(eq(mobile))).thenReturn(user); | ||||
|         // mock 缓存登录用户到 Redis | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||||
|                 .setUserType(UserTypeEnum.ADMIN.getValue())); | ||||
|         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) | ||||
|                 .thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO); | ||||
|         assertPojoEquals(accessTokenDO, loginRespVO); | ||||
|         // 断言调用 | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) | ||||
|                         && o.getUserId().equals(user.getId())) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSocialLogin_success() { | ||||
|         // 准备参数 | ||||
|         AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class); | ||||
|         // mock 方法(绑定的用户编号) | ||||
|         Long userId = 1L; | ||||
|         when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()), | ||||
|                 eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId)); | ||||
|         // mock(用户) | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); | ||||
|         when(userService.getUser(eq(userId))).thenReturn(user); | ||||
|         // mock 缓存登录用户到 Redis | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||||
|                 .setUserType(UserTypeEnum.ADMIN.getValue())); | ||||
|         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) | ||||
|                 .thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO); | ||||
|         assertPojoEquals(accessTokenDO, loginRespVO); | ||||
|         // 断言调用 | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) | ||||
|                         && o.getUserId().equals(user.getId())) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateCaptcha_successWithEnable() { | ||||
|         // 准备参数 | ||||
|         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||||
|  | ||||
|         // mock 验证码打开 | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", true); | ||||
|         // mock 验证通过 | ||||
|         when(captchaService.verification(argThat(captchaVO -> { | ||||
|             assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); | ||||
|             return true; | ||||
|         }))).thenReturn(ResponseModel.success()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         authService.validateCaptcha(reqVO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateCaptcha_successWithDisable() { | ||||
|         // 准备参数 | ||||
|         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||||
|  | ||||
|         // mock 验证码关闭 | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", false); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         authService.validateCaptcha(reqVO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateCaptcha_constraintViolationException() { | ||||
|         // 准备参数 | ||||
|         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null); | ||||
|  | ||||
|         // mock 验证码打开 | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", true); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertThrows(ConstraintViolationException.class, () -> authService.validateCaptcha(reqVO), | ||||
|                 "验证码不能为空"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Test | ||||
|     public void testCaptcha_fail() { | ||||
|         // 准备参数 | ||||
|         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||||
|  | ||||
|         // mock 验证码打开 | ||||
|         ReflectUtil.setFieldValue(authService, "captchaEnable", true); | ||||
|         // mock 验证通过 | ||||
|         when(captchaService.verification(argThat(captchaVO -> { | ||||
|             assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); | ||||
|             return true; | ||||
|         }))).thenReturn(ResponseModel.errorMsg("就是不对")); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对"); | ||||
|         // 校验调用参数 | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshToken() { | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default"))) | ||||
|                 .thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken); | ||||
|         // 断言 | ||||
|         assertPojoEquals(accessTokenDO, loginRespVO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLogout_success() { | ||||
|         // 准备参数 | ||||
|         String token = randomString(); | ||||
|         // mock | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||||
|                 .setUserType(UserTypeEnum.ADMIN.getValue())); | ||||
|         when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | ||||
|         // 校验调用参数 | ||||
|         verify(loginLogService).createLoginLog( | ||||
|                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) | ||||
|                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) | ||||
|         ); | ||||
|         // 调用,并校验 | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLogout_fail() { | ||||
|         // 准备参数 | ||||
|         String token = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | ||||
|         // 校验调用参数 | ||||
|         verify(loginLogService, never()).createLoginLog(any()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,296 @@ | ||||
| package cn.iocoder.yudao.module.system.service.dept; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link DeptServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author niudehua | ||||
|  */ | ||||
| @Import(DeptServiceImpl.class) | ||||
| public class DeptServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private DeptServiceImpl deptService; | ||||
|     @Resource | ||||
|     private DeptMapper deptMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateDept() { | ||||
|         // 准备参数 | ||||
|         DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { | ||||
|             o.setId(null); // 防止 id 被设置 | ||||
|             o.setParentId(DeptDO.PARENT_ID_ROOT); | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         Long deptId = deptService.createDept(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(deptId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         DeptDO deptDO = deptMapper.selectById(deptId); | ||||
|         assertPojoEquals(reqVO, deptDO, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateDept() { | ||||
|         // mock 数据 | ||||
|         DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); | ||||
|         deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { | ||||
|             // 设置更新的 ID | ||||
|             o.setParentId(DeptDO.PARENT_ID_ROOT); | ||||
|             o.setId(dbDeptDO.getId()); | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         deptService.updateDept(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, deptDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDept_success() { | ||||
|         // mock 数据 | ||||
|         DeptDO dbDeptDO = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDeptDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         deptService.deleteDept(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(deptMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDept_exitsChildren() { | ||||
|         // mock 数据 | ||||
|         DeptDO parentDept = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { | ||||
|             o.setParentId(parentDept.getId()); | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|         // 插入子部门 | ||||
|         deptMapper.insert(childrenDeptDO); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDeptExists_notFound() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentDept_parentError() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateParentDept(id, id), | ||||
|                 DEPT_PARENT_ERROR); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentDept_parentIsChild() { | ||||
|         // mock 数据(父节点) | ||||
|         DeptDO parentDept = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(parentDept); | ||||
|         // mock 数据(子节点) | ||||
|         DeptDO childDept = randomPojo(DeptDO.class, o -> { | ||||
|             o.setParentId(parentDept.getId()); | ||||
|         }); | ||||
|         deptMapper.insert(childDept); | ||||
|  | ||||
|         // 准备参数 | ||||
|         Long id = parentDept.getId(); | ||||
|         Long parentId = childDept.getId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateNameUnique_duplicate() { | ||||
|         // mock 数据 | ||||
|         DeptDO deptDO = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(deptDO); | ||||
|  | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         Long parentId = deptDO.getParentId(); | ||||
|         String name = deptDO.getName(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name), | ||||
|                 DEPT_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDept() { | ||||
|         // mock 数据 | ||||
|         DeptDO deptDO = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(deptDO); | ||||
|         // 准备参数 | ||||
|         Long id = deptDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         DeptDO dbDept = deptService.getDept(id); | ||||
|         // 断言 | ||||
|         assertEquals(deptDO, dbDept); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptList_ids() { | ||||
|         // mock 数据 | ||||
|         DeptDO deptDO01 = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(deptDO01); | ||||
|         DeptDO deptDO02 = randomPojo(DeptDO.class); | ||||
|         deptMapper.insert(deptDO02); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<DeptDO> deptDOList = deptService.getDeptList(ids); | ||||
|         // 断言 | ||||
|         assertEquals(2, deptDOList.size()); | ||||
|         assertEquals(deptDO01, deptDOList.get(0)); | ||||
|         assertEquals(deptDO02, deptDOList.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptList_reqVO() { | ||||
|         // mock 数据 | ||||
|         DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 | ||||
|             o.setName("开发部"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         deptMapper.insert(dept); | ||||
|         // 测试 name 不匹配 | ||||
|         deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("发"))); | ||||
|         // 测试 status 不匹配 | ||||
|         deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         DeptListReqVO reqVO = new DeptListReqVO(); | ||||
|         reqVO.setName("开"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<DeptDO> sysDeptDOS = deptService.getDeptList(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, sysDeptDOS.size()); | ||||
|         assertPojoEquals(dept, sysDeptDOS.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetChildDeptList() { | ||||
|         // mock 数据(1 级别子节点) | ||||
|         DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); | ||||
|         deptMapper.insert(dept1); | ||||
|         DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); | ||||
|         deptMapper.insert(dept2); | ||||
|         // mock 数据(2 级子节点) | ||||
|         DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); | ||||
|         deptMapper.insert(dept1a); | ||||
|         DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); | ||||
|         deptMapper.insert(dept2a); | ||||
|         // 准备参数 | ||||
|         Long id = dept1.getParentId(); | ||||
|  | ||||
|         // 调用 | ||||
|         List<DeptDO> result = deptService.getChildDeptList(id); | ||||
|         // 断言 | ||||
|         assertEquals(result.size(), 2); | ||||
|         assertPojoEquals(dept1, result.get(0)); | ||||
|         assertPojoEquals(dept1a, result.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetChildDeptListFromCache() { | ||||
|         // mock 数据(1 级别子节点) | ||||
|         DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); | ||||
|         deptMapper.insert(dept1); | ||||
|         DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); | ||||
|         deptMapper.insert(dept2); | ||||
|         // mock 数据(2 级子节点) | ||||
|         DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); | ||||
|         deptMapper.insert(dept1a); | ||||
|         DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); | ||||
|         deptMapper.insert(dept2a); | ||||
|         // 准备参数 | ||||
|         Long id = dept1.getParentId(); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = deptService.getChildDeptIdListFromCache(id); | ||||
|         // 断言 | ||||
|         assertEquals(result.size(), 2); | ||||
|         assertTrue(result.contains(dept1.getId())); | ||||
|         assertTrue(result.contains(dept1a.getId())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDeptList_success() { | ||||
|         // mock 数据 | ||||
|         DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         deptMapper.insert(deptDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(deptDO.getId()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         deptService.validateDeptList(ids); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDeptList_notFound() { | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(randomLongId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDeptList_notEnable() { | ||||
|         // mock 数据 | ||||
|         DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         deptMapper.insert(deptDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(deptDO.getId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,248 @@ | ||||
| package cn.iocoder.yudao.module.system.service.dept; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link PostServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author niudehua | ||||
|  */ | ||||
| @Import(PostServiceImpl.class) | ||||
| public class PostServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private PostServiceImpl postService; | ||||
|  | ||||
|     @Resource | ||||
|     private PostMapper postMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreatePost_success() { | ||||
|         // 准备参数 | ||||
|         PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, | ||||
|                 o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) | ||||
|                 .setId(null); // 防止 id 被设置 | ||||
|         // 调用 | ||||
|         Long postId = postService.createPost(reqVO); | ||||
|  | ||||
|         // 断言 | ||||
|         assertNotNull(postId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         PostDO post = postMapper.selectById(postId); | ||||
|         assertPojoEquals(reqVO, post, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdatePost_success() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO(); | ||||
|         postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { | ||||
|             // 设置更新的 ID | ||||
|             o.setId(postDO.getId()); | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         postService.updatePost(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         PostDO post = postMapper.selectById(reqVO.getId()); | ||||
|         assertPojoEquals(reqVO, post); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeletePost_success() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO(); | ||||
|         postMapper.insert(postDO); | ||||
|         // 准备参数 | ||||
|         Long id = postDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         postService.deletePost(id); | ||||
|         assertNull(postMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePost_notFoundForDelete() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePost_nameDuplicateForCreate() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO(); | ||||
|         postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, | ||||
|             // 模拟 name 重复 | ||||
|             o -> o.setName(postDO.getName())); | ||||
|         assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePost_codeDuplicateForUpdate() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO(); | ||||
|         postMapper.insert(postDO); | ||||
|         // mock 数据:稍后模拟重复它的 code | ||||
|         PostDO codePostDO = randomPostDO(); | ||||
|         postMapper.insert(codePostDO); | ||||
|         // 准备参数 | ||||
|         PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { | ||||
|             // 设置更新的 ID | ||||
|             o.setId(postDO.getId()); | ||||
|             // 模拟 code 重复 | ||||
|             o.setCode(codePostDO.getCode()); | ||||
|         }); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetPostPage() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPojo(PostDO.class, o -> { | ||||
|             o.setName("码仔"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         postMapper.insert(postDO); | ||||
|         // 测试 name 不匹配 | ||||
|         postMapper.insert(cloneIgnoreId(postDO, o -> o.setName("程序员"))); | ||||
|         // 测试 status 不匹配 | ||||
|         postMapper.insert(cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         PostPageReqVO reqVO = new PostPageReqVO(); | ||||
|         reqVO.setName("码"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<PostDO> pageResult = postService.getPostPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(postDO, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetPostList() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO01 = randomPojo(PostDO.class); | ||||
|         postMapper.insert(postDO01); | ||||
|         // 测试 id 不匹配 | ||||
|         PostDO postDO02 = randomPojo(PostDO.class); | ||||
|         postMapper.insert(postDO02); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(postDO01.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<PostDO> list = postService.getPostList(ids); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(postDO01, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetPostList_idsAndStatus() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO01 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         postMapper.insert(postDO01); | ||||
|         // 测试 status 不匹配 | ||||
|         PostDO postDO02 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         postMapper.insert(postDO02); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = Arrays.asList(postDO01.getId(), postDO02.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<PostDO> list = postService.getPostList(ids, singletonList(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(postDO01, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetPost() { | ||||
|         // mock 数据 | ||||
|         PostDO dbPostDO = randomPostDO(); | ||||
|         postMapper.insert(dbPostDO); | ||||
|         // 准备参数 | ||||
|         Long id = dbPostDO.getId(); | ||||
|         // 调用 | ||||
|         PostDO post = postService.getPost(id); | ||||
|         // 断言 | ||||
|         assertNotNull(post); | ||||
|         assertPojoEquals(dbPostDO, post); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePostList_success() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         postMapper.insert(postDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(postDO.getId()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         postService.validatePostList(ids); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePostList_notFound() { | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(randomLongId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> postService.validatePostList(ids), POST_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidatePostList_notEnable() { | ||||
|         // mock 数据 | ||||
|         PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         postMapper.insert(postDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(postDO.getId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> postService.validatePostList(ids), POST_NOT_ENABLE, | ||||
|                 postDO.getName()); | ||||
|     } | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static PostDO randomPostDO(Consumer<PostDO>... consumers) { | ||||
|         Consumer<PostDO> consumer = (o) -> { | ||||
|             o.setStatus(randomCommonStatus()); // 保证 status 的范围 | ||||
|         }; | ||||
|         return randomPojo(PostDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,352 @@ | ||||
| package cn.iocoder.yudao.module.system.service.dict; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; | ||||
| 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.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| @Import(DictDataServiceImpl.class) | ||||
| public class DictDataServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private DictDataServiceImpl dictDataService; | ||||
|  | ||||
|     @Resource | ||||
|     private DictDataMapper dictDataMapper; | ||||
|     @MockBean | ||||
|     private DictTypeService dictTypeService; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictDataList() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2) | ||||
|                 .setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO01); | ||||
|         DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1) | ||||
|                 .setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO02); | ||||
|         DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3) | ||||
|                 .setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO03); | ||||
|         DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3) | ||||
|                 .setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO04); | ||||
|         // 准备参数 | ||||
|         Integer status = CommonStatusEnum.ENABLE.getStatus(); | ||||
|         String dictType = "yunai"; | ||||
|  | ||||
|         // 调用 | ||||
|         List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType); | ||||
|         // 断言 | ||||
|         assertEquals(2, dictDataDOList.size()); | ||||
|         assertPojoEquals(dictDataDO02, dictDataDOList.get(0)); | ||||
|         assertPojoEquals(dictDataDO01, dictDataDOList.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictDataPage() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到 | ||||
|             o.setLabel("芋艿"); | ||||
|             o.setDictType("yunai"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         dictDataMapper.insert(dbDictData); | ||||
|         // 测试 label 不匹配 | ||||
|         dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("艿"))); | ||||
|         // 测试 dictType 不匹配 | ||||
|         dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai"))); | ||||
|         // 测试 status 不匹配 | ||||
|         dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         DictDataPageReqVO reqVO = new DictDataPageReqVO(); | ||||
|         reqVO.setLabel("芋"); | ||||
|         reqVO.setDictType("yunai"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbDictData, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictData() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData); | ||||
|         // 准备参数 | ||||
|         Long id = dbDictData.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         DictDataDO dictData = dictDataService.getDictData(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbDictData, dictData); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateDictData_success() { | ||||
|         // 准备参数 | ||||
|         DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|         // mock 方法 | ||||
|         when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | ||||
|  | ||||
|         // 调用 | ||||
|         Long dictDataId = dictDataService.createDictData(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(dictDataId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         DictDataDO dictData = dictDataMapper.selectById(dictDataId); | ||||
|         assertPojoEquals(reqVO, dictData, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateDictData_success() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> { | ||||
|             o.setId(dbDictData.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|         // mock 方法,字典类型 | ||||
|         when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | ||||
|  | ||||
|         // 调用 | ||||
|         dictDataService.updateDictData(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, dictData); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictData_success() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictData.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         dictDataService.deleteDictData(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(dictDataMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataExists_success() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用成功 | ||||
|         dictDataService.validateDictDataExists(dbDictData.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataExists_notExists() { | ||||
|         assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeExists_success() { | ||||
|         // mock 方法,数据类型被禁用 | ||||
|         String type = randomString(); | ||||
|         when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type)); | ||||
|  | ||||
|         // 调用, 成功 | ||||
|         dictDataService.validateDictTypeExists(type); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeExists_notExists() { | ||||
|         assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeExists_notEnable() { | ||||
|         // mock 方法,数据类型被禁用 | ||||
|         String dictType = randomString(); | ||||
|         when(dictTypeService.getDictType(eq(dictType))).thenReturn( | ||||
|                 randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataValueUnique_success() { | ||||
|         // 调用,成功 | ||||
|         dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataValueUnique_valueDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         String dictType = randomString(); | ||||
|         String value = randomString(); | ||||
|         // mock 数据 | ||||
|         dictDataMapper.insert(randomDictDataDO(o -> { | ||||
|             o.setDictType(dictType); | ||||
|             o.setValue(value); | ||||
|         })); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value), | ||||
|                 DICT_DATA_VALUE_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String dictType = randomString(); | ||||
|         String value = randomString(); | ||||
|         // mock 数据 | ||||
|         dictDataMapper.insert(randomDictDataDO(o -> { | ||||
|             o.setDictType(dictType); | ||||
|             o.setValue(value); | ||||
|         })); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value), | ||||
|                 DICT_DATA_VALUE_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictDataCountByDictType() { | ||||
|         // mock 数据 | ||||
|         dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); | ||||
|         dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou"))); | ||||
|         dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); | ||||
|         // 准备参数 | ||||
|         String dictType = "yunai"; | ||||
|  | ||||
|         // 调用 | ||||
|         long count = dictDataService.getDictDataCountByDictType(dictType); | ||||
|         // 校验 | ||||
|         assertEquals(2L, count); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataList_success() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO); | ||||
|         // 准备参数 | ||||
|         String dictType = dictDataDO.getDictType(); | ||||
|         List<String> values = singletonList(dictDataDO.getValue()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         dictDataService.validateDictDataList(dictType, values); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataList_notFound() { | ||||
|         // 准备参数 | ||||
|         String dictType = randomString(); | ||||
|         List<String> values = singletonList(randomString()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataList_notEnable() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         dictDataMapper.insert(dictDataDO); | ||||
|         // 准备参数 | ||||
|         String dictType = dictDataDO.getDictType(); | ||||
|         List<String> values = singletonList(dictDataDO.getValue()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), | ||||
|                 DICT_DATA_NOT_ENABLE, dictDataDO.getLabel()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictData_dictType() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1"); | ||||
|         dictDataMapper.insert(dictDataDO); | ||||
|         DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2"); | ||||
|         dictDataMapper.insert(dictDataDO02); | ||||
|         // 准备参数 | ||||
|         String dictType = "yunai"; | ||||
|         String value = "1"; | ||||
|  | ||||
|         // 调用 | ||||
|         DictDataDO dbDictData = dictDataService.getDictData(dictType, value); | ||||
|         // 断言 | ||||
|         assertEquals(dictDataDO, dbDictData); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testParseDictData() { | ||||
|         // mock 数据 | ||||
|         DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1"); | ||||
|         dictDataMapper.insert(dictDataDO); | ||||
|         DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2"); | ||||
|         dictDataMapper.insert(dictDataDO02); | ||||
|         // 准备参数 | ||||
|         String dictType = "yunai"; | ||||
|         String label = "1"; | ||||
|  | ||||
|         // 调用 | ||||
|         DictDataDO dbDictData = dictDataService.parseDictData(dictType, label); | ||||
|         // 断言 | ||||
|         assertEquals(dictDataDO, dbDictData); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) { | ||||
|         Consumer<DictDataDO> consumer = (o) -> { | ||||
|             o.setStatus(randomCommonStatus()); // 保证 status 的范围 | ||||
|         }; | ||||
|         return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 生成一个有效的字典类型 | ||||
|      * | ||||
|      * @param type 字典类型 | ||||
|      * @return DictTypeDO 对象 | ||||
|      */ | ||||
|     private static DictTypeDO randomDictTypeDO(String type) { | ||||
|         return randomPojo(DictTypeDO.class, o -> { | ||||
|             o.setType(type); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,271 @@ | ||||
| package cn.iocoder.yudao.module.system.service.dict; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper; | ||||
| 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.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| @Import(DictTypeServiceImpl.class) | ||||
| public class DictTypeServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private DictTypeServiceImpl dictTypeService; | ||||
|  | ||||
|     @Resource | ||||
|     private DictTypeMapper dictTypeMapper; | ||||
|     @MockBean | ||||
|     private DictDataService dictDataService; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictTypePage() { | ||||
|        // mock 数据 | ||||
|        DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到 | ||||
|            o.setName("yunai"); | ||||
|            o.setType("芋艿"); | ||||
|            o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|            o.setCreateTime(buildTime(2021, 1, 15)); | ||||
|        }); | ||||
|        dictTypeMapper.insert(dbDictType); | ||||
|        // 测试 name 不匹配 | ||||
|        dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setName("tudou"))); | ||||
|        // 测试 type 不匹配 | ||||
|        dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setType("土豆"))); | ||||
|        // 测试 status 不匹配 | ||||
|        dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|        // 测试 createTime 不匹配 | ||||
|        dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1)))); | ||||
|        // 准备参数 | ||||
|        DictTypePageReqVO reqVO = new DictTypePageReqVO(); | ||||
|        reqVO.setName("nai"); | ||||
|        reqVO.setType("艿"); | ||||
|        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|        reqVO.setCreateTime(buildBetweenTime(2021, 1, 10, 2021, 1, 20)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<DictTypeDO> pageResult = dictTypeService.getDictTypePage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbDictType, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictType_id() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType); | ||||
|         // 准备参数 | ||||
|         Long id = dbDictType.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         DictTypeDO dictType = dictTypeService.getDictType(id); | ||||
|         // 断言 | ||||
|         assertNotNull(dictType); | ||||
|         assertPojoEquals(dbDictType, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictType_type() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType); | ||||
|         // 准备参数 | ||||
|         String type = dbDictType.getType(); | ||||
|  | ||||
|         // 调用 | ||||
|         DictTypeDO dictType = dictTypeService.getDictType(type); | ||||
|         // 断言 | ||||
|         assertNotNull(dictType); | ||||
|         assertPojoEquals(dbDictType, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateDictType_success() { | ||||
|         // 准备参数 | ||||
|         DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, | ||||
|                 o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) | ||||
|                 .setId(null); // 避免 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long dictTypeId = dictTypeService.createDictType(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(dictTypeId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         DictTypeDO dictType = dictTypeMapper.selectById(dictTypeId); | ||||
|         assertPojoEquals(reqVO, dictType, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateDictType_success() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, o -> { | ||||
|             o.setId(dbDictType.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         dictTypeService.updateDictType(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         DictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictType_success() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictType.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         dictTypeService.deleteDictType(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(dictTypeMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictType_hasChildren() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictType.getId(); | ||||
|         // mock 方法 | ||||
|         when(dictDataService.getDictDataCountByDictType(eq(dbDictType.getType()))).thenReturn(1L); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictTypeList() { | ||||
|         // 准备参数 | ||||
|         DictTypeDO dictTypeDO01 = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dictTypeDO01); | ||||
|         DictTypeDO dictTypeDO02 = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dictTypeDO02); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         List<DictTypeDO> dictTypeDOList = dictTypeService.getDictTypeList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, dictTypeDOList.size()); | ||||
|         assertPojoEquals(dictTypeDO01, dictTypeDOList.get(0)); | ||||
|         assertPojoEquals(dictTypeDO02, dictTypeDOList.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataExists_success() { | ||||
|         // mock 数据 | ||||
|         DictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用成功 | ||||
|         dictTypeService.validateDictTypeExists(dbDictType.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataExists_notExists() { | ||||
|         assertServiceException(() -> dictTypeService.validateDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeUnique_success() { | ||||
|         // 调用,成功 | ||||
|         dictTypeService.validateDictTypeUnique(randomLongId(), randomString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeUnique_valueDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         String type = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.validateDictTypeUnique(null, type), | ||||
|                 DICT_TYPE_TYPE_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeUnique_valueDuplicateForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String type = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.validateDictTypeUnique(id, type), | ||||
|                 DICT_TYPE_TYPE_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypNameUnique_success() { | ||||
|         // 调用,成功 | ||||
|         dictTypeService.validateDictTypeNameUnique(randomLongId(), randomString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeNameUnique_nameDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         String name = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(null, name), | ||||
|                 DICT_TYPE_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictTypeNameUnique_nameDuplicateForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String name = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(id, name), | ||||
|                 DICT_TYPE_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static DictTypeDO randomDictTypeDO(Consumer<DictTypeDO>... consumers) { | ||||
|         Consumer<DictTypeDO> consumer = (o) -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|         }; | ||||
|         return randomPojo(DictTypeDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,308 @@ | ||||
| package cn.iocoder.yudao.module.system.service.errorcode; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO; | ||||
| import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.errorcode.ErrorCodeMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.errorcode.ErrorCodeTypeEnum; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.List; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_DUPLICATE; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| @Import(ErrorCodeServiceImpl.class) | ||||
| public class ErrorCodeServiceTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private ErrorCodeServiceImpl errorCodeService; | ||||
|  | ||||
|     @Resource | ||||
|     private ErrorCodeMapper errorCodeMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateErrorCode_success() { | ||||
|         // 准备参数 | ||||
|         ErrorCodeSaveReqVO reqVO = randomPojo(ErrorCodeSaveReqVO.class) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long errorCodeId = errorCodeService.createErrorCode(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(errorCodeId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(errorCodeId); | ||||
|         assertPojoEquals(reqVO, errorCode, "id"); | ||||
|         assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateErrorCode_success() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(); | ||||
|         errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         ErrorCodeSaveReqVO reqVO = randomPojo(ErrorCodeSaveReqVO.class, o -> { | ||||
|             o.setId(dbErrorCode.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         errorCodeService.updateErrorCode(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, errorCode); | ||||
|         assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteErrorCode_success() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(); | ||||
|         errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbErrorCode.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         errorCodeService.deleteErrorCode(id); | ||||
|        // 校验数据不存在了 | ||||
|        assertNull(errorCodeMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetErrorCodePage() { | ||||
|        // mock 数据 | ||||
|        ErrorCodeDO dbErrorCode = initGetErrorCodePage(); | ||||
|        // 准备参数 | ||||
|        ErrorCodePageReqVO reqVO = new ErrorCodePageReqVO(); | ||||
|        reqVO.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()); | ||||
|        reqVO.setApplicationName("tu"); | ||||
|        reqVO.setCode(1); | ||||
|        reqVO.setMessage("ma"); | ||||
|        reqVO.setCreateTime(buildBetweenTime(2020, 11, 1, 2020, 11, 30)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<ErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbErrorCode, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 初始化 getErrorCodePage 方法的测试数据 | ||||
|      */ | ||||
|     private ErrorCodeDO initGetErrorCodePage() { | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> { // 等会查询到 | ||||
|             o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()); | ||||
|             o.setApplicationName("tudou"); | ||||
|             o.setCode(1); | ||||
|             o.setMessage("yuanma"); | ||||
|             o.setCreateTime(buildTime(2020, 11, 11)); | ||||
|         }); | ||||
|         errorCodeMapper.insert(dbErrorCode); | ||||
|         // 测试 type 不匹配 | ||||
|         errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType()))); | ||||
|         // 测试 applicationName 不匹配 | ||||
|         errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setApplicationName("yuan"))); | ||||
|         // 测试 code 不匹配 | ||||
|         errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setCode(2))); | ||||
|         // 测试 message 不匹配 | ||||
|         errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setMessage("nai"))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setCreateTime(buildTime(2020, 12, 12)))); | ||||
|         return dbErrorCode; | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateCodeDuplicate_codeDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         Integer code = randomInteger(); | ||||
|         // mock 数据 | ||||
|         errorCodeMapper.insert(randomErrorCodeDO(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(randomErrorCodeDO(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)); | ||||
|         // 断言 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectOne(null); | ||||
|         assertPojoEquals(generateReqDTO, errorCode); | ||||
|         assertEquals(ErrorCodeTypeEnum.AUTO_GENERATION.getType(), errorCode.getType()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 情况 2.1,错误码存在,但是是 ErrorCodeTypeEnum.MANUAL_OPERATION 类型 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testAutoGenerateErrorCodes_021() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType())); | ||||
|         errorCodeMapper.insert(dbErrorCode); | ||||
|         // 准备参数 | ||||
|         ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class, | ||||
|                 o -> o.setCode(dbErrorCode.getCode())); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO)); | ||||
|         // 断言,相等,说明不会更新 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId()); | ||||
|         assertPojoEquals(dbErrorCode, errorCode); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 情况 2.2,错误码存在,但是是 applicationName 不匹配 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testAutoGenerateErrorCodes_022() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType())); | ||||
|         errorCodeMapper.insert(dbErrorCode); | ||||
|         // 准备参数 | ||||
|         ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class, | ||||
|                 o -> o.setCode(dbErrorCode.getCode()).setApplicationName(randomString())); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO)); | ||||
|         // 断言,相等,说明不会更新 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId()); | ||||
|         assertPojoEquals(dbErrorCode, errorCode); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 情况 2.3,错误码存在,但是是 message 相同 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testAutoGenerateErrorCodes_023() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.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)); | ||||
|         // 断言,相等,说明不会更新 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId()); | ||||
|         assertPojoEquals(dbErrorCode, errorCode); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 情况 2.3,错误码存在,但是是 message 不同,则进行更新 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testAutoGenerateErrorCodes_024() { | ||||
|         // mock 数据 | ||||
|         ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.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)); | ||||
|         // 断言,匹配 | ||||
|         ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId()); | ||||
|         assertPojoEquals(generateReqDTO, errorCode); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetErrorCode() { | ||||
|         // 准备参数 | ||||
|         ErrorCodeDO errorCodeDO = randomErrorCodeDO(); | ||||
|         errorCodeMapper.insert(errorCodeDO); | ||||
|         // mock 方法 | ||||
|         Long id = errorCodeDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         ErrorCodeDO dbErrorCode = errorCodeService.getErrorCode(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(errorCodeDO, dbErrorCode); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetErrorCodeList() { | ||||
|         // 准备参数 | ||||
|         ErrorCodeDO errorCodeDO01 = randomErrorCodeDO( | ||||
|                 o -> o.setApplicationName("yunai_server").setUpdateTime(buildTime(2022, 1, 10))); | ||||
|         errorCodeMapper.insert(errorCodeDO01); | ||||
|         ErrorCodeDO errorCodeDO02 = randomErrorCodeDO( | ||||
|                 o -> o.setApplicationName("yunai_server").setUpdateTime(buildTime(2022, 1, 12))); | ||||
|         errorCodeMapper.insert(errorCodeDO02); | ||||
|         // mock 方法 | ||||
|         String applicationName = "yunai_server"; | ||||
|         LocalDateTime minUpdateTime = buildTime(2022, 1, 11); | ||||
|  | ||||
|         // 调用 | ||||
|         List<ErrorCodeRespDTO> errorCodeList = errorCodeService.getErrorCodeList(applicationName, minUpdateTime); | ||||
|         // 断言 | ||||
|         assertEquals(1, errorCodeList.size()); | ||||
|         assertPojoEquals(errorCodeDO02, errorCodeList.get(0)); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static ErrorCodeDO randomErrorCodeDO(Consumer<ErrorCodeDO>... consumers) { | ||||
|         Consumer<ErrorCodeDO> consumer = (o) -> { | ||||
|             o.setType(randomEle(ErrorCodeTypeEnum.values()).getType()); // 保证 key 的范围 | ||||
|         }; | ||||
|         return randomPojo(ErrorCodeDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,76 @@ | ||||
| package cn.iocoder.yudao.module.system.service.logger; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR; | ||||
| import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.SUCCESS; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
|  | ||||
| @Import(LoginLogServiceImpl.class) | ||||
| public class LoginLogServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private LoginLogServiceImpl loginLogService; | ||||
|  | ||||
|     @Resource | ||||
|     private LoginLogMapper loginLogMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetLoginLogPage() { | ||||
|         // mock 数据 | ||||
|         LoginLogDO loginLogDO = randomPojo(LoginLogDO.class, o -> { | ||||
|             o.setUserIp("192.168.199.16"); | ||||
|             o.setUsername("wang"); | ||||
|             o.setResult(SUCCESS.getResult()); | ||||
|             o.setCreateTime(buildTime(2021, 3, 6)); | ||||
|         }); | ||||
|         loginLogMapper.insert(loginLogDO); | ||||
|         // 测试 status 不匹配 | ||||
|         loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setResult(CAPTCHA_CODE_ERROR.getResult()))); | ||||
|         // 测试 ip 不匹配 | ||||
|         loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUserIp("192.168.128.18"))); | ||||
|         // 测试 username 不匹配 | ||||
|         loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUsername("yunai"))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); | ||||
|         // 构造调用参数 | ||||
|         LoginLogPageReqVO reqVO = new LoginLogPageReqVO(); | ||||
|         reqVO.setUsername("wang"); | ||||
|         reqVO.setUserIp("192.168.199"); | ||||
|         reqVO.setStatus(true); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<LoginLogDO> pageResult = loginLogService.getLoginLogPage(reqVO); | ||||
|         // 断言,只查到了一条符合条件的 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(loginLogDO, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateLoginLog() { | ||||
|         LoginLogCreateReqDTO reqDTO = randomPojo(LoginLogCreateReqDTO.class); | ||||
|  | ||||
|         // 调用 | ||||
|         loginLogService.createLoginLog(reqDTO); | ||||
|         // 断言 | ||||
|         LoginLogDO loginLogDO = loginLogMapper.selectOne(null); | ||||
|         assertPojoEquals(reqDTO, loginLogDO); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,106 @@ | ||||
| package cn.iocoder.yudao.module.system.service.logger; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.framework.test.core.util.RandomUtils; | ||||
| import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| 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.Collections; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| @Import({OperateLogServiceImpl.class}) | ||||
| public class OperateLogServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private OperateLogService operateLogServiceImpl; | ||||
|  | ||||
|     @Resource | ||||
|     private OperateLogMapper operateLogMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private AdminUserService userService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateOperateLog() { | ||||
|         OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class, | ||||
|                 o -> o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build())); | ||||
|  | ||||
|         // 调研 | ||||
|         operateLogServiceImpl.createOperateLog(reqVO); | ||||
|         // 断言 | ||||
|         OperateLogDO operateLogDO = operateLogMapper.selectOne(null); | ||||
|         assertPojoEquals(reqVO, operateLogDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetOperateLogPage() { | ||||
|         // mock(用户信息) | ||||
|         AdminUserDO user = RandomUtils.randomPojo(AdminUserDO.class, o -> { | ||||
|             o.setNickname("wang"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         when(userService.getUserListByNickname("wang")).thenReturn(Collections.singletonList(user)); | ||||
|         Long userId = user.getId(); | ||||
|  | ||||
|         // 构造操作日志 | ||||
|         OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { | ||||
|             o.setUserId(userId); | ||||
|             o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||||
|             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(operateLogDO); | ||||
|         // 测试 userId 不匹配 | ||||
|         operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(userId + 1))); | ||||
|         // 测试 module 不匹配 | ||||
|         operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setModule("user"))); | ||||
|         // 测试 type 不匹配 | ||||
|         operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType(OperateTypeEnum.IMPORT.getType()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setStartTime(buildTime(2021, 2, 6)))); | ||||
|         // 测试 resultCode 不匹配 | ||||
|         operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setResultCode(BAD_REQUEST.getCode()))); | ||||
|  | ||||
|         // 构造调用参数 | ||||
|         OperateLogPageReqVO reqVO = new OperateLogPageReqVO(); | ||||
|         reqVO.setUserNickname("wang"); | ||||
|         reqVO.setModule("order"); | ||||
|         reqVO.setType(OperateTypeEnum.CREATE.getType()); | ||||
|         reqVO.setStartTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); | ||||
|         reqVO.setSuccess(true); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<OperateLogDO> pageResult = operateLogServiceImpl.getOperateLogPage(reqVO); | ||||
|         // 断言,只查到了一条符合条件的 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(operateLogDO, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,179 @@ | ||||
| package cn.iocoder.yudao.module.system.service.mail; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; | ||||
| 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.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link MailAccountServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(MailAccountServiceImpl.class) | ||||
| public class MailAccountServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private MailAccountServiceImpl mailAccountService; | ||||
|  | ||||
|     @Resource | ||||
|     private MailAccountMapper mailAccountMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private MailTemplateService mailTemplateService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateMailAccount_success() { | ||||
|         // 准备参数 | ||||
|         MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> o.setMail(randomEmail())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long mailAccountId = mailAccountService.createMailAccount(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(mailAccountId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); | ||||
|         assertPojoEquals(reqVO, mailAccount, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailAccount_success() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> { | ||||
|             o.setId(dbMailAccount.getId()); // 设置更新的 ID | ||||
|             o.setMail(randomEmail()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         mailAccountService.updateMailAccount(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, mailAccount); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailAccount_notExists() { | ||||
|         // 准备参数 | ||||
|         MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> mailAccountService.updateMailAccount(reqVO), MAIL_ACCOUNT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMailAccount_success() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbMailAccount.getId(); | ||||
|         // mock 方法(无关联模版) | ||||
|         when(mailTemplateService.getMailTemplateCountByAccountId(eq(id))).thenReturn(0L); | ||||
|  | ||||
|         // 调用 | ||||
|         mailAccountService.deleteMailAccount(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(mailAccountMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailAccountFromCache() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbMailAccount.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         MailAccountDO mailAccount = mailAccountService.getMailAccountFromCache(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbMailAccount, mailAccount); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMailAccount_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> mailAccountService.deleteMailAccount(id), MAIL_ACCOUNT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailAccountPage() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 | ||||
|             o.setMail("768@qq.com"); | ||||
|             o.setUsername("yunai"); | ||||
|         }); | ||||
|         mailAccountMapper.insert(dbMailAccount); | ||||
|         // 测试 mail 不匹配 | ||||
|         mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); | ||||
|         // 测试 username 不匹配 | ||||
|         mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); | ||||
|         // 准备参数 | ||||
|         MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); | ||||
|         reqVO.setMail("768"); | ||||
|         reqVO.setUsername("yu"); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailAccount() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbMailAccount.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         MailAccountDO mailAccount = mailAccountService.getMailAccount(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbMailAccount, mailAccount); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailAccountList() { | ||||
|         // mock 数据 | ||||
|         MailAccountDO dbMailAccount01 = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount01); | ||||
|         MailAccountDO dbMailAccount02 = randomPojo(MailAccountDO.class); | ||||
|         mailAccountMapper.insert(dbMailAccount02); | ||||
|         // 准备参数 | ||||
|  | ||||
|         // 调用 | ||||
|         List<MailAccountDO> list = mailAccountService.getMailAccountList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertPojoEquals(dbMailAccount01, list.get(0)); | ||||
|         assertPojoEquals(dbMailAccount02, list.get(1)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,183 @@ | ||||
| package cn.iocoder.yudao.module.system.service.mail; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link MailLogServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(MailLogServiceImpl.class) | ||||
| public class MailLogServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private MailLogServiceImpl mailLogService; | ||||
|  | ||||
|     @Resource | ||||
|     private MailLogMapper mailLogMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateMailLog() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String toMail = randomEmail(); | ||||
|         MailAccountDO account = randomPojo(MailAccountDO.class); | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class); | ||||
|         String templateContent = randomString(); | ||||
|         Map<String, Object> templateParams = randomTemplateParams(); | ||||
|         Boolean isSend = true; | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         Long logId = mailLogService.createMailLog(userId, userType, toMail, account, template, templateContent, templateParams, isSend); | ||||
|         // 断言 | ||||
|         MailLogDO log = mailLogMapper.selectById(logId); | ||||
|         assertNotNull(log); | ||||
|         assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus()); | ||||
|         assertEquals(userId, log.getUserId()); | ||||
|         assertEquals(userType, log.getUserType()); | ||||
|         assertEquals(toMail, log.getToMail()); | ||||
|         assertEquals(account.getId(), log.getAccountId()); | ||||
|         assertEquals(account.getMail(), log.getFromMail()); | ||||
|         assertEquals(template.getId(), log.getTemplateId()); | ||||
|         assertEquals(template.getCode(), log.getTemplateCode()); | ||||
|         assertEquals(template.getNickname(), log.getTemplateNickname()); | ||||
|         assertEquals(template.getTitle(), log.getTemplateTitle()); | ||||
|         assertEquals(templateContent, log.getTemplateContent()); | ||||
|         assertEquals(templateParams, log.getTemplateParams()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailSendResult_success() { | ||||
|         // mock 数据 | ||||
|         MailLogDO log = randomPojo(MailLogDO.class, o -> { | ||||
|             o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); | ||||
|             o.setSendTime(null).setSendMessageId(null).setSendException(null) | ||||
|                     .setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         mailLogMapper.insert(log); | ||||
|         // 准备参数 | ||||
|         Long logId = log.getId(); | ||||
|         String messageId = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         mailLogService.updateMailSendResult(logId, messageId, null); | ||||
|         // 断言 | ||||
|         MailLogDO dbLog = mailLogMapper.selectById(logId); | ||||
|         assertEquals(MailSendStatusEnum.SUCCESS.getStatus(), dbLog.getSendStatus()); | ||||
|         assertNotNull(dbLog.getSendTime()); | ||||
|         assertEquals(messageId, dbLog.getSendMessageId()); | ||||
|         assertNull(dbLog.getSendException()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailSendResult_exception() { | ||||
|         // mock 数据 | ||||
|         MailLogDO log = randomPojo(MailLogDO.class, o -> { | ||||
|             o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); | ||||
|             o.setSendTime(null).setSendMessageId(null).setSendException(null) | ||||
|                     .setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         mailLogMapper.insert(log); | ||||
|         // 准备参数 | ||||
|         Long logId = log.getId(); | ||||
|         Exception exception = new NullPointerException("测试异常"); | ||||
|  | ||||
|         // 调用 | ||||
|         mailLogService.updateMailSendResult(logId, null, exception); | ||||
|         // 断言 | ||||
|         MailLogDO dbLog = mailLogMapper.selectById(logId); | ||||
|         assertEquals(MailSendStatusEnum.FAILURE.getStatus(), dbLog.getSendStatus()); | ||||
|         assertNotNull(dbLog.getSendTime()); | ||||
|         assertNull(dbLog.getSendMessageId()); | ||||
|         assertEquals("NullPointerException: 测试异常", dbLog.getSendException()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailLog() { | ||||
|         // mock 数据 | ||||
|         MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> o.setTemplateParams(randomTemplateParams())); | ||||
|         mailLogMapper.insert(dbMailLog); | ||||
|         // 准备参数 | ||||
|         Long id = dbMailLog.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         MailLogDO mailLog = mailLogService.getMailLog(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbMailLog, mailLog); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailLogPage() { | ||||
|        // mock 数据 | ||||
|        MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到 | ||||
|            o.setUserId(1L); | ||||
|            o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|            o.setToMail("768@qq.com"); | ||||
|            o.setAccountId(10L); | ||||
|            o.setTemplateId(100L); | ||||
|            o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); | ||||
|            o.setSendTime(buildTime(2023, 2, 10)); | ||||
|            o.setTemplateParams(randomTemplateParams()); | ||||
|        }); | ||||
|        mailLogMapper.insert(dbMailLog); | ||||
|        // 测试 userId 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L))); | ||||
|        // 测试 userType 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|        // 测试 toMail 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMail("788@.qq.com"))); | ||||
|        // 测试 accountId 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L))); | ||||
|        // 测试 templateId 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setTemplateId(101L))); | ||||
|        // 测试 sendStatus 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()))); | ||||
|        // 测试 sendTime 不匹配 | ||||
|        mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendTime(buildTime(2023, 3, 10)))); | ||||
|        // 准备参数 | ||||
|        MailLogPageReqVO reqVO = new MailLogPageReqVO(); | ||||
|        reqVO.setUserId(1L); | ||||
|        reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|        reqVO.setToMail("768"); | ||||
|        reqVO.setAccountId(10L); | ||||
|        reqVO.setTemplateId(100L); | ||||
|        reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus()); | ||||
|        reqVO.setSendTime((buildBetweenTime(2023, 2, 1, 2023, 2, 15))); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<MailLogDO> pageResult = mailLogService.getMailLogPage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbMailLog, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     private static Map<String, Object> randomTemplateParams() { | ||||
|         return MapUtil.<String, Object>builder().put(randomString(), randomString()) | ||||
|                 .put(randomString(), randomString()).build(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,332 @@ | ||||
| package cn.iocoder.yudao.module.system.service.mail; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.hutool.extra.mail.MailAccount; | ||||
| import cn.hutool.extra.mail.MailUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.framework.test.core.util.RandomUtils; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; | ||||
| import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; | ||||
| import cn.iocoder.yudao.module.system.service.member.MemberService; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Disabled; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.MockedStatic; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| public class MailSendServiceImplTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private MailSendServiceImpl mailSendService; | ||||
|  | ||||
|     @Mock | ||||
|     private AdminUserService adminUserService; | ||||
|     @Mock | ||||
|     private MemberService memberService; | ||||
|     @Mock | ||||
|     private MailAccountService mailAccountService; | ||||
|     @Mock | ||||
|     private MailTemplateService mailTemplateService; | ||||
|     @Mock | ||||
|     private MailLogService mailLogService; | ||||
|     @Mock | ||||
|     private MailProducer mailProducer; | ||||
|  | ||||
|     /** | ||||
|      * 用于快速测试你的邮箱账号是否正常 | ||||
|      */ | ||||
|     @Test | ||||
|     @Disabled | ||||
|     public void testDemo() { | ||||
|         MailAccount mailAccount = new MailAccount() | ||||
| //                .setFrom("奥特曼 <ydym_test@163.com>") | ||||
|                 .setFrom("ydym_test@163.com") // 邮箱地址 | ||||
|                 .setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器 | ||||
|                 .setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE"); // 登录账号密码 | ||||
|         String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false); | ||||
|         System.out.println("发送结果:" + messageId); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleMailToAdmin() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = RandomUtils.randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock adminUserService 的方法 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); | ||||
|         when(adminUserService.getUser(eq(userId))).thenReturn(user); | ||||
|  | ||||
|         // mock MailTemplateService 的方法 | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String title = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) | ||||
|                 .thenReturn(title); | ||||
|         String content = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock MailAccountService 的方法 | ||||
|         MailAccountDO account = randomPojo(MailAccountDO.class); | ||||
|         when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); | ||||
|         // mock MailLogService 的方法 | ||||
|         Long mailLogId = randomLongId(); | ||||
|         when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(user.getEmail()), | ||||
|                 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMailLogId = mailSendService.sendSingleMailToAdmin(null, userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(mailLogId, resultMailLogId); | ||||
|         // 断言调用 | ||||
|         verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(user.getEmail()), | ||||
|                 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleMailToMember() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = RandomUtils.randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock memberService 的方法 | ||||
|         String mail = randomEmail(); | ||||
|         when(memberService.getMemberUserEmail(eq(userId))).thenReturn(mail); | ||||
|  | ||||
|         // mock MailTemplateService 的方法 | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String title = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) | ||||
|                 .thenReturn(title); | ||||
|         String content = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock MailAccountService 的方法 | ||||
|         MailAccountDO account = randomPojo(MailAccountDO.class); | ||||
|         when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); | ||||
|         // mock MailLogService 的方法 | ||||
|         Long mailLogId = randomLongId(); | ||||
|         when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(mail), | ||||
|                 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMailLogId = mailSendService.sendSingleMailToMember(null, userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(mailLogId, resultMailLogId); | ||||
|         // 断言调用 | ||||
|         verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), | ||||
|                 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 发送成功,当短信模板开启时 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testSendSingleMail_successWhenMailTemplateEnable() { | ||||
|         // 准备参数 | ||||
|         String mail = randomEmail(); | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String templateCode = RandomUtils.randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock MailTemplateService 的方法 | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String title = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) | ||||
|                 .thenReturn(title); | ||||
|         String content = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock MailAccountService 的方法 | ||||
|         MailAccountDO account = randomPojo(MailAccountDO.class); | ||||
|         when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); | ||||
|         // mock MailLogService 的方法 | ||||
|         Long mailLogId = randomLongId(); | ||||
|         when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), | ||||
|                 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(mailLogId, resultMailLogId); | ||||
|         // 断言调用 | ||||
|         verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), | ||||
|                 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 发送成功,当短信模板关闭时 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testSendSingleMail_successWhenSmsTemplateDisable() { | ||||
|         // 准备参数 | ||||
|         String mail = randomEmail(); | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String templateCode = RandomUtils.randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock MailTemplateService 的方法 | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String title = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) | ||||
|                 .thenReturn(title); | ||||
|         String content = RandomUtils.randomString(); | ||||
|         when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock MailAccountService 的方法 | ||||
|         MailAccountDO account = randomPojo(MailAccountDO.class); | ||||
|         when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); | ||||
|         // mock MailLogService 的方法 | ||||
|         Long mailLogId = randomLongId(); | ||||
|         when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), | ||||
|                 eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(mailLogId, resultMailLogId); | ||||
|         // 断言调用 | ||||
|         verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), anyString(), | ||||
|                 anyLong(), anyString(), anyString(), anyString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMailTemplateValid_notExists() { | ||||
|         // 准备参数 | ||||
|         String templateCode = RandomUtils.randomString(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> mailSendService.validateMailTemplate(templateCode), | ||||
|                 MAIL_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateTemplateParams_paramMiss() { | ||||
|         // 准备参数 | ||||
|         MailTemplateDO template = randomPojo(MailTemplateDO.class, | ||||
|                 o -> o.setParams(Lists.newArrayList("code"))); | ||||
|         Map<String, Object> templateParams = new HashMap<>(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> mailSendService.validateTemplateParams(template, templateParams), | ||||
|                 MAIL_SEND_TEMPLATE_PARAM_MISS, "code"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMail_notExists() { | ||||
|         // 准备参数 | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> mailSendService.validateMail(null), | ||||
|                 MAIL_SEND_MAIL_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDoSendMail_success() { | ||||
|         try (MockedStatic<MailUtil> mailUtilMock = mockStatic(MailUtil.class)) { | ||||
|             // 准备参数 | ||||
|             MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); | ||||
|             // mock 方法(获得邮箱账号) | ||||
|             MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); | ||||
|             when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) | ||||
|                     .thenReturn(account); | ||||
|  | ||||
|             // mock 方法(发送邮件) | ||||
|             String messageId = randomString(); | ||||
|             mailUtilMock.when(() -> MailUtil.send( | ||||
|                     argThat(mailAccount -> { | ||||
|                         assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); | ||||
|                         assertTrue(mailAccount.isAuth()); | ||||
|                         assertEquals(account.getUsername(), mailAccount.getUser()); | ||||
|                         assertEquals(account.getPassword(), mailAccount.getPass()); | ||||
|                         assertEquals(account.getHost(), mailAccount.getHost()); | ||||
|                         assertEquals(account.getPort(), mailAccount.getPort()); | ||||
|                         assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); | ||||
|                         return true; | ||||
|                     }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) | ||||
|                     .thenReturn(messageId); | ||||
|  | ||||
|             // 调用 | ||||
|             mailSendService.doSendMail(message); | ||||
|             // 断言 | ||||
|             verify(mailLogService).updateMailSendResult(eq(message.getLogId()), eq(messageId), isNull()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDoSendMail_exception() { | ||||
|         try (MockedStatic<MailUtil> mailUtilMock = mockStatic(MailUtil.class)) { | ||||
|             // 准备参数 | ||||
|             MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); | ||||
|             // mock 方法(获得邮箱账号) | ||||
|             MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); | ||||
|             when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) | ||||
|                     .thenReturn(account); | ||||
|  | ||||
|             // mock 方法(发送邮件) | ||||
|             Exception e = new NullPointerException("啦啦啦"); | ||||
|             mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> { | ||||
|                         assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); | ||||
|                         assertTrue(mailAccount.isAuth()); | ||||
|                         assertEquals(account.getUsername(), mailAccount.getUser()); | ||||
|                         assertEquals(account.getPassword(), mailAccount.getPass()); | ||||
|                         assertEquals(account.getHost(), mailAccount.getHost()); | ||||
|                         assertEquals(account.getPort(), mailAccount.getPort()); | ||||
|                         assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); | ||||
|                         return true; | ||||
|                     }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) | ||||
|                     .thenThrow(e); | ||||
|  | ||||
|             // 调用 | ||||
|             mailSendService.doSendMail(message); | ||||
|             // 断言 | ||||
|             verify(mailLogService).updateMailSendResult(eq(message.getLogId()), isNull(), same(e)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,215 @@ | ||||
| package cn.iocoder.yudao.module.system.service.mail; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link MailTemplateServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(MailTemplateServiceImpl.class) | ||||
| public class MailTemplateServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private MailTemplateServiceImpl mailTemplateService; | ||||
|  | ||||
|     @Resource | ||||
|     private MailTemplateMapper mailTemplateMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateMailTemplate_success() { | ||||
|         // 准备参数 | ||||
|         MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long mailTemplateId = mailTemplateService.createMailTemplate(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(mailTemplateId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); | ||||
|         assertPojoEquals(reqVO, mailTemplate, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailTemplate_success() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class, o -> { | ||||
|             o.setId(dbMailTemplate.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         mailTemplateService.updateMailTemplate(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, mailTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMailTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> mailTemplateService.updateMailTemplate(reqVO), MAIL_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMailTemplate_success() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbMailTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         mailTemplateService.deleteMailTemplate(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(mailTemplateMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMailTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> mailTemplateService.deleteMailTemplate(id), MAIL_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailTemplatePage() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 | ||||
|             o.setName("源码"); | ||||
|             o.setCode("test_01"); | ||||
|             o.setAccountId(1L); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2023, 2, 3)); | ||||
|         }); | ||||
|         mailTemplateMapper.insert(dbMailTemplate); | ||||
|         // 测试 name 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); | ||||
|         // 测试 code 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); | ||||
|         // 测试 accountId 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); | ||||
|         // 测试 status 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); | ||||
|         // 准备参数 | ||||
|         MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); | ||||
|         reqVO.setName("源"); | ||||
|         reqVO.setCode("est_01"); | ||||
|         reqVO.setAccountId(1L); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<MailTemplateDO> pageResult = mailTemplateService.getMailTemplatePage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailTemplateList() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate01 = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate01); | ||||
|         MailTemplateDO dbMailTemplate02 = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate02); | ||||
|  | ||||
|         // 调用 | ||||
|         List<MailTemplateDO> list = mailTemplateService.getMailTemplateList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertEquals(dbMailTemplate01, list.get(0)); | ||||
|         assertEquals(dbMailTemplate02, list.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailTemplate() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate); | ||||
|         // 准备参数 | ||||
|         Long id = dbMailTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         MailTemplateDO mailTemplate = mailTemplateService.getMailTemplate(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbMailTemplate, mailTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMailTemplateByCodeFromCache() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate); | ||||
|         // 准备参数 | ||||
|         String code = dbMailTemplate.getCode(); | ||||
|  | ||||
|         // 调用 | ||||
|         MailTemplateDO mailTemplate = mailTemplateService.getMailTemplateByCodeFromCache(code); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbMailTemplate, mailTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testFormatMailTemplateContent() { | ||||
|         // 准备参数 | ||||
|         Map<String, Object> params = new HashMap<>(); | ||||
|         params.put("name", "小红"); | ||||
|         params.put("what", "饭"); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertEquals("小红,你好,饭吃了吗?", | ||||
|                 mailTemplateService.formatMailTemplateContent("{name},你好,{what}吃了吗?", params)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCountByAccountId() { | ||||
|         // mock 数据 | ||||
|         MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); | ||||
|         mailTemplateMapper.insert(dbMailTemplate); | ||||
|         // 测试 accountId 不匹配 | ||||
|         mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); | ||||
|         // 准备参数 | ||||
|         Long accountId = dbMailTemplate.getAccountId(); | ||||
|  | ||||
|         // 调用 | ||||
|         long count = mailTemplateService.getMailTemplateCountByAccountId(accountId); | ||||
|         // 断言 | ||||
|         assertEquals(1, count); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,130 @@ | ||||
| package cn.iocoder.yudao.module.system.service.notice; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| @Import(NoticeServiceImpl.class) | ||||
| class NoticeServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private NoticeServiceImpl noticeService; | ||||
|  | ||||
|     @Resource | ||||
|     private NoticeMapper noticeMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNoticePage_success() { | ||||
|         // 插入前置数据 | ||||
|         NoticeDO dbNotice = randomPojo(NoticeDO.class, o -> { | ||||
|             o.setTitle("尼古拉斯赵四来啦!"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         noticeMapper.insert(dbNotice); | ||||
|         // 测试 title 不匹配 | ||||
|         noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!"))); | ||||
|         // 测试 status 不匹配 | ||||
|         noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         NoticePageReqVO reqVO = new NoticePageReqVO(); | ||||
|         reqVO.setTitle("尼古拉斯赵四来啦!"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<NoticeDO> pageResult = noticeService.getNoticePage(reqVO); | ||||
|         // 验证查询结果经过筛选 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbNotice, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotice_success() { | ||||
|         // 插入前置数据 | ||||
|         NoticeDO dbNotice = randomPojo(NoticeDO.class); | ||||
|         noticeMapper.insert(dbNotice); | ||||
|  | ||||
|         // 查询 | ||||
|         NoticeDO notice = noticeService.getNotice(dbNotice.getId()); | ||||
|  | ||||
|         // 验证插入与读取对象是否一致 | ||||
|         assertNotNull(notice); | ||||
|         assertPojoEquals(dbNotice, notice); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateNotice_success() { | ||||
|         // 准备参数 | ||||
|         NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class) | ||||
|                 .setId(null); // 避免 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long noticeId = noticeService.createNotice(reqVO); | ||||
|         // 校验插入属性是否正确 | ||||
|         assertNotNull(noticeId); | ||||
|         NoticeDO notice = noticeMapper.selectById(noticeId); | ||||
|         assertPojoEquals(reqVO, notice, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateNotice_success() { | ||||
|         // 插入前置数据 | ||||
|         NoticeDO dbNoticeDO = randomPojo(NoticeDO.class); | ||||
|         noticeMapper.insert(dbNoticeDO); | ||||
|  | ||||
|         // 准备更新参数 | ||||
|         NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class, o -> o.setId(dbNoticeDO.getId())); | ||||
|  | ||||
|         // 更新 | ||||
|         noticeService.updateNotice(reqVO); | ||||
|         // 检验是否更新成功 | ||||
|         NoticeDO notice = noticeMapper.selectById(reqVO.getId()); | ||||
|         assertPojoEquals(reqVO, notice); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteNotice_success() { | ||||
|         // 插入前置数据 | ||||
|         NoticeDO dbNotice = randomPojo(NoticeDO.class); | ||||
|         noticeMapper.insert(dbNotice); | ||||
|  | ||||
|         // 删除 | ||||
|         noticeService.deleteNotice(dbNotice.getId()); | ||||
|  | ||||
|         // 检查是否删除成功 | ||||
|         assertNull(noticeMapper.selectById(dbNotice.getId())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateNoticeExists_success() { | ||||
|         // 插入前置数据 | ||||
|         NoticeDO dbNotice = randomPojo(NoticeDO.class); | ||||
|         noticeMapper.insert(dbNotice); | ||||
|  | ||||
|         // 成功调用 | ||||
|         noticeService.validateNoticeExists(dbNotice.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateNoticeExists_noExists() { | ||||
|         assertServiceException(() -> | ||||
|                 noticeService.validateNoticeExists(randomLongId()), NOTICE_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,280 @@ | ||||
| package cn.iocoder.yudao.module.system.service.notify; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; | ||||
| import com.baomidou.mybatisplus.annotation.DbType; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
| * {@link NotifyMessageServiceImpl} 的单元测试类 | ||||
| * | ||||
| * @author 芋道源码 | ||||
| */ | ||||
| @Import(NotifyMessageServiceImpl.class) | ||||
| public class NotifyMessageServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private NotifyMessageServiceImpl notifyMessageService; | ||||
|  | ||||
|     @Resource | ||||
|     private NotifyMessageMapper notifyMessageMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateNotifyMessage_success() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class); | ||||
|         String templateContent = randomString(); | ||||
|         Map<String, Object> templateParams = randomTemplateParams(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         Long messageId = notifyMessageService.createNotifyMessage(userId, userType, | ||||
|                 template, templateContent, templateParams); | ||||
|         // 断言 | ||||
|         NotifyMessageDO message = notifyMessageMapper.selectById(messageId); | ||||
|         assertNotNull(message); | ||||
|         assertEquals(userId, message.getUserId()); | ||||
|         assertEquals(userType, message.getUserType()); | ||||
|         assertEquals(template.getId(), message.getTemplateId()); | ||||
|         assertEquals(template.getCode(), message.getTemplateCode()); | ||||
|         assertEquals(template.getType(), message.getTemplateType()); | ||||
|         assertEquals(template.getNickname(), message.getTemplateNickname()); | ||||
|         assertEquals(templateContent, message.getTemplateContent()); | ||||
|         assertEquals(templateParams, message.getTemplateParams()); | ||||
|         assertEquals(false, message.getReadStatus()); | ||||
|         assertNull(message.getReadTime()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotifyMessagePage() { | ||||
|        // mock 数据 | ||||
|        NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|            o.setUserId(1L); | ||||
|            o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|            o.setTemplateCode("test_01"); | ||||
|            o.setTemplateType(10); | ||||
|            o.setCreateTime(buildTime(2022, 1, 2)); | ||||
|            o.setTemplateParams(randomTemplateParams()); | ||||
|        }); | ||||
|        notifyMessageMapper.insert(dbNotifyMessage); | ||||
|        // 测试 userId 不匹配 | ||||
|        notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|        // 测试 userType 不匹配 | ||||
|        notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|        // 测试 templateCode 不匹配 | ||||
|        notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11"))); | ||||
|        // 测试 templateType 不匹配 | ||||
|        notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20))); | ||||
|        // 测试 createTime 不匹配 | ||||
|        notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); | ||||
|        // 准备参数 | ||||
|        NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO(); | ||||
|        reqVO.setUserId(1L); | ||||
|        reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|        reqVO.setTemplateCode("est_01"); | ||||
|        reqVO.setTemplateType(10); | ||||
|        reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotifyMessage() { | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, | ||||
|                 o -> o.setTemplateParams(randomTemplateParams())); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 准备参数 | ||||
|         Long id = dbNotifyMessage.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id); | ||||
|         assertPojoEquals(dbNotifyMessage, notifyMessage); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMyNotifyMessagePage() { | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(1L); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setReadStatus(true); | ||||
|             o.setCreateTime(buildTime(2022, 1, 2)); | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 测试 userId 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 readStatus 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO(); | ||||
|         reqVO.setReadStatus(true); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUnreadNotifyMessageList() { | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(1L); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setReadStatus(false); | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 测试 userId 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 readStatus 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         Integer size = 10; | ||||
|  | ||||
|         // 调用 | ||||
|         List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbNotifyMessage, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUnreadNotifyMessageCount() { | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(1L); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setReadStatus(false); | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 测试 userId 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 readStatus 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateNotifyMessageRead() { | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(1L); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setReadStatus(false); | ||||
|             o.setReadTime(null); | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 测试 userId 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 readStatus 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); | ||||
|         // 准备参数 | ||||
|         Collection<Long> ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1, | ||||
|                 dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3); | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|  | ||||
|         // 调用 | ||||
|         int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType); | ||||
|         // 断言 | ||||
|         assertEquals(1, updateCount); | ||||
|         NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); | ||||
|         assertTrue(notifyMessage.getReadStatus()); | ||||
|         assertNotNull(notifyMessage.getReadTime()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateAllNotifyMessageRead() { | ||||
|         // mock 数据 | ||||
|         NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(1L); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setReadStatus(false); | ||||
|             o.setReadTime(null); | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|         }); | ||||
|         notifyMessageMapper.insert(dbNotifyMessage); | ||||
|         // 测试 userId 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 readStatus 不匹配 | ||||
|         notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|  | ||||
|         // 调用 | ||||
|         int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType); | ||||
|         // 断言 | ||||
|         assertEquals(1, updateCount); | ||||
|         NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); | ||||
|         assertTrue(notifyMessage.getReadStatus()); | ||||
|         assertNotNull(notifyMessage.getReadTime()); | ||||
|     } | ||||
|  | ||||
|     private static Map<String, Object> randomTemplateParams() { | ||||
|         return MapUtil.<String, Object>builder().put(randomString(), randomString()) | ||||
|                 .put(randomString(), randomString()).build(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,190 @@ | ||||
| package cn.iocoder.yudao.module.system.service.notify; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Assertions; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_SEND_TEMPLATE_PARAM_MISS; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| class NotifySendServiceImplTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private NotifySendServiceImpl notifySendService; | ||||
|  | ||||
|     @Mock | ||||
|     private NotifyTemplateService notifyTemplateService; | ||||
|     @Mock | ||||
|     private NotifyMessageService notifyMessageService; | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleNotifyToAdmin() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock NotifyTemplateService 的方法 | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String content = randomString(); | ||||
|         when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock NotifyMessageService 的方法 | ||||
|         Long messageId = randomLongId(); | ||||
|         when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), | ||||
|                 eq(template), eq(content), eq(templateParams))).thenReturn(messageId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMessageId = notifySendService.sendSingleNotifyToAdmin(userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(messageId, resultMessageId); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleNotifyToMember() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock NotifyTemplateService 的方法 | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String content = randomString(); | ||||
|         when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock NotifyMessageService 的方法 | ||||
|         Long messageId = randomLongId(); | ||||
|         when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), | ||||
|                 eq(template), eq(content), eq(templateParams))).thenReturn(messageId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMessageId = notifySendService.sendSingleNotifyToMember(userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(messageId, resultMessageId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 发送成功,当短信模板开启时 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testSendSingleNotify_successWhenMailTemplateEnable() { | ||||
|         // 准备参数 | ||||
|         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 NotifyTemplateService 的方法 | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|         String content = randomString(); | ||||
|         when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) | ||||
|                 .thenReturn(content); | ||||
|         // mock NotifyMessageService 的方法 | ||||
|         Long messageId = randomLongId(); | ||||
|         when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType), | ||||
|                 eq(template), eq(content), eq(templateParams))).thenReturn(messageId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(messageId, resultMessageId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 发送成功,当短信模板关闭时 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testSendSingleMail_successWhenSmsTemplateDisable() { | ||||
|         // 准备参数 | ||||
|         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 NotifyTemplateService 的方法 | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { | ||||
|             o.setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|             o.setContent("验证码为{code}, 操作为{op}"); | ||||
|             o.setParams(Lists.newArrayList("code", "op")); | ||||
|         }); | ||||
|         when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertNull(resultMessageId); | ||||
|         verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap()); | ||||
|         verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckMailTemplateValid_notExists() { | ||||
|         // 准备参数 | ||||
|         String templateCode = randomString(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> notifySendService.validateNotifyTemplate(templateCode), | ||||
|                 NOTICE_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckTemplateParams_paramMiss() { | ||||
|         // 准备参数 | ||||
|         NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, | ||||
|                 o -> o.setParams(Lists.newArrayList("code"))); | ||||
|         Map<String, Object> templateParams = new HashMap<>(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> notifySendService.validateTemplateParams(template, templateParams), | ||||
|                 NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendBatchNotify() { | ||||
|         // 准备参数 | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         UnsupportedOperationException exception = Assertions.assertThrows( | ||||
|                 UnsupportedOperationException.class, | ||||
|                 () -> notifySendService.sendBatchNotify(null, null, null, null, null) | ||||
|         ); | ||||
|         // 断言 | ||||
|         assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,178 @@ | ||||
| package cn.iocoder.yudao.module.system.service.notify; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link NotifyTemplateServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(NotifyTemplateServiceImpl.class) | ||||
| public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private NotifyTemplateServiceImpl notifyTemplateService; | ||||
|  | ||||
|     @Resource | ||||
|     private NotifyTemplateMapper notifyTemplateMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateNotifyTemplate_success() { | ||||
|         // 准备参数 | ||||
|         NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(notifyTemplateId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); | ||||
|         assertPojoEquals(reqVO, notifyTemplate, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateNotifyTemplate_success() { | ||||
|         // mock 数据 | ||||
|         NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); | ||||
|         notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, o -> { | ||||
|             o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         notifyTemplateService.updateNotifyTemplate(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, notifyTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateNotifyTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteNotifyTemplate_success() { | ||||
|         // mock 数据 | ||||
|         NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); | ||||
|         notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbNotifyTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         notifyTemplateService.deleteNotifyTemplate(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(notifyTemplateMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteNotifyTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotifyTemplatePage() { | ||||
|         // mock 数据 | ||||
|         NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 | ||||
|             o.setName("芋头"); | ||||
|             o.setCode("test_01"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2022, 2, 3)); | ||||
|         }); | ||||
|         notifyTemplateMapper.insert(dbNotifyTemplate); | ||||
|         // 测试 name 不匹配 | ||||
|         notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); | ||||
|         // 测试 code 不匹配 | ||||
|         notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); | ||||
|         // 测试 status 不匹配 | ||||
|         notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); | ||||
|         // 准备参数 | ||||
|         NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); | ||||
|         reqVO.setName("芋"); | ||||
|         reqVO.setCode("est_01"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotifyTemplate() { | ||||
|         // mock 数据 | ||||
|         NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); | ||||
|         notifyTemplateMapper.insert(dbNotifyTemplate); | ||||
|         // 准备参数 | ||||
|         Long id = dbNotifyTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbNotifyTemplate, notifyTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetNotifyTemplateByCodeFromCache() { | ||||
|         // mock 数据 | ||||
|         NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); | ||||
|         notifyTemplateMapper.insert(dbNotifyTemplate); | ||||
|         // 准备参数 | ||||
|         String code = dbNotifyTemplate.getCode(); | ||||
|  | ||||
|         // 调用 | ||||
|         NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplateByCodeFromCache(code); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbNotifyTemplate, notifyTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testFormatNotifyTemplateContent() { | ||||
|         // 准备参数 | ||||
|         Map<String, Object> params = new HashMap<>(); | ||||
|         params.put("name", "小红"); | ||||
|         params.put("what", "饭"); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertEquals("小红,你好,饭吃了吗?", | ||||
|                 notifyTemplateService.formatNotifyTemplateContent("{name},你好,{what}吃了吗?", params)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,269 @@ | ||||
| package cn.iocoder.yudao.module.system.service.oauth2; | ||||
|  | ||||
| import cn.hutool.core.date.LocalDateTimeUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; | ||||
| import org.assertj.core.util.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.time.LocalDateTime; | ||||
| import java.time.ZoneId; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.*; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.*; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2ApproveServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(OAuth2ApproveServiceImpl.class) | ||||
| public class OAuth2ApproveServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2ApproveServiceImpl oauth2ApproveService; | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2ApproveMapper oauth2ApproveMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private OAuth2ClientService oauth2ClientService; | ||||
|  | ||||
|     @Test | ||||
|     public void checkForPreApproval_clientAutoApprove() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> requestedScopes = Lists.newArrayList("read"); | ||||
|         // mock 方法 | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) | ||||
|                 .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(requestedScopes)); | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, | ||||
|                 clientId, requestedScopes); | ||||
|         // 断言 | ||||
|         assertTrue(success); | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(userId, result.get(0).getUserId()); | ||||
|         assertEquals(userType, result.get(0).getUserType()); | ||||
|         assertEquals(clientId, result.get(0).getClientId()); | ||||
|         assertEquals("read", result.get(0).getScope()); | ||||
|         assertTrue(result.get(0).getApproved()); | ||||
|         assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void checkForPreApproval_approve() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> requestedScopes = Lists.newArrayList("read"); | ||||
|         // mock 方法 | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) | ||||
|                 .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); | ||||
|         // mock 数据 | ||||
|         OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) | ||||
|                 .setUserType(userType).setClientId(clientId).setScope("read") | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(true); // 同意 | ||||
|         oauth2ApproveMapper.insert(approve); | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, | ||||
|                 clientId, requestedScopes); | ||||
|         // 断言 | ||||
|         assertTrue(success); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void checkForPreApproval_reject() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> requestedScopes = Lists.newArrayList("read"); | ||||
|         // mock 方法 | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) | ||||
|                 .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); | ||||
|         // mock 数据 | ||||
|         OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) | ||||
|                 .setUserType(userType).setClientId(clientId).setScope("read") | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(false); // 拒绝 | ||||
|         oauth2ApproveMapper.insert(approve); | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, | ||||
|                 clientId, requestedScopes); | ||||
|         // 断言 | ||||
|         assertFalse(success); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateAfterApproval_none() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, | ||||
|                 null); | ||||
|         // 断言 | ||||
|         assertTrue(success); | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(0, result.size()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateAfterApproval_approved() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         Map<String, Boolean> requestedScopes = new LinkedHashMap<>(); // 有序,方便判断 | ||||
|         requestedScopes.put("read", true); | ||||
|         requestedScopes.put("write", false); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, | ||||
|                 requestedScopes); | ||||
|         // 断言 | ||||
|         assertTrue(success); | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(2, result.size()); | ||||
|         // read | ||||
|         assertEquals(userId, result.get(0).getUserId()); | ||||
|         assertEquals(userType, result.get(0).getUserType()); | ||||
|         assertEquals(clientId, result.get(0).getClientId()); | ||||
|         assertEquals("read", result.get(0).getScope()); | ||||
|         assertTrue(result.get(0).getApproved()); | ||||
|         assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); | ||||
|         // write | ||||
|         assertEquals(userId, result.get(1).getUserId()); | ||||
|         assertEquals(userType, result.get(1).getUserType()); | ||||
|         assertEquals(clientId, result.get(1).getClientId()); | ||||
|         assertEquals("write", result.get(1).getScope()); | ||||
|         assertFalse(result.get(1).getApproved()); | ||||
|         assertFalse(DateUtils.isExpired(result.get(1).getExpiresTime())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateAfterApproval_reject() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         Map<String, Boolean> requestedScopes = new LinkedHashMap<>(); | ||||
|         requestedScopes.put("write", false); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, | ||||
|                 requestedScopes); | ||||
|         // 断言 | ||||
|         assertFalse(success); | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(1, result.size()); | ||||
|         // write | ||||
|         assertEquals(userId, result.get(0).getUserId()); | ||||
|         assertEquals(userType, result.get(0).getUserType()); | ||||
|         assertEquals(clientId, result.get(0).getClientId()); | ||||
|         assertEquals("write", result.get(0).getScope()); | ||||
|         assertFalse(result.get(0).getApproved()); | ||||
|         assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetApproveList() { | ||||
|         // 准备参数 | ||||
|         Long userId = 10L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 数据 | ||||
|         OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) | ||||
|                 .setUserType(userType).setClientId(clientId).setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)); | ||||
|         oauth2ApproveMapper.insert(approve); // 未过期 | ||||
|         oauth2ApproveMapper.insert(ObjectUtil.clone(approve).setId(null) | ||||
|                 .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), -1L, ChronoUnit.DAYS))); // 已过期 | ||||
|  | ||||
|         // 调用 | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveService.getApproveList(userId, userType, clientId); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertPojoEquals(approve, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSaveApprove_insert() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         String scope = randomString(); | ||||
|         Boolean approved = randomBoolean(); | ||||
|         LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         oauth2ApproveService.saveApprove(userId, userType, clientId, | ||||
|                 scope, approved, expireTime); | ||||
|         // 断言 | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(userId, result.get(0).getUserId()); | ||||
|         assertEquals(userType, result.get(0).getUserType()); | ||||
|         assertEquals(clientId, result.get(0).getClientId()); | ||||
|         assertEquals(scope, result.get(0).getScope()); | ||||
|         assertEquals(approved, result.get(0).getApproved()); | ||||
|         assertEquals(expireTime, result.get(0).getExpiresTime()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSaveApprove_update() { | ||||
|         // mock 数据 | ||||
|         OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class); | ||||
|         oauth2ApproveMapper.insert(approve); | ||||
|         // 准备参数 | ||||
|         Long userId = approve.getUserId(); | ||||
|         Integer userType = approve.getUserType(); | ||||
|         String clientId = approve.getClientId(); | ||||
|         String scope = approve.getScope(); | ||||
|         Boolean approved = randomBoolean(); | ||||
|         LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         oauth2ApproveService.saveApprove(userId, userType, clientId, | ||||
|                 scope, approved, expireTime); | ||||
|         // 断言 | ||||
|         List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList(); | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(approve.getId(), result.get(0).getId()); | ||||
|         assertEquals(userId, result.get(0).getUserId()); | ||||
|         assertEquals(userType, result.get(0).getUserType()); | ||||
|         assertEquals(clientId, result.get(0).getClientId()); | ||||
|         assertEquals(scope, result.get(0).getScope()); | ||||
|         assertEquals(approved, result.get(0).getApproved()); | ||||
|         assertEquals(expireTime, result.get(0).getExpiresTime()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,220 @@ | ||||
| package cn.iocoder.yudao.module.system.service.oauth2; | ||||
|  | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.MockedStatic; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Collections; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.mockStatic; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2ClientServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(OAuth2ClientServiceImpl.class) | ||||
| public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2ClientServiceImpl oauth2ClientService; | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2ClientMapper oauth2ClientMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateOAuth2Client_success() { | ||||
|         // 准备参数 | ||||
|         OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, | ||||
|                 o -> o.setLogo(randomString())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long oauth2ClientId = oauth2ClientService.createOAuth2Client(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(oauth2ClientId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); | ||||
|         assertPojoEquals(reqVO, oAuth2Client, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateOAuth2Client_success() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); | ||||
|         oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, o -> { | ||||
|             o.setId(dbOAuth2Client.getId()); // 设置更新的 ID | ||||
|             o.setLogo(randomString()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         oauth2ClientService.updateOAuth2Client(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, oAuth2Client); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateOAuth2Client_notExists() { | ||||
|         // 准备参数 | ||||
|         OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> oauth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteOAuth2Client_success() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); | ||||
|         oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbOAuth2Client.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         oauth2ClientService.deleteOAuth2Client(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(oauth2ClientMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteOAuth2Client_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> oauth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateClientIdExists_withId() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); | ||||
|         oauth2ClientMapper.insert(client); | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String clientId = "tudou"; | ||||
|  | ||||
|         // 调用,不会报错 | ||||
|         assertServiceException(() -> oauth2ClientService.validateClientIdExists(id, clientId), OAUTH2_CLIENT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateClientIdExists_noId() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); | ||||
|         oauth2ClientMapper.insert(client); | ||||
|         // 准备参数 | ||||
|         String clientId = "tudou"; | ||||
|  | ||||
|         // 调用,不会报错 | ||||
|         assertServiceException(() -> oauth2ClientService.validateClientIdExists(null, clientId), OAUTH2_CLIENT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetOAuth2Client() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); | ||||
|         oauth2ClientMapper.insert(clientDO); | ||||
|         // 准备参数 | ||||
|         Long id = clientDO.getId(); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2Client(id); | ||||
|         assertPojoEquals(clientDO, dbClientDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetOAuth2ClientFromCache() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); | ||||
|         oauth2ClientMapper.insert(clientDO); | ||||
|         // 准备参数 | ||||
|         String clientId = clientDO.getClientId(); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId); | ||||
|         assertPojoEquals(clientDO, dbClientDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetOAuth2ClientPage() { | ||||
|         // mock 数据 | ||||
|         OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 | ||||
|             o.setName("潜龙"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         oauth2ClientMapper.insert(dbOAuth2Client); | ||||
|         // 测试 name 不匹配 | ||||
|         oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); | ||||
|         // 测试 status 不匹配 | ||||
|         oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); | ||||
|         reqVO.setName("龙"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<OAuth2ClientDO> pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidOAuthClientFromCache() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class))) | ||||
|                     .thenReturn(oauth2ClientService); | ||||
|  | ||||
|             // mock 方法 | ||||
|             OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             oauth2ClientMapper.insert(client); | ||||
|             OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") | ||||
|                     .setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|             oauth2ClientMapper.insert(client02); | ||||
|  | ||||
|             // 调用,并断言 | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), | ||||
|                     null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", | ||||
|                     null, null, null, null), OAUTH2_CLIENT_DISABLE); | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", | ||||
|                     randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", | ||||
|                     null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", | ||||
|                     null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); | ||||
|             assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", | ||||
|                     null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); | ||||
|             // 成功调用(1:参数完整) | ||||
|             OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), | ||||
|                     client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); | ||||
|             assertPojoEquals(client, result); | ||||
|             // 成功调用(2:只有 clientId 参数) | ||||
|             result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); | ||||
|             assertPojoEquals(client, result); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,99 @@ | ||||
| package cn.iocoder.yudao.module.system.service.oauth2; | ||||
|  | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2CodeServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(OAuth2CodeServiceImpl.class) | ||||
| class OAuth2CodeServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2CodeServiceImpl oauth2CodeService; | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2CodeMapper oauth2CodeMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateAuthorizationCode() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = RandomUtil.randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         String redirectUri = randomString(); | ||||
|         String state = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         OAuth2CodeDO codeDO = oauth2CodeService.createAuthorizationCode(userId, userType, clientId, | ||||
|                 scopes, redirectUri, state); | ||||
|         // 断言 | ||||
|         OAuth2CodeDO dbCodeDO = oauth2CodeMapper.selectByCode(codeDO.getCode()); | ||||
|         assertPojoEquals(codeDO, dbCodeDO, "createTime", "updateTime", "deleted"); | ||||
|         assertEquals(userId, codeDO.getUserId()); | ||||
|         assertEquals(userType, codeDO.getUserType()); | ||||
|         assertEquals(clientId, codeDO.getClientId()); | ||||
|         assertEquals(scopes, codeDO.getScopes()); | ||||
|         assertEquals(redirectUri, codeDO.getRedirectUri()); | ||||
|         assertEquals(state, codeDO.getState()); | ||||
|         assertFalse(DateUtils.isExpired(codeDO.getExpiresTime())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testConsumeAuthorizationCode_null() { | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(randomString()), | ||||
|                 OAUTH2_CODE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testConsumeAuthorizationCode_expired() { | ||||
|         // 准备参数 | ||||
|         String code = "test_code"; | ||||
|         // mock 数据 | ||||
|         OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) | ||||
|                 .setExpiresTime(LocalDateTime.now().minusDays(1)); | ||||
|         oauth2CodeMapper.insert(codeDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(code), | ||||
|                 OAUTH2_CODE_EXPIRE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testConsumeAuthorizationCode_success() { | ||||
|         // 准备参数 | ||||
|         String code = "test_code"; | ||||
|         // mock 数据 | ||||
|         OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) | ||||
|                 .setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         oauth2CodeMapper.insert(codeDO); | ||||
|  | ||||
|         // 调用 | ||||
|         OAuth2CodeDO result = oauth2CodeService.consumeAuthorizationCode(code); | ||||
|         assertPojoEquals(codeDO, result); | ||||
|         assertNull(oauth2CodeMapper.selectByCode(code)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,173 @@ | ||||
| package cn.iocoder.yudao.module.system.service.oauth2; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; | ||||
| import com.google.common.collect.Lists; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static java.util.Collections.emptyList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2GrantServiceImpl} 的单元测试 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private OAuth2GrantServiceImpl oauth2GrantService; | ||||
|  | ||||
|     @Mock | ||||
|     private OAuth2TokenService oauth2TokenService; | ||||
|     @Mock | ||||
|     private OAuth2CodeService oauth2CodeService; | ||||
|     @Mock | ||||
|     private AdminAuthService adminAuthService; | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantImplicit() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         // mock 方法 | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.createAccessToken(eq(userId), eq(userType), | ||||
|                 eq(clientId), eq(scopes))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertPojoEquals(accessTokenDO, oauth2GrantService.grantImplicit( | ||||
|                 userId, userType, clientId, scopes)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantAuthorizationCodeForCode() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         String redirectUri = randomString(); | ||||
|         String state = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class); | ||||
|         when(oauth2CodeService.createAuthorizationCode(eq(userId), eq(userType), | ||||
|                 eq(clientId), eq(scopes), eq(redirectUri), eq(state))).thenReturn(codeDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertEquals(codeDO.getCode(), oauth2GrantService.grantAuthorizationCodeForCode(userId, userType, | ||||
|                 clientId, scopes, redirectUri, state)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantAuthorizationCodeForAccessToken() { | ||||
|         // 准备参数 | ||||
|         String clientId = randomString(); | ||||
|         String code = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         String redirectUri = randomString(); | ||||
|         String state = randomString(); | ||||
|         // mock 方法(code) | ||||
|         OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class, o -> { | ||||
|             o.setClientId(clientId); | ||||
|             o.setRedirectUri(redirectUri); | ||||
|             o.setState(state); | ||||
|             o.setScopes(scopes); | ||||
|         }); | ||||
|         when(oauth2CodeService.consumeAuthorizationCode(eq(code))).thenReturn(codeDO); | ||||
|         // mock 方法(创建令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.createAccessToken(eq(codeDO.getUserId()), eq(codeDO.getUserType()), | ||||
|                 eq(codeDO.getClientId()), eq(codeDO.getScopes()))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertPojoEquals(accessTokenDO, oauth2GrantService.grantAuthorizationCodeForAccessToken( | ||||
|                 clientId, code, redirectUri, state)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantPassword() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         String password = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         // mock 方法(认证) | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class); | ||||
|         when(adminAuthService.authenticate(eq(username), eq(password))).thenReturn(user); | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()), | ||||
|                 eq(clientId), eq(scopes))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertPojoEquals(accessTokenDO, oauth2GrantService.grantPassword( | ||||
|                 username, password, clientId, scopes)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantRefreshToken() { | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq(clientId))) | ||||
|                 .thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertPojoEquals(accessTokenDO, oauth2GrantService.grantRefreshToken( | ||||
|                 refreshToken, clientId)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGrantClientCredentials() { | ||||
|         assertThrows(UnsupportedOperationException.class, | ||||
|                 () -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()), | ||||
|                 "暂时不支持 client_credentials 授权模式"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRevokeToken_clientIdError() { | ||||
|         // 准备参数 | ||||
|         String clientId = randomString(); | ||||
|         String accessToken = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); | ||||
|         when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertFalse(oauth2GrantService.revokeToken(clientId, accessToken)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRevokeToken_success() { | ||||
|         // 准备参数 | ||||
|         String clientId = randomString(); | ||||
|         String accessToken = randomString(); | ||||
|         // mock 方法(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setClientId(clientId); | ||||
|         when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); | ||||
|         // mock 方法(移除) | ||||
|         when(oauth2TokenService.removeAccessToken(eq(accessToken))).thenReturn(accessTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertTrue(oauth2GrantService.revokeToken(clientId, accessToken)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,289 @@ | ||||
| package cn.iocoder.yudao.module.system.service.oauth2; | ||||
|  | ||||
| import cn.hutool.core.date.LocalDateTimeUtil; | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.exception.ErrorCode; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||
| import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; | ||||
| import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; | ||||
| import org.assertj.core.util.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.time.LocalDateTime; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link OAuth2TokenServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import({OAuth2TokenServiceImpl.class, OAuth2AccessTokenRedisDAO.class}) | ||||
| public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2TokenServiceImpl oauth2TokenService; | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2AccessTokenMapper oauth2AccessTokenMapper; | ||||
|     @Resource | ||||
|     private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; | ||||
|  | ||||
|     @Resource | ||||
|     private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; | ||||
|  | ||||
|     @MockBean | ||||
|     private OAuth2ClientService oauth2ClientService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateAccessToken() { | ||||
|         TenantContextHolder.setTenantId(0L); | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = RandomUtil.randomEle(UserTypeEnum.values()).getValue(); | ||||
|         String clientId = randomString(); | ||||
|         List<String> scopes = Lists.newArrayList("read", "write"); | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) | ||||
|                 .setAccessTokenValiditySeconds(30).setRefreshTokenValiditySeconds(60); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); | ||||
|  | ||||
|         // 调用 | ||||
|         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); | ||||
|         // 断言访问令牌 | ||||
|         OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken()); | ||||
|         assertPojoEquals(accessTokenDO, dbAccessTokenDO, "createTime", "updateTime", "deleted"); | ||||
|         assertEquals(userId, accessTokenDO.getUserId()); | ||||
|         assertEquals(userType, accessTokenDO.getUserType()); | ||||
|         assertEquals(clientId, accessTokenDO.getClientId()); | ||||
|         assertEquals(scopes, accessTokenDO.getScopes()); | ||||
|         assertFalse(DateUtils.isExpired(accessTokenDO.getExpiresTime())); | ||||
|         // 断言访问令牌的缓存 | ||||
|         OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken()); | ||||
|         assertPojoEquals(accessTokenDO, redisAccessTokenDO, "createTime", "updateTime", "deleted"); | ||||
|         // 断言刷新令牌 | ||||
|         OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectList().get(0); | ||||
|         assertPojoEquals(accessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted"); | ||||
|         assertFalse(DateUtils.isExpired(refreshTokenDO.getExpiresTime())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshAccessToken_null() { | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), | ||||
|                 new ErrorCode(400, "无效的刷新令牌")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshAccessToken_clientIdError() { | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) | ||||
|                 .setRefreshToken(refreshToken).setClientId("error"); | ||||
|         oauth2RefreshTokenMapper.insert(refreshTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), | ||||
|                 new ErrorCode(400, "刷新令牌的客户端编号不正确")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshAccessToken_expired() { | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) | ||||
|                 .setRefreshToken(refreshToken).setClientId(clientId) | ||||
|                 .setExpiresTime(LocalDateTime.now().minusDays(1)); | ||||
|         oauth2RefreshTokenMapper.insert(refreshTokenDO); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), | ||||
|                 new ErrorCode(401, "刷新令牌已过期")); | ||||
|         assertEquals(0, oauth2RefreshTokenMapper.selectCount()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshAccessToken_success() { | ||||
|         TenantContextHolder.setTenantId(0L); | ||||
|         // 准备参数 | ||||
|         String refreshToken = randomString(); | ||||
|         String clientId = randomString(); | ||||
|         // mock 方法 | ||||
|         OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) | ||||
|                 .setAccessTokenValiditySeconds(30); | ||||
|         when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) | ||||
|                 .setRefreshToken(refreshToken).setClientId(clientId) | ||||
|                 .setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         oauth2RefreshTokenMapper.insert(refreshTokenDO); | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setRefreshToken(refreshToken); | ||||
|         oauth2AccessTokenMapper.insert(accessTokenDO); | ||||
|         oauth2AccessTokenRedisDAO.set(accessTokenDO); | ||||
|  | ||||
|         // 调用 | ||||
|         OAuth2AccessTokenDO newAccessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); | ||||
|         // 断言,老的访问令牌被删除 | ||||
|         assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); | ||||
|         assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); | ||||
|         // 断言,新的访问令牌 | ||||
|         OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(newAccessTokenDO.getAccessToken()); | ||||
|         assertPojoEquals(newAccessTokenDO, dbAccessTokenDO, "createTime", "updateTime", "deleted"); | ||||
|         assertPojoEquals(newAccessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted", | ||||
|                 "creator", "updater"); | ||||
|         assertFalse(DateUtils.isExpired(newAccessTokenDO.getExpiresTime())); | ||||
|         // 断言,新的访问令牌的缓存 | ||||
|         OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(newAccessTokenDO.getAccessToken()); | ||||
|         assertPojoEquals(newAccessTokenDO, redisAccessTokenDO, "createTime", "updateTime", "deleted"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetAccessToken() { | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         oauth2AccessTokenMapper.insert(accessTokenDO); | ||||
|         // 准备参数 | ||||
|         String accessToken = accessTokenDO.getAccessToken(); | ||||
|  | ||||
|         // 调用 | ||||
|         OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); | ||||
|         // 断言 | ||||
|         assertPojoEquals(accessTokenDO, result, "createTime", "updateTime", "deleted", | ||||
|                 "creator", "updater"); | ||||
|         assertPojoEquals(accessTokenDO, oauth2AccessTokenRedisDAO.get(accessToken), "createTime", "updateTime", "deleted", | ||||
|                 "creator", "updater"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckAccessToken_null() { | ||||
|         // 调研,并断言 | ||||
|         assertServiceException(() -> oauth2TokenService.checkAccessToken(randomString()), | ||||
|                 new ErrorCode(401, "访问令牌不存在")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckAccessToken_expired() { | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTime.now().minusDays(1)); | ||||
|         oauth2AccessTokenMapper.insert(accessTokenDO); | ||||
|         // 准备参数 | ||||
|         String accessToken = accessTokenDO.getAccessToken(); | ||||
|  | ||||
|         // 调研,并断言 | ||||
|         assertServiceException(() -> oauth2TokenService.checkAccessToken(accessToken), | ||||
|                 new ErrorCode(401, "访问令牌已过期")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckAccessToken_success() { | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         oauth2AccessTokenMapper.insert(accessTokenDO); | ||||
|         // 准备参数 | ||||
|         String accessToken = accessTokenDO.getAccessToken(); | ||||
|  | ||||
|         // 调研,并断言 | ||||
|         OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); | ||||
|         // 断言 | ||||
|         assertPojoEquals(accessTokenDO, result, "createTime", "updateTime", "deleted", | ||||
|                 "creator", "updater"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRemoveAccessToken_null() { | ||||
|         // 调用,并断言 | ||||
|         assertNull(oauth2TokenService.removeAccessToken(randomString())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRemoveAccessToken_success() { | ||||
|         // mock 数据(访问令牌) | ||||
|         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | ||||
|                 .setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         oauth2AccessTokenMapper.insert(accessTokenDO); | ||||
|         // mock 数据(刷新令牌) | ||||
|         OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) | ||||
|                 .setRefreshToken(accessTokenDO.getRefreshToken()); | ||||
|         oauth2RefreshTokenMapper.insert(refreshTokenDO); | ||||
|         // 调用 | ||||
|         OAuth2AccessTokenDO result = oauth2TokenService.removeAccessToken(accessTokenDO.getAccessToken()); | ||||
|         assertPojoEquals(accessTokenDO, result, "createTime", "updateTime", "deleted", | ||||
|                 "creator", "updater"); | ||||
|         // 断言数据 | ||||
|         assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); | ||||
|         assertNull(oauth2RefreshTokenMapper.selectByRefreshToken(accessTokenDO.getRefreshToken())); | ||||
|         assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Test | ||||
|     public void testGetAccessTokenPage() { | ||||
|         // mock 数据 | ||||
|         OAuth2AccessTokenDO dbAccessToken = randomPojo(OAuth2AccessTokenDO.class, o -> { // 等会查询到 | ||||
|             o.setUserId(10L); | ||||
|             o.setUserType(1); | ||||
|             o.setClientId("test_client"); | ||||
|             o.setExpiresTime(LocalDateTime.now().plusDays(1)); | ||||
|         }); | ||||
|         oauth2AccessTokenMapper.insert(dbAccessToken); | ||||
|         // 测试 userId 不匹配 | ||||
|         oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserId(20L))); | ||||
|         // 测试 userType 不匹配 | ||||
|         oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserType(2))); | ||||
|         // 测试 userType 不匹配 | ||||
|         oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("it_client"))); | ||||
|         // 测试 expireTime 不匹配 | ||||
|         oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTimeUtil.now()))); | ||||
|         // 准备参数 | ||||
|         OAuth2AccessTokenPageReqVO reqVO = new OAuth2AccessTokenPageReqVO(); | ||||
|         reqVO.setUserId(10L); | ||||
|         reqVO.setUserType(1); | ||||
|         reqVO.setClientId("test"); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbAccessToken, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,331 @@ | ||||
| package cn.iocoder.yudao.module.system.service.permission; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.service.tenant.TenantService; | ||||
| 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.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||||
| import static org.mockito.ArgumentMatchers.argThat; | ||||
| import static org.mockito.Mockito.doNothing; | ||||
| import static org.mockito.Mockito.verify; | ||||
|  | ||||
| @Import(MenuServiceImpl.class) | ||||
| public class MenuServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private MenuServiceImpl menuService; | ||||
|  | ||||
|     @Resource | ||||
|     private MenuMapper menuMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private PermissionService permissionService; | ||||
|     @MockBean | ||||
|     private TenantService tenantService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateMenu_success() { | ||||
|         // mock 数据(构造父菜单) | ||||
|         MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, | ||||
|                 "parent", 0L); | ||||
|         menuMapper.insert(menuDO); | ||||
|         Long parentId = menuDO.getId(); | ||||
|         // 准备参数 | ||||
|         MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { | ||||
|             o.setParentId(parentId); | ||||
|             o.setName("testSonName"); | ||||
|             o.setType(MenuTypeEnum.MENU.getType()); | ||||
|         }).setId(null); // 防止 id 被赋值 | ||||
|         Long menuId = menuService.createMenu(reqVO); | ||||
|  | ||||
|         // 校验记录的属性是否正确 | ||||
|         MenuDO dbMenu = menuMapper.selectById(menuId); | ||||
|         assertPojoEquals(reqVO, dbMenu, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMenu_success() { | ||||
|         // mock 数据(构造父子菜单) | ||||
|         MenuDO sonMenuDO = createParentAndSonMenu(); | ||||
|         Long sonId = sonMenuDO.getId(); | ||||
|         // 准备参数 | ||||
|         MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { | ||||
|             o.setId(sonId); | ||||
|             o.setName("testSonName"); // 修改名字 | ||||
|             o.setParentId(sonMenuDO.getParentId()); | ||||
|             o.setType(MenuTypeEnum.MENU.getType()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         menuService.updateMenu(reqVO); | ||||
|         // 校验记录的属性是否正确 | ||||
|         MenuDO dbMenu = menuMapper.selectById(sonId); | ||||
|         assertPojoEquals(reqVO, dbMenu); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateMenu_sonIdNotExist() { | ||||
|         // 准备参数 | ||||
|         MenuSaveVO reqVO = randomPojo(MenuSaveVO.class); | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> menuService.updateMenu(reqVO), MENU_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMenu_success() { | ||||
|         // mock 数据 | ||||
|         MenuDO menuDO = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menuDO); | ||||
|         // 准备参数 | ||||
|         Long id = menuDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         menuService.deleteMenu(id); | ||||
|         // 断言 | ||||
|         MenuDO dbMenuDO = menuMapper.selectById(id); | ||||
|         assertNull(dbMenuDO); | ||||
|         verify(permissionService).processMenuDeleted(id); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMenu_menuNotExist() { | ||||
|         assertServiceException(() -> menuService.deleteMenu(randomLongId()), | ||||
|                 MENU_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteMenu_existChildren() { | ||||
|         // mock 数据(构造父子菜单) | ||||
|         MenuDO sonMenu = createParentAndSonMenu(); | ||||
|         // 准备参数 | ||||
|         Long parentId = sonMenu.getParentId(); | ||||
|  | ||||
|         // 调用并断言异常 | ||||
|         assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuList_all() { | ||||
|         // mock 数据 | ||||
|         MenuDO menu100 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu100); | ||||
|         MenuDO menu101 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu101); | ||||
|         // 准备参数 | ||||
|  | ||||
|         // 调用 | ||||
|         List<MenuDO> list = menuService.getMenuList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertPojoEquals(menu100, list.get(0)); | ||||
|         assertPojoEquals(menu101, list.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuList() { | ||||
|         // mock 数据 | ||||
|         MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("芋艿").setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         menuMapper.insert(menuDO); | ||||
|         // 测试 status 不匹配 | ||||
|         menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 name 不匹配 | ||||
|         menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setName("艿"))); | ||||
|         // 准备参数 | ||||
|         MenuListReqVO reqVO = new MenuListReqVO().setName("芋").setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<MenuDO> result = menuService.getMenuList(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertPojoEquals(menuDO, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuListByTenant() { | ||||
|         // mock 数据 | ||||
|         MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         menuMapper.insert(menu100); | ||||
|         MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         menuMapper.insert(menu101); | ||||
|         MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         menuMapper.insert(menu102); | ||||
|         // mock 过滤菜单 | ||||
|         Set<Long> menuIds = asSet(100L, 101L); | ||||
|         doNothing().when(tenantService).handleTenantMenu(argThat(handler -> { | ||||
|             handler.handle(menuIds); | ||||
|             return true; | ||||
|         })); | ||||
|         // 准备参数 | ||||
|         MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<MenuDO> result = menuService.getMenuListByTenant(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertPojoEquals(menu100, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuIdListByPermissionFromCache() { | ||||
|         // mock 数据 | ||||
|         MenuDO menu100 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu100); | ||||
|         MenuDO menu101 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu101); | ||||
|         // 准备参数 | ||||
|         String permission = menu100.getPermission(); | ||||
|  | ||||
|         // 调用 | ||||
|         List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission); | ||||
|         // 断言 | ||||
|         assertEquals(1, ids.size()); | ||||
|         assertEquals(menu100.getId(), ids.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuList_ids() { | ||||
|         // mock 数据 | ||||
|         MenuDO menu100 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu100); | ||||
|         MenuDO menu101 = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu101); | ||||
|         // 准备参数 | ||||
|         Collection<Long> ids = Collections.singleton(menu100.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<MenuDO> list = menuService.getMenuList(ids); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(menu100, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenu() { | ||||
|         // mock 数据 | ||||
|         MenuDO menu = randomPojo(MenuDO.class); | ||||
|         menuMapper.insert(menu); | ||||
|         // 准备参数 | ||||
|         Long id = menu.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         MenuDO dbMenu = menuService.getMenu(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(menu, dbMenu); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentMenu_success() { | ||||
|         // mock 数据 | ||||
|         MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); | ||||
|         menuMapper.insert(menuDO); | ||||
|         // 准备参数 | ||||
|         Long parentId = menuDO.getId(); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         menuService.validateParentMenu(parentId, null); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentMenu_canNotSetSelfToBeParent() { | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> menuService.validateParentMenu(1L, 1L), | ||||
|                 MENU_PARENT_ERROR); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentMenu_parentNotExist() { | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null), | ||||
|                 MENU_PARENT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateParentMenu_parentTypeError() { | ||||
|         // mock 数据 | ||||
|         MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); | ||||
|         menuMapper.insert(menuDO); | ||||
|         // 准备参数 | ||||
|         Long parentId = menuDO.getId(); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> menuService.validateParentMenu(parentId, null), | ||||
|                 MENU_PARENT_NOT_DIR_OR_MENU); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMenu_success() { | ||||
|         // mock 父子菜单 | ||||
|         MenuDO sonMenu = createParentAndSonMenu(); | ||||
|         // 准备参数 | ||||
|         Long parentId = sonMenu.getParentId(); | ||||
|         Long otherSonMenuId = randomLongId(); | ||||
|         String otherSonMenuName = randomString(); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMenu_sonMenuNameDuplicate() { | ||||
|         // mock 父子菜单 | ||||
|         MenuDO sonMenu = createParentAndSonMenu(); | ||||
|         // 准备参数 | ||||
|         Long parentId = sonMenu.getParentId(); | ||||
|         Long otherSonMenuId = randomLongId(); | ||||
|         String otherSonMenuName = sonMenu.getName(); //相同名称 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId), | ||||
|                 MENU_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     // ====================== 初始化方法 ====================== | ||||
|  | ||||
|     /** | ||||
|      * 插入父子菜单,返回子菜单 | ||||
|      * | ||||
|      * @return 子菜单 | ||||
|      */ | ||||
|     private MenuDO createParentAndSonMenu() { | ||||
|         // 构造父子菜单 | ||||
|         MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); | ||||
|         menuMapper.insert(parentMenuDO); | ||||
|         // 构建子菜单 | ||||
|         MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName", | ||||
|                 parentMenuDO.getParentId()); | ||||
|         menuMapper.insert(sonMenuDO); | ||||
|         return sonMenuDO; | ||||
|     } | ||||
|  | ||||
|     private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { | ||||
|         return buildMenuDO(type, name, parentId, randomCommonStatus()); | ||||
|     } | ||||
|  | ||||
|     private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { | ||||
|         return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) | ||||
|                 .setType(type.getType()).setStatus(status)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,527 @@ | ||||
| package cn.iocoder.yudao.module.system.service.permission; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | ||||
| import cn.iocoder.yudao.module.system.service.dept.DeptService; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.MockedStatic; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.hutool.core.collection.ListUtil.toList; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static java.util.Collections.singleton; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| @Import({PermissionServiceImpl.class}) | ||||
| public class PermissionServiceTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private PermissionServiceImpl permissionService; | ||||
|  | ||||
|     @Resource | ||||
|     private RoleMenuMapper roleMenuMapper; | ||||
|     @Resource | ||||
|     private UserRoleMapper userRoleMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private RoleService roleService; | ||||
|     @MockBean | ||||
|     private MenuService menuService; | ||||
|     @MockBean | ||||
|     private DeptService deptService; | ||||
|     @MockBean | ||||
|     private AdminUserService userService; | ||||
|  | ||||
|     @Test | ||||
|     public void testHasAnyPermissions_superAdmin() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             String[] roles = new String[]{"system:user:query", "system:user:create"}; | ||||
|             // mock 用户登录的角色 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); | ||||
|             RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); | ||||
|             // mock 其它方法 | ||||
|             when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); | ||||
|  | ||||
|             // 调用,并断言 | ||||
|             assertTrue(permissionService.hasAnyPermissions(userId, roles)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHasAnyPermissions_normal() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             String[] roles = new String[]{"system:user:query", "system:user:create"}; | ||||
|             // mock 用户登录的角色 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); | ||||
|             RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); | ||||
|             // mock 菜单 | ||||
|             Long menuId = 1000L; | ||||
|             when(menuService.getMenuIdListByPermissionFromCache( | ||||
|                     eq("system:user:create"))).thenReturn(singletonList(menuId)); | ||||
|             roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); | ||||
|  | ||||
|             // 调用,并断言 | ||||
|             assertTrue(permissionService.hasAnyPermissions(userId, roles)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHasAnyRoles() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             String[] roles = new String[]{"yunai", "tudou"}; | ||||
|             // mock 用户与角色的缓存 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); | ||||
|             RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); | ||||
|  | ||||
|             // 调用,并断言 | ||||
|             assertTrue(permissionService.hasAnyRoles(userId, roles)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // ========== 角色-菜单的相关方法  ========== | ||||
|  | ||||
|     @Test | ||||
|     public void testAssignRoleMenu() { | ||||
|         // 准备参数 | ||||
|         Long roleId = 1L; | ||||
|         Set<Long> menuIds = asSet(200L, 300L); | ||||
|         // mock 数据 | ||||
|         RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L); | ||||
|         roleMenuMapper.insert(roleMenu01); | ||||
|         RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L); | ||||
|         roleMenuMapper.insert(roleMenu02); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.assignRoleMenu(roleId, menuIds); | ||||
|         // 断言 | ||||
|         List<RoleMenuDO> roleMenuList = roleMenuMapper.selectList(); | ||||
|         assertEquals(2, roleMenuList.size()); | ||||
|         assertEquals(1L, roleMenuList.get(0).getRoleId()); | ||||
|         assertEquals(200L, roleMenuList.get(0).getMenuId()); | ||||
|         assertEquals(1L, roleMenuList.get(1).getRoleId()); | ||||
|         assertEquals(300L, roleMenuList.get(1).getMenuId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testProcessRoleDeleted() { | ||||
|         // 准备参数 | ||||
|         Long roleId = randomLongId(); | ||||
|         // mock 数据 UserRole | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除 | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 | ||||
|         userRoleMapper.insert(userRoleDO02); | ||||
|         // mock 数据 RoleMenu | ||||
|         RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除 | ||||
|         roleMenuMapper.insert(roleMenuDO01); | ||||
|         RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 | ||||
|         roleMenuMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.processRoleDeleted(roleId); | ||||
|         // 断言数据 RoleMenuDO | ||||
|         List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList(); | ||||
|         assertEquals(1, dbRoleMenus.size()); | ||||
|         assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); | ||||
|         // 断言数据 UserRoleDO | ||||
|         List<UserRoleDO> dbUserRoles = userRoleMapper.selectList(); | ||||
|         assertEquals(1, dbUserRoles.size()); | ||||
|         assertPojoEquals(dbUserRoles.get(0), userRoleDO02); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testProcessMenuDeleted() { | ||||
|         // 准备参数 | ||||
|         Long menuId = randomLongId(); | ||||
|         // mock 数据 | ||||
|         RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除 | ||||
|         roleMenuMapper.insert(roleMenuDO01); | ||||
|         RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 | ||||
|         roleMenuMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.processMenuDeleted(menuId); | ||||
|         // 断言数据 | ||||
|         List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList(); | ||||
|         assertEquals(1, dbRoleMenus.size()); | ||||
|         assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleMenuIds_superAdmin() { | ||||
|         // 准备参数 | ||||
|         Long roleId = 100L; | ||||
|         // mock 方法 | ||||
|         when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); | ||||
|         List<MenuDO> menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); | ||||
|         when(menuService.getMenuList()).thenReturn(menuList); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId); | ||||
|         // 断言 | ||||
|         assertEquals(singleton(1L), menuIds); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleMenuIds_normal() { | ||||
|         // 准备参数 | ||||
|         Long roleId = 100L; | ||||
|         // mock 数据 | ||||
|         RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); | ||||
|         roleMenuMapper.insert(roleMenu01); | ||||
|         RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); | ||||
|         roleMenuMapper.insert(roleMenu02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(1L, 2L), menuIds); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetMenuRoleIdListByMenuIdFromCache() { | ||||
|         // 准备参数 | ||||
|         Long menuId = 1L; | ||||
|         // mock 数据 | ||||
|         RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); | ||||
|         roleMenuMapper.insert(roleMenu01); | ||||
|         RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L); | ||||
|         roleMenuMapper.insert(roleMenu02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(100L, 200L), roleIds); | ||||
|     } | ||||
|  | ||||
|     // ========== 用户-角色的相关方法  ========== | ||||
|  | ||||
|     @Test | ||||
|     public void testAssignUserRole() { | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Set<Long> roleIds = asSet(200L, 300L); | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); | ||||
|         userRoleMapper.insert(userRole01); | ||||
|         UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); | ||||
|         userRoleMapper.insert(userRole02); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.assignUserRole(userId, roleIds); | ||||
|         // 断言 | ||||
|         List<UserRoleDO> userRoleDOList = userRoleMapper.selectList(); | ||||
|         assertEquals(2, userRoleDOList.size()); | ||||
|         assertEquals(1L, userRoleDOList.get(0).getUserId()); | ||||
|         assertEquals(200L, userRoleDOList.get(0).getRoleId()); | ||||
|         assertEquals(1L, userRoleDOList.get(1).getUserId()); | ||||
|         assertEquals(300L, userRoleDOList.get(1).getRoleId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testProcessUserDeleted() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除 | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 | ||||
|         userRoleMapper.insert(userRoleDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.processUserDeleted(userId); | ||||
|         // 断言数据 | ||||
|         List<UserRoleDO> dbUserRoles = userRoleMapper.selectList(); | ||||
|         assertEquals(1, dbUserRoles.size()); | ||||
|         assertPojoEquals(dbUserRoles.get(0), userRoleDO02); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserRoleIdListByUserId() { | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); | ||||
|         userRoleMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = permissionService.getUserRoleIdListByUserId(userId); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(10L, 20L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserRoleIdListByUserIdFromCache() { | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); | ||||
|         userRoleMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(10L, 20L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserRoleIdsFromCache() { | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); | ||||
|         userRoleMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(10L, 20L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserRoleIdListByRoleId() { | ||||
|         // 准备参数 | ||||
|         Collection<Long> roleIds = asSet(10L, 20L); | ||||
|         // mock 数据 | ||||
|         UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); | ||||
|         userRoleMapper.insert(userRoleDO01); | ||||
|         UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); | ||||
|         userRoleMapper.insert(roleMenuDO02); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = permissionService.getUserRoleIdListByRoleId(roleIds); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(1L, 2L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetEnableUserRoleListByUserIdFromCache() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户登录的角色 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); | ||||
|             RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) | ||||
|                     .setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) | ||||
|                     .thenReturn(toList(role01, role02)); | ||||
|  | ||||
|             // 调用 | ||||
|             List<RoleDO> result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); | ||||
|             // 断言 | ||||
|             assertEquals(1, result.size()); | ||||
|             assertPojoEquals(role01, result.get(0)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // ========== 用户-部门的相关方法  ========== | ||||
|  | ||||
|     @Test | ||||
|     public void testAssignRoleDataScope() { | ||||
|         // 准备参数 | ||||
|         Long roleId = 1L; | ||||
|         Integer dataScope = 2; | ||||
|         Set<Long> dataScopeDeptIds = asSet(10L, 20L); | ||||
|  | ||||
|         // 调用 | ||||
|         permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); | ||||
|         // 断言 | ||||
|         verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptDataPermission_All() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户的角色编号 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); | ||||
|             // mock 获得用户的角色 | ||||
|             RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); | ||||
|  | ||||
|             // 调用 | ||||
|             DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); | ||||
|             // 断言 | ||||
|             assertTrue(result.getAll()); | ||||
|             assertFalse(result.getSelf()); | ||||
|             assertTrue(CollUtil.isEmpty(result.getDeptIds())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptDataPermission_DeptCustom() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户的角色编号 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); | ||||
|             // mock 获得用户的角色 | ||||
|             RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); | ||||
|             // mock 部门的返回 | ||||
|             when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), | ||||
|                     null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||||
|  | ||||
|             // 调用 | ||||
|             DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); | ||||
|             // 断言 | ||||
|             assertFalse(result.getAll()); | ||||
|             assertFalse(result.getSelf()); | ||||
|             assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); | ||||
|             assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); | ||||
|             assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptDataPermission_DeptOnly() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户的角色编号 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); | ||||
|             // mock 获得用户的角色 | ||||
|             RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); | ||||
|             // mock 部门的返回 | ||||
|             when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), | ||||
|                     null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||||
|  | ||||
|             // 调用 | ||||
|             DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); | ||||
|             // 断言 | ||||
|             assertFalse(result.getAll()); | ||||
|             assertFalse(result.getSelf()); | ||||
|             assertEquals(1, result.getDeptIds().size()); | ||||
|             assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptDataPermission_DeptAndChild() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户的角色编号 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); | ||||
|             // mock 获得用户的角色 | ||||
|             RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); | ||||
|             // mock 部门的返回 | ||||
|             when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), | ||||
|                     null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||||
|             // mock 方法(部门) | ||||
|             DeptDO deptDO = randomPojo(DeptDO.class); | ||||
|             when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); | ||||
|  | ||||
|             // 调用 | ||||
|             DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); | ||||
|             // 断言 | ||||
|             assertFalse(result.getAll()); | ||||
|             assertFalse(result.getSelf()); | ||||
|             assertEquals(2, result.getDeptIds().size()); | ||||
|             assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); | ||||
|             assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDeptDataPermission_Self() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) | ||||
|                     .thenReturn(permissionService); | ||||
|  | ||||
|             // 准备参数 | ||||
|             Long userId = 1L; | ||||
|             // mock 用户的角色编号 | ||||
|             userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); | ||||
|             // mock 获得用户的角色 | ||||
|             RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) | ||||
|                     .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); | ||||
|  | ||||
|             // 调用 | ||||
|             DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); | ||||
|             // 断言 | ||||
|             assertFalse(result.getAll()); | ||||
|             assertTrue(result.getSelf()); | ||||
|             assertTrue(CollUtil.isEmpty(result.getDeptIds())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,388 @@ | ||||
| package cn.iocoder.yudao.module.system.service.permission; | ||||
|  | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.MockedStatic; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singleton; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.mockStatic; | ||||
| import static org.mockito.Mockito.verify; | ||||
|  | ||||
| @Import(RoleServiceImpl.class) | ||||
| public class RoleServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private RoleServiceImpl roleService; | ||||
|  | ||||
|     @Resource | ||||
|     private RoleMapper roleMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private PermissionService permissionService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateRole() { | ||||
|         // 准备参数 | ||||
|         RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long roleId = roleService.createRole(reqVO, null); | ||||
|         // 断言 | ||||
|         RoleDO roleDO = roleMapper.selectById(roleId); | ||||
|         assertPojoEquals(reqVO, roleDO, "id"); | ||||
|         assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); | ||||
|         assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus()); | ||||
|         assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateRole() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         Long id = roleDO.getId(); | ||||
|         RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class, o -> o.setId(id)); | ||||
|  | ||||
|         // 调用 | ||||
|         roleService.updateRole(reqVO); | ||||
|         // 断言 | ||||
|         RoleDO newRoleDO = roleMapper.selectById(id); | ||||
|         assertPojoEquals(reqVO, newRoleDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateRoleStatus() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setType(RoleTypeEnum.CUSTOM.getType())); | ||||
|         roleMapper.insert(roleDO); | ||||
|  | ||||
|         // 准备参数 | ||||
|         Long roleId = roleDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         roleService.updateRoleStatus(roleId, CommonStatusEnum.DISABLE.getStatus()); | ||||
|         // 断言 | ||||
|         RoleDO dbRoleDO = roleMapper.selectById(roleId); | ||||
|         assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateRoleDataScope() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         Long id = roleDO.getId(); | ||||
|         Integer dataScope = randomEle(DataScopeEnum.values()).getScope(); | ||||
|         Set<Long> dataScopeRoleIds = randomSet(Long.class); | ||||
|  | ||||
|         // 调用 | ||||
|         roleService.updateRoleDataScope(id, dataScope, dataScopeRoleIds); | ||||
|         // 断言 | ||||
|         RoleDO dbRoleDO = roleMapper.selectById(id); | ||||
|         assertEquals(dataScope, dbRoleDO.getDataScope()); | ||||
|         assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteRole() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 参数准备 | ||||
|         Long id = roleDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         roleService.deleteRole(id); | ||||
|         // 断言 | ||||
|         assertNull(roleMapper.selectById(id)); | ||||
|         // verify 删除相关数据 | ||||
|         verify(permissionService).processRoleDeleted(id); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleDuplicate_success() { | ||||
|         // 调用,不会抛异常 | ||||
|         roleService.validateRoleDuplicate(randomString(), randomString(), null); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleDuplicate_nameDuplicate() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         String name = "role_name"; | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), | ||||
|                 ROLE_NAME_DUPLICATE, name); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleDuplicate_codeDuplicate() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         String code = "code"; | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), | ||||
|                 ROLE_CODE_DUPLICATE, code); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUpdateRole_success() { | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         Long id = roleDO.getId(); | ||||
|  | ||||
|         // 调用,无异常 | ||||
|         roleService.validateRoleForUpdate(id); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUpdateRole_roleIdNotExist() { | ||||
|         assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         Long id = roleDO.getId(); | ||||
|  | ||||
|         assertServiceException(() -> roleService.validateRoleForUpdate(id), | ||||
|                 ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRole() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 参数准备 | ||||
|         Long id = roleDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         RoleDO dbRoleDO = roleService.getRole(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(roleDO, dbRoleDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleFromCache() { | ||||
|         // mock 数据(缓存) | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 参数准备 | ||||
|         Long id = roleDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         RoleDO dbRoleDO = roleService.getRoleFromCache(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(roleDO, dbRoleDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleListByStatus() { | ||||
|         // mock 数据 | ||||
|         RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole01); | ||||
|         RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole02); | ||||
|  | ||||
|         // 调用 | ||||
|         List<RoleDO> list = roleService.getRoleListByStatus( | ||||
|                 singleton(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbRole01, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleList() { | ||||
|         // mock 数据 | ||||
|         RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole01); | ||||
|         RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole02); | ||||
|  | ||||
|         // 调用 | ||||
|         List<RoleDO> list = roleService.getRoleList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertPojoEquals(dbRole01, list.get(0)); | ||||
|         assertPojoEquals(dbRole02, list.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleList_ids() { | ||||
|         // mock 数据 | ||||
|         RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole01); | ||||
|         RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         roleMapper.insert(dbRole02); | ||||
|         // 准备参数 | ||||
|         Collection<Long> ids = singleton(dbRole01.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<RoleDO> list = roleService.getRoleList(ids); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbRole01, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRoleListFromCache() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) | ||||
|                     .thenReturn(roleService); | ||||
|  | ||||
|             // mock 数据 | ||||
|             RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|             roleMapper.insert(dbRole); | ||||
|             // 测试 id 不匹配 | ||||
|             roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); | ||||
|             // 准备参数 | ||||
|             Collection<Long> ids = singleton(dbRole.getId()); | ||||
|  | ||||
|             // 调用 | ||||
|             List<RoleDO> list = roleService.getRoleListFromCache(ids); | ||||
|             // 断言 | ||||
|             assertEquals(1, list.size()); | ||||
|             assertPojoEquals(dbRole, list.get(0)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetRolePage() { | ||||
|         // mock 数据 | ||||
|         RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到 | ||||
|             o.setName("土豆"); | ||||
|             o.setCode("tudou"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2022, 2, 8)); | ||||
|         }); | ||||
|         roleMapper.insert(dbRole); | ||||
|         // 测试 name 不匹配 | ||||
|         roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯"))); | ||||
|         // 测试 code 不匹配 | ||||
|         roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong"))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(buildTime(2022, 2, 16)))); | ||||
|         // 准备参数 | ||||
|         RolePageReqVO reqVO = new RolePageReqVO(); | ||||
|         reqVO.setName("土豆"); | ||||
|         reqVO.setCode("tu"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<RoleDO> pageResult = roleService.getRolePage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbRole, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHasAnySuperAdmin_true() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) | ||||
|                     .thenReturn(roleService); | ||||
|  | ||||
|             // mock 数据 | ||||
|             RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin"); | ||||
|             roleMapper.insert(dbRole); | ||||
|             // 准备参数 | ||||
|             Long id = dbRole.getId(); | ||||
|  | ||||
|             // 调用,并调用 | ||||
|             assertTrue(roleService.hasAnySuperAdmin(singletonList(id))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHasAnySuperAdmin_false() { | ||||
|         try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | ||||
|             springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) | ||||
|                     .thenReturn(roleService); | ||||
|  | ||||
|             // mock 数据 | ||||
|             RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin"); | ||||
|             roleMapper.insert(dbRole); | ||||
|             // 准备参数 | ||||
|             Long id = dbRole.getId(); | ||||
|  | ||||
|             // 调用,并调用 | ||||
|             assertFalse(roleService.hasAnySuperAdmin(singletonList(id))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleList_success() { | ||||
|         // mock 数据 | ||||
|         RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         roleMapper.insert(roleDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(roleDO.getId()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         roleService.validateRoleList(ids); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleList_notFound() { | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(randomLongId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> roleService.validateRoleList(ids), ROLE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateRoleList_notEnable() { | ||||
|         // mock 数据 | ||||
|         RoleDO RoleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         roleMapper.insert(RoleDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(RoleDO.getId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> roleService.validateRoleList(ids), ROLE_IS_DISABLE, RoleDO.getName()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,303 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sensitiveword; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.time.Duration; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| /** | ||||
|  * {@link SensitiveWordServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 永不言败 | ||||
|  */ | ||||
| @Import(SensitiveWordServiceImpl.class) | ||||
| public class SensitiveWordServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SensitiveWordServiceImpl sensitiveWordService; | ||||
|  | ||||
|     @Resource | ||||
|     private SensitiveWordMapper sensitiveWordMapper; | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void setUp() { | ||||
|         SensitiveWordServiceImpl.ENABLED = true; | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testInitLocalCache() { | ||||
|         SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜") | ||||
|                 .setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         sensitiveWordMapper.insert(wordDO1); | ||||
|         SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋") | ||||
|                 .setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         sensitiveWordMapper.insert(wordDO2); | ||||
|         SensitiveWordDO wordDO3 = randomPojo(SensitiveWordDO.class, o -> o.setName("白") | ||||
|                 .setTags(singletonList("测试")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         sensitiveWordMapper.insert(wordDO3); | ||||
|         SensitiveWordDO wordDO4 = randomPojo(SensitiveWordDO.class, o -> o.setName("白痴") | ||||
|                 .setTags(singletonList("测试")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         sensitiveWordMapper.insert(wordDO4); | ||||
|  | ||||
|         // 调用 | ||||
|         sensitiveWordService.initLocalCache(); | ||||
|         // 断言 sensitiveWordTagsCache 缓存 | ||||
|         assertEquals(SetUtils.asSet("论坛", "蔬菜", "测试"), sensitiveWordService.getSensitiveWordTagSet()); | ||||
|         // 断言 sensitiveWordCache | ||||
|         assertEquals(4, sensitiveWordService.getSensitiveWordCache().size()); | ||||
|         assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0)); | ||||
|         assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1)); | ||||
|         assertPojoEquals(wordDO3, sensitiveWordService.getSensitiveWordCache().get(2)); | ||||
|         // 断言 tagSensitiveWordTries 缓存 | ||||
|         assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie()); | ||||
|         assertEquals(3, sensitiveWordService.getTagSensitiveWordTries().size()); | ||||
|         assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛")); | ||||
|         assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜")); | ||||
|         assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("测试")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRefreshLocalCache() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜") | ||||
|                 .setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         wordDO1.setUpdateTime(LocalDateTime.now()); | ||||
|         sensitiveWordMapper.insert(wordDO1); | ||||
|         sensitiveWordService.initLocalCache(); | ||||
|         // mock 数据 ② | ||||
|         SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋") | ||||
|                 .setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         wordDO2.setUpdateTime(LocalDateTimeUtils.addTime(Duration.ofMinutes(1))); // 避免时间相同 | ||||
|         sensitiveWordMapper.insert(wordDO2); | ||||
|  | ||||
|         // 调用 | ||||
|         sensitiveWordService.refreshLocalCache(); | ||||
|         // 断言 sensitiveWordTagsCache 缓存 | ||||
|         assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet()); | ||||
|         // 断言 sensitiveWordCache | ||||
|         assertEquals(2, sensitiveWordService.getSensitiveWordCache().size()); | ||||
|         assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0)); | ||||
|         assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1)); | ||||
|         // 断言 tagSensitiveWordTries 缓存 | ||||
|         assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie()); | ||||
|         assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size()); | ||||
|         assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛")); | ||||
|         assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateSensitiveWord_success() { | ||||
|         // 准备参数 | ||||
|         SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long sensitiveWordId = sensitiveWordService.createSensitiveWord(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(sensitiveWordId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId); | ||||
|         assertPojoEquals(reqVO, sensitiveWord, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSensitiveWord_success() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class); | ||||
|         sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class, o -> { | ||||
|             o.setId(dbSensitiveWord.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         sensitiveWordService.updateSensitiveWord(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, sensitiveWord); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSensitiveWord_notExists() { | ||||
|         // 准备参数 | ||||
|         SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> sensitiveWordService.updateSensitiveWord(reqVO), SENSITIVE_WORD_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSensitiveWord_success() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class); | ||||
|         sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSensitiveWord.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         sensitiveWordService.deleteSensitiveWord(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(sensitiveWordMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSensitiveWord_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> sensitiveWordService.deleteSensitiveWord(id), SENSITIVE_WORD_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSensitiveWord() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO sensitiveWord = randomPojo(SensitiveWordDO.class); | ||||
|         sensitiveWordMapper.insert(sensitiveWord); | ||||
|         // 准备参数 | ||||
|         Long id = sensitiveWord.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         SensitiveWordDO dbSensitiveWord = sensitiveWordService.getSensitiveWord(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(sensitiveWord, dbSensitiveWord); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSensitiveWordList() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO sensitiveWord01 = randomPojo(SensitiveWordDO.class); | ||||
|         sensitiveWordMapper.insert(sensitiveWord01); | ||||
|         SensitiveWordDO sensitiveWord02 = randomPojo(SensitiveWordDO.class); | ||||
|         sensitiveWordMapper.insert(sensitiveWord02); | ||||
|  | ||||
|         // 调用 | ||||
|         List<SensitiveWordDO> list = sensitiveWordService.getSensitiveWordList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertEquals(sensitiveWord01, list.get(0)); | ||||
|         assertEquals(sensitiveWord02, list.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSensitiveWordPage() { | ||||
|         // mock 数据 | ||||
|         SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class, o -> { // 等会查询到 | ||||
|             o.setName("笨蛋"); | ||||
|             o.setTags(Arrays.asList("论坛", "蔬菜")); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2022, 2, 8)); | ||||
|         }); | ||||
|         sensitiveWordMapper.insert(dbSensitiveWord); | ||||
|         // 测试 name 不匹配 | ||||
|         sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setName("傻瓜"))); | ||||
|         // 测试 tags 不匹配 | ||||
|         sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setTags(Arrays.asList("短信", "日用品")))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setCreateTime(buildTime(2022, 2, 16)))); | ||||
|         // 准备参数 | ||||
|         SensitiveWordPageReqVO reqVO = new SensitiveWordPageReqVO(); | ||||
|         reqVO.setName("笨"); | ||||
|         reqVO.setTag("论坛"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<SensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbSensitiveWord, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateText_noTag() { | ||||
|         testInitLocalCache(); | ||||
|         // 准备参数 | ||||
|         String text = "你是傻瓜,你是笨蛋"; | ||||
|         // 调用 | ||||
|         List<String> result = sensitiveWordService.validateText(text, null); | ||||
|         // 断言 | ||||
|         assertEquals(Arrays.asList("傻瓜", "笨蛋"), result); | ||||
|  | ||||
|         // 准备参数 | ||||
|         String text2 = "你是傻瓜,你是笨蛋,你是白"; | ||||
|         // 调用 | ||||
|         List<String> result2 = sensitiveWordService.validateText(text2, null); | ||||
|         // 断言 | ||||
|         assertEquals(Arrays.asList("傻瓜", "笨蛋","白"), result2); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateText_hasTag() { | ||||
|         testInitLocalCache(); | ||||
|         // 准备参数 | ||||
|         String text = "你是傻瓜,你是笨蛋"; | ||||
|         // 调用 | ||||
|         List<String> result = sensitiveWordService.validateText(text, singletonList("论坛")); | ||||
|         // 断言 | ||||
|         assertEquals(singletonList("傻瓜"), result); | ||||
|  | ||||
|  | ||||
|         // 准备参数 | ||||
|         String text2 = "你是白"; | ||||
|         // 调用 | ||||
|         List<String> result2 = sensitiveWordService.validateText(text2, singletonList("测试")); | ||||
|         // 断言 | ||||
|         assertEquals(singletonList("白"), result2); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testIsTestValid_noTag() { | ||||
|         testInitLocalCache(); | ||||
|         // 准备参数 | ||||
|         String text = "你是傻瓜,你是笨蛋"; | ||||
|         // 调用,断言 | ||||
|         assertFalse(sensitiveWordService.isTextValid(text, null)); | ||||
|  | ||||
|         // 准备参数 | ||||
|         String text2 = "你是白"; | ||||
|         // 调用,断言 | ||||
|         assertFalse(sensitiveWordService.isTextValid(text2, null)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testIsTestValid_hasTag() { | ||||
|         testInitLocalCache(); | ||||
|         // 准备参数 | ||||
|         String text = "你是傻瓜,你是笨蛋"; | ||||
|         // 调用,断言 | ||||
|         assertFalse(sensitiveWordService.isTextValid(text, singletonList("论坛"))); | ||||
|  | ||||
|         // 准备参数 | ||||
|         String text2 = "你是白"; | ||||
|         // 调用,断言 | ||||
|         assertFalse(sensitiveWordService.isTextValid(text2, singletonList("测试"))); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,236 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sms; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; | ||||
| 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.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| @Import(SmsChannelServiceImpl.class) | ||||
| public class SmsChannelServiceTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SmsChannelServiceImpl smsChannelService; | ||||
|  | ||||
|     @Resource | ||||
|     private SmsChannelMapper smsChannelMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private SmsClientFactory smsClientFactory; | ||||
|     @MockBean | ||||
|     private SmsTemplateService smsTemplateService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateSmsChannel_success() { | ||||
|         // 准备参数 | ||||
|         SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> o.setStatus(randomCommonStatus())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long smsChannelId = smsChannelService.createSmsChannel(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(smsChannelId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); | ||||
|         assertPojoEquals(reqVO, smsChannel, "id"); | ||||
|         // 断言 cache | ||||
|         assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId())); | ||||
|         assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSmsChannel_success() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> { | ||||
|             o.setId(dbSmsChannel.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|             o.setCallbackUrl(randomString()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         smsChannelService.updateSmsChannel(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, smsChannel); | ||||
|         // 断言 cache | ||||
|         assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId())); | ||||
|         assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSmsChannel_notExists() { | ||||
|         // 准备参数 | ||||
|         SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSmsChannel_success() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsChannel.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         smsChannelService.deleteSmsChannel(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(smsChannelMapper.selectById(id)); | ||||
|         // 断言 cache | ||||
|         assertNull(smsChannelService.getIdClientCache().getIfPresent(dbSmsChannel.getId())); | ||||
|         assertNull(smsChannelService.getCodeClientCache().getIfPresent(dbSmsChannel.getCode())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSmsChannel_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSmsChannel_hasChildren() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsChannel.getId(); | ||||
|         // mock 方法 | ||||
|         when(smsTemplateService.getSmsTemplateCountByChannelId(eq(id))).thenReturn(10L); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsChannel() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel); // @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsChannel.getId(); | ||||
|  | ||||
|         // 调用,并断言 | ||||
|         assertPojoEquals(dbSmsChannel, smsChannelService.getSmsChannel(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsChannelList() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO dbSmsChannel01 = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel01); | ||||
|         SmsChannelDO dbSmsChannel02 = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(dbSmsChannel02); | ||||
|         // 准备参数 | ||||
|  | ||||
|         // 调用 | ||||
|         List<SmsChannelDO> list = smsChannelService.getSmsChannelList(); | ||||
|         // 断言 | ||||
|         assertEquals(2, list.size()); | ||||
|         assertPojoEquals(dbSmsChannel01, list.get(0)); | ||||
|         assertPojoEquals(dbSmsChannel02, list.get(1)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsChannelPage() { | ||||
|        // mock 数据 | ||||
|        SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 | ||||
|            o.setSignature("芋道源码"); | ||||
|            o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|            o.setCreateTime(buildTime(2020, 12, 12)); | ||||
|        }); | ||||
|        smsChannelMapper.insert(dbSmsChannel); | ||||
|        // 测试 signature 不匹配 | ||||
|        smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); | ||||
|        // 测试 status 不匹配 | ||||
|        smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|        // 测试 createTime 不匹配 | ||||
|        smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); | ||||
|        // 准备参数 | ||||
|        SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); | ||||
|        reqVO.setSignature("芋道"); | ||||
|        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|        reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsClient_id() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO channel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(channel); | ||||
|         // mock 参数 | ||||
|         Long id = channel.getId(); | ||||
|         // mock 方法 | ||||
|         SmsClient mockClient = mock(SmsClient.class); | ||||
|         when(smsClientFactory.getSmsClient(eq(id))).thenReturn(mockClient); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsClient client = smsChannelService.getSmsClient(id); | ||||
|         // 断言 | ||||
|         assertSame(client, mockClient); | ||||
|         verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> { | ||||
|             SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); | ||||
|             return properties.equals(arg); | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsClient_code() { | ||||
|         // mock 数据 | ||||
|         SmsChannelDO channel = randomPojo(SmsChannelDO.class); | ||||
|         smsChannelMapper.insert(channel); | ||||
|         // mock 参数 | ||||
|         String code = channel.getCode(); | ||||
|         // mock 方法 | ||||
|         SmsClient mockClient = mock(SmsClient.class); | ||||
|         when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsClient client = smsChannelService.getSmsClient(code); | ||||
|         // 断言 | ||||
|         assertSame(client, mockClient); | ||||
|         verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> { | ||||
|             SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); | ||||
|             return properties.equals(arg); | ||||
|         })); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,209 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sms; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; | ||||
| import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; | ||||
| import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.config.SmsCodeProperties; | ||||
| import com.baomidou.mybatisplus.annotation.DbType; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| 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.time.Duration; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.ArgumentMatchers.isNull; | ||||
| import static org.mockito.Mockito.verify; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| @Import(SmsCodeServiceImpl.class) | ||||
| public class SmsCodeServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SmsCodeServiceImpl smsCodeService; | ||||
|  | ||||
|     @Resource | ||||
|     private SmsCodeMapper smsCodeMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private SmsCodeProperties smsCodeProperties; | ||||
|     @MockBean | ||||
|     private SmsSendService smsSendService; | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void setUp() { | ||||
|         when(smsCodeProperties.getExpireTimes()).thenReturn(Duration.ofMinutes(5)); | ||||
|         when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMinutes(1)); | ||||
|         when(smsCodeProperties.getSendMaximumQuantityPerDay()).thenReturn(10); | ||||
|         when(smsCodeProperties.getBeginCode()).thenReturn(9999); | ||||
|         when(smsCodeProperties.getEndCode()).thenReturn(9999); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void sendSmsCode_success() { | ||||
|         // 准备参数 | ||||
|         SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); | ||||
|         }); | ||||
|         // mock 方法 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|  | ||||
|         // 调用 | ||||
|         smsCodeService.sendSmsCode(reqDTO); | ||||
|         // 断言 code 验证码 | ||||
|         SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); | ||||
|         assertPojoEquals(reqDTO, smsCodeDO); | ||||
|         assertEquals("9999", smsCodeDO.getCode()); | ||||
|         assertEquals(1, smsCodeDO.getTodayIndex()); | ||||
|         assertFalse(smsCodeDO.getUsed()); | ||||
|         // 断言调用 | ||||
|         verify(smsSendService).sendSingleSms(eq(reqDTO.getMobile()), isNull(), isNull(), | ||||
|                 eq("user-sms-login"), eq(MapUtil.of("code", "9999"))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void sendSmsCode_tooFast() { | ||||
|         // mock 数据 | ||||
|         SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, | ||||
|                 o -> o.setMobile("15601691300").setTodayIndex(1)); | ||||
|         smsCodeMapper.insert(smsCodeDO); | ||||
|         // 准备参数 | ||||
|         SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); | ||||
|         }); | ||||
|         // mock 方法 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), | ||||
|                 SMS_CODE_SEND_TOO_FAST); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void sendSmsCode_exceedDay() { | ||||
|         // mock 数据 | ||||
|         SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, | ||||
|                 o -> o.setMobile("15601691300").setTodayIndex(10).setCreateTime(LocalDateTime.now())); | ||||
|         smsCodeMapper.insert(smsCodeDO); | ||||
|         // 准备参数 | ||||
|         SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); | ||||
|         }); | ||||
|         // mock 方法 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMillis(0)); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), | ||||
|                 SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUseSmsCode_success() { | ||||
|         // 准备参数 | ||||
|         SmsCodeUseReqDTO reqDTO = randomPojo(SmsCodeUseReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(randomEle(SmsSceneEnum.values()).getScene()); | ||||
|         }); | ||||
|         // mock 数据 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> { | ||||
|             o.setMobile(reqDTO.getMobile()).setScene(reqDTO.getScene()) | ||||
|                     .setCode(reqDTO.getCode()).setUsed(false); | ||||
|         })); | ||||
|  | ||||
|         // 调用 | ||||
|         smsCodeService.useSmsCode(reqDTO); | ||||
|         // 断言 | ||||
|         SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); | ||||
|         assertTrue(smsCodeDO.getUsed()); | ||||
|         assertNotNull(smsCodeDO.getUsedTime()); | ||||
|         assertEquals(reqDTO.getUsedIp(), smsCodeDO.getUsedIp()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void validateSmsCode_success() { | ||||
|         // 准备参数 | ||||
|         SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(randomEle(SmsSceneEnum.values()).getScene()); | ||||
|         }); | ||||
|         // mock 数据 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) | ||||
|                 .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false))); | ||||
|  | ||||
|         // 调用 | ||||
|         smsCodeService.validateSmsCode(reqDTO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void validateSmsCode_notFound() { | ||||
|         // 准备参数 | ||||
|         SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(randomEle(SmsSceneEnum.values()).getScene()); | ||||
|         }); | ||||
|         // mock 数据 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), | ||||
|                 SMS_CODE_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void validateSmsCode_expired() { | ||||
|         // 准备参数 | ||||
|         SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(randomEle(SmsSceneEnum.values()).getScene()); | ||||
|         }); | ||||
|         // mock 数据 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) | ||||
|                 .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false) | ||||
|                 .setCreateTime(LocalDateTime.now().minusMinutes(6)))); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), | ||||
|                 SMS_CODE_EXPIRED); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void validateSmsCode_used() { | ||||
|         // 准备参数 | ||||
|         SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setScene(randomEle(SmsSceneEnum.values()).getScene()); | ||||
|         }); | ||||
|         // mock 数据 | ||||
|         SqlConstants.init(DbType.MYSQL); | ||||
|         smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) | ||||
|                 .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(true) | ||||
|                 .setCreateTime(LocalDateTime.now()))); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), | ||||
|                 SMS_CODE_USED); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,190 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sms; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.time.LocalDateTime; | ||||
| 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.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
|  | ||||
| @Import(SmsLogServiceImpl.class) | ||||
| public class SmsLogServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SmsLogServiceImpl smsLogService; | ||||
|  | ||||
|     @Resource | ||||
|     private SmsLogMapper smsLogMapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsLogPage() { | ||||
|        // mock 数据 | ||||
|        SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到 | ||||
|            o.setChannelId(1L); | ||||
|            o.setTemplateId(10L); | ||||
|            o.setMobile("15601691300"); | ||||
|            o.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); | ||||
|            o.setSendTime(buildTime(2020, 11, 11)); | ||||
|            o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); | ||||
|            o.setReceiveTime(buildTime(2021, 11, 11)); | ||||
|        }); | ||||
|        smsLogMapper.insert(dbSmsLog); | ||||
|        // 测试 channelId 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setChannelId(2L))); | ||||
|        // 测试 templateId 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setTemplateId(20L))); | ||||
|        // 测试 mobile 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setMobile("18818260999"))); | ||||
|        // 测试 sendStatus 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus()))); | ||||
|        // 测试 sendTime 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12)))); | ||||
|        // 测试 receiveStatus 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveStatus(SmsReceiveStatusEnum.SUCCESS.getStatus()))); | ||||
|        // 测试 receiveTime 不匹配 | ||||
|        smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12)))); | ||||
|        // 准备参数 | ||||
|        SmsLogPageReqVO reqVO = new SmsLogPageReqVO(); | ||||
|        reqVO.setChannelId(1L); | ||||
|        reqVO.setTemplateId(10L); | ||||
|        reqVO.setMobile("156"); | ||||
|        reqVO.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); | ||||
|        reqVO.setSendTime(buildBetweenTime(2020, 11, 1, 2020, 11, 30)); | ||||
|        reqVO.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); | ||||
|        reqVO.setReceiveTime(buildBetweenTime(2021, 11, 1, 2021, 11, 30)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<SmsLogDO> pageResult = smsLogService.getSmsLogPage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbSmsLog, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateSmsLog() { | ||||
|         // 准备参数 | ||||
|         String mobile = randomString(); | ||||
|         Long userId = randomLongId(); | ||||
|         Integer userType = randomEle(UserTypeEnum.values()).getValue(); | ||||
|         Boolean isSend = randomBoolean(); | ||||
|         SmsTemplateDO templateDO = randomPojo(SmsTemplateDO.class, | ||||
|                 o -> o.setType(randomEle(SmsTemplateTypeEnum.values()).getType())); | ||||
|         String templateContent = randomString(); | ||||
|         Map<String, Object> templateParams = randomTemplateParams(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend, | ||||
|                 templateDO, templateContent, templateParams); | ||||
|         // 断言 | ||||
|         SmsLogDO logDO = smsLogMapper.selectById(logId); | ||||
|         assertEquals(isSend ? SmsSendStatusEnum.INIT.getStatus() : SmsSendStatusEnum.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(SmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSmsSendResult() { | ||||
|         // mock 数据 | ||||
|         SmsLogDO dbSmsLog = randomSmsLogDO( | ||||
|                 o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus())); | ||||
|         smsLogMapper.insert(dbSmsLog); | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsLog.getId(); | ||||
|         Boolean success = randomBoolean(); | ||||
|         String apiSendCode = randomString(); | ||||
|         String apiSendMsg = randomString(); | ||||
|         String apiRequestId = randomString(); | ||||
|         String apiSerialNo = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         smsLogService.updateSmsSendResult(id, success, | ||||
|                 apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); | ||||
|         // 断言 | ||||
|         dbSmsLog = smsLogMapper.selectById(id); | ||||
|         assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(), | ||||
|                 dbSmsLog.getSendStatus()); | ||||
|         assertNotNull(dbSmsLog.getSendTime()); | ||||
|         assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); | ||||
|         assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); | ||||
|         assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); | ||||
|         assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSmsReceiveResult() { | ||||
|         // mock 数据 | ||||
|         SmsLogDO dbSmsLog = randomSmsLogDO( | ||||
|                 o -> o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus())); | ||||
|         smsLogMapper.insert(dbSmsLog); | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsLog.getId(); | ||||
|         Boolean success = randomBoolean(); | ||||
|         LocalDateTime receiveTime = randomLocalDateTime(); | ||||
|         String apiReceiveCode = randomString(); | ||||
|         String apiReceiveMsg = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg); | ||||
|         // 断言 | ||||
|         dbSmsLog = smsLogMapper.selectById(id); | ||||
|         assertEquals(success ? SmsReceiveStatusEnum.SUCCESS.getStatus() | ||||
|                 : SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus()); | ||||
|         assertEquals(receiveTime, dbSmsLog.getReceiveTime()); | ||||
|         assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode()); | ||||
|         assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg()); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static SmsLogDO randomSmsLogDO(Consumer<SmsLogDO>... consumers) { | ||||
|         Consumer<SmsLogDO> consumer = (o) -> { | ||||
|             o.setTemplateParams(randomTemplateParams()); | ||||
|             o.setTemplateType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围 | ||||
|             o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围 | ||||
|             o.setSendStatus(randomEle(SmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围 | ||||
|             o.setReceiveStatus(randomEle(SmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围 | ||||
|         }; | ||||
|         return randomPojo(SmsLogDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
|     private static Map<String, Object> randomTemplateParams() { | ||||
|         return MapUtil.<String, Object>builder().put(randomString(), randomString()) | ||||
|                 .put(randomString(), randomString()).build(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,298 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sms; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.core.KeyValue; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; | ||||
| import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; | ||||
| import cn.iocoder.yudao.module.system.service.member.MemberService; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| import org.assertj.core.util.Lists; | ||||
| import org.junit.jupiter.api.Assertions; | ||||
| 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.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| public class SmsSendServiceImplTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private SmsSendServiceImpl smsSendService; | ||||
|  | ||||
|     @Mock | ||||
|     private AdminUserService adminUserService; | ||||
|     @Mock | ||||
|     private MemberService memberService; | ||||
|     @Mock | ||||
|     private SmsChannelService smsChannelService; | ||||
|     @Mock | ||||
|     private SmsTemplateService smsTemplateService; | ||||
|     @Mock | ||||
|     private SmsLogService smsLogService; | ||||
|     @Mock | ||||
|     private SmsProducer smsProducer; | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleSmsToAdmin() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock adminUserService 的方法 | ||||
|         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); | ||||
|         when(adminUserService.getUser(eq(userId))).thenReturn(user); | ||||
|  | ||||
|         // mock SmsTemplateService 的方法 | ||||
|         SmsTemplateDO template = randomPojo(SmsTemplateDO.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 SmsChannelService 的方法 | ||||
|         SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); | ||||
|         // mock SmsLogService 的方法 | ||||
|         Long smsLogId = randomLongId(); | ||||
|         when(smsLogService.createSmsLog(eq(user.getMobile()), eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(Boolean.TRUE), eq(template), | ||||
|                 eq(content), eq(templateParams))).thenReturn(smsLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(smsLogId, resultSmsLogId); | ||||
|         // 断言调用 | ||||
|         verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), | ||||
|                 eq(template.getChannelId()), eq(template.getApiTemplateId()), | ||||
|                 eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendSingleSmsToUser() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLongId(); | ||||
|         String templateCode = randomString(); | ||||
|         Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | ||||
|                 .put("op", "login").build(); | ||||
|         // mock memberService 的方法 | ||||
|         String mobile = "15601691300"; | ||||
|         when(memberService.getMemberUserMobile(eq(userId))).thenReturn(mobile); | ||||
|  | ||||
|         // mock SmsTemplateService 的方法 | ||||
|         SmsTemplateDO template = randomPojo(SmsTemplateDO.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 SmsChannelService 的方法 | ||||
|         SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); | ||||
|         // mock SmsLogService 的方法 | ||||
|         Long smsLogId = randomLongId(); | ||||
|         when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(Boolean.TRUE), eq(template), | ||||
|                 eq(content), eq(templateParams))).thenReturn(smsLogId); | ||||
|  | ||||
|         // 调用 | ||||
|         Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(smsLogId, resultSmsLogId); | ||||
|         // 断言调用 | ||||
|         verify(smsProducer).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_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 的方法 | ||||
|         SmsTemplateDO template = randomPojo(SmsTemplateDO.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 SmsChannelService 的方法 | ||||
|         SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); | ||||
|         // 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 = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); | ||||
|         // 断言 | ||||
|         assertEquals(smsLogId, resultSmsLogId); | ||||
|         // 断言调用 | ||||
|         verify(smsProducer).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 的方法 | ||||
|         SmsTemplateDO template = randomPojo(SmsTemplateDO.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 SmsChannelService 的方法 | ||||
|         SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); | ||||
|         // 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 = smsSendService.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(() -> smsSendService.validateSmsTemplate(templateCode), | ||||
|                 SMS_SEND_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuildTemplateParams_paramMiss() { | ||||
|         // 准备参数 | ||||
|         SmsTemplateDO template = randomPojo(SmsTemplateDO.class, | ||||
|                 o -> o.setParams(Lists.newArrayList("code"))); | ||||
|         Map<String, Object> templateParams = new HashMap<>(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsSendService.buildTemplateParams(template, templateParams), | ||||
|                 SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckMobile_notExists() { | ||||
|         // 准备参数 | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> smsSendService.validateMobile(null), | ||||
|                 SMS_SEND_MOBILE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSendBatchNotify() { | ||||
|         // 准备参数 | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         UnsupportedOperationException exception = Assertions.assertThrows( | ||||
|                 UnsupportedOperationException.class, | ||||
|                 () -> smsSendService.sendBatchSms(null, null, null, null, null) | ||||
|         ); | ||||
|         // 断言 | ||||
|         assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void testDoSendSms() throws Throwable { | ||||
|         // 准备参数 | ||||
|         SmsSendMessage message = randomPojo(SmsSendMessage.class); | ||||
|         // mock SmsClientFactory 的方法 | ||||
|         SmsClient smsClient = spy(SmsClient.class); | ||||
|         when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); | ||||
|         // mock SmsClient 的方法 | ||||
|         SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class); | ||||
|         when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), | ||||
|                 eq(message.getTemplateParams()))).thenReturn(sendResult); | ||||
|  | ||||
|         // 调用 | ||||
|         smsSendService.doSendSms(message); | ||||
|         // 断言 | ||||
|         verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), | ||||
|                 eq(sendResult.getSuccess()), eq(sendResult.getApiCode()), | ||||
|                 eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testReceiveSmsStatus() throws Throwable { | ||||
|         // 准备参数 | ||||
|         String channelCode = randomString(); | ||||
|         String text = randomString(); | ||||
|         // mock SmsClientFactory 的方法 | ||||
|         SmsClient smsClient = spy(SmsClient.class); | ||||
|         when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient); | ||||
|         // mock SmsClient 的方法 | ||||
|         List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class); | ||||
|  | ||||
|         // 调用 | ||||
|         smsSendService.receiveSmsStatus(channelCode, text); | ||||
|         // 断言 | ||||
|         receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()), | ||||
|                 eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode()))); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,348 @@ | ||||
| package cn.iocoder.yudao.module.system.service.sms; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; | ||||
| import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; | ||||
| 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.List; | ||||
| import java.util.Map; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| @Import(SmsTemplateServiceImpl.class) | ||||
| public class SmsTemplateServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SmsTemplateServiceImpl smsTemplateService; | ||||
|  | ||||
|     @Resource | ||||
|     private SmsTemplateMapper smsTemplateMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private SmsChannelService smsChannelService; | ||||
|     @MockBean | ||||
|     private SmsClient smsClient; | ||||
|  | ||||
|     @Test | ||||
|     public void testFormatSmsTemplateContent() { | ||||
|         // 准备参数 | ||||
|         String content = "正在进行登录操作{operation},您的验证码是{code}"; | ||||
|         Map<String, Object> params = MapUtil.<String, Object>builder("operation", "登录") | ||||
|                 .put("code", "1234").build(); | ||||
|  | ||||
|         // 调用 | ||||
|         String result = smsTemplateService.formatSmsTemplateContent(content, params); | ||||
|         // 断言 | ||||
|         assertEquals("正在进行登录操作登录,您的验证码是1234", result); | ||||
|     } | ||||
|  | ||||
|     @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() throws Throwable { | ||||
|         // 准备参数 | ||||
|         SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { | ||||
|             o.setContent("正在进行登录操作{operation},您的验证码是{code}"); | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | ||||
|         }).setId(null); // 防止 id 被赋值 | ||||
|         // mock Channel 的方法 | ||||
|         SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { | ||||
|             o.setId(reqVO.getChannelId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | ||||
|         }); | ||||
|         when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); | ||||
|         // mock 获得 API 短信模板成功 | ||||
|         when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); | ||||
|         when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( | ||||
|                 randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); | ||||
|  | ||||
|         // 调用 | ||||
|         Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(smsTemplateId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId); | ||||
|         assertPojoEquals(reqVO, smsTemplate, "id"); | ||||
|         assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); | ||||
|         assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void testUpdateSmsTemplate_success() throws Throwable { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { | ||||
|             o.setId(dbSmsTemplate.getId()); // 设置更新的 ID | ||||
|             o.setContent("正在进行登录操作{operation},您的验证码是{code}"); | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | ||||
|         }); | ||||
|         // mock 方法 | ||||
|         SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { | ||||
|             o.setId(reqVO.getChannelId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | ||||
|         }); | ||||
|         when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); | ||||
|         // mock 获得 API 短信模板成功 | ||||
|         when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); | ||||
|         when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( | ||||
|                 randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); | ||||
|  | ||||
|         // 调用 | ||||
|         smsTemplateService.updateSmsTemplate(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, smsTemplate); | ||||
|         assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); | ||||
|         assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSmsTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSmsTemplate_success() { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         smsTemplateService.deleteSmsTemplate(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(smsTemplateMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSmsTemplate_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsTemplate() { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSmsTemplate.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id); | ||||
|         // 校验 | ||||
|         assertPojoEquals(dbSmsTemplate, smsTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsTemplateByCodeFromCache() { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         String code = dbSmsTemplate.getCode(); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplateByCodeFromCache(code); | ||||
|         // 校验 | ||||
|         assertPojoEquals(dbSmsTemplate, smsTemplate); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsTemplatePage() { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 | ||||
|             o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCode("tudou"); | ||||
|             o.setContent("芋道源码"); | ||||
|             o.setApiTemplateId("yunai"); | ||||
|             o.setChannelId(1L); | ||||
|             o.setCreateTime(buildTime(2021, 11, 11)); | ||||
|         }); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate); | ||||
|         // 测试 type 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); | ||||
|         // 测试 status 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 code 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); | ||||
|         // 测试 content 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); | ||||
|         // 测试 apiTemplateId 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); | ||||
|         // 测试 channelId 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); | ||||
|         // 准备参数 | ||||
|         SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); | ||||
|         reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCode("tu"); | ||||
|         reqVO.setContent("芋道"); | ||||
|         reqVO.setApiTemplateId("yu"); | ||||
|         reqVO.setChannelId(1L); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<SmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSmsTemplateCountByChannelId() { | ||||
|         // mock 数据 | ||||
|         SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> o.setChannelId(1L)); | ||||
|         smsTemplateMapper.insert(dbSmsTemplate); | ||||
|         // 测试 channelId 不匹配 | ||||
|         smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); | ||||
|         // 准备参数 | ||||
|         Long channelId = 1L; | ||||
|  | ||||
|         // 调用 | ||||
|         Long count = smsTemplateService.getSmsTemplateCountByChannelId(channelId); | ||||
|         // 断言 | ||||
|         assertEquals(1, count); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateSmsChannel_success() { | ||||
|         // 准备参数 | ||||
|         Long channelId = randomLongId(); | ||||
|         // mock 方法 | ||||
|         SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { | ||||
|             o.setId(channelId); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | ||||
|         }); | ||||
|         when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); | ||||
|  | ||||
|         // 调用 | ||||
|         SmsChannelDO returnChannelDO = smsTemplateService.validateSmsChannel(channelId); | ||||
|         // 断言 | ||||
|         assertPojoEquals(returnChannelDO, channelDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateSmsChannel_notExists() { | ||||
|         // 准备参数 | ||||
|         Long channelId = randomLongId(); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), | ||||
|                 SMS_CHANNEL_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateSmsChannel_disable() { | ||||
|         // 准备参数 | ||||
|         Long channelId = randomLongId(); | ||||
|         // mock 方法 | ||||
|         SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { | ||||
|             o.setId(channelId); | ||||
|             o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败 | ||||
|         }); | ||||
|         when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), | ||||
|                 SMS_CHANNEL_DISABLE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataValueUnique_success() { | ||||
|         // 调用,成功 | ||||
|         smsTemplateService.validateSmsTemplateCodeDuplicate(randomLongId(), randomString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateSmsTemplateCodeDuplicate_valueDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         String code = randomString(); | ||||
|         // mock 数据 | ||||
|         smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(null, code), | ||||
|                 SMS_TEMPLATE_CODE_DUPLICATE, code); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String code = randomString(); | ||||
|         // mock 数据 | ||||
|         smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(id, code), | ||||
|                 SMS_TEMPLATE_CODE_DUPLICATE, code); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static SmsTemplateDO randomSmsTemplateDO(Consumer<SmsTemplateDO>... consumers) { | ||||
|         Consumer<SmsTemplateDO> consumer = (o) -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | ||||
|         }; | ||||
|         return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,472 @@ | ||||
| package cn.iocoder.yudao.module.system.service.social; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.api.WxMaUserService; | ||||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; | ||||
| import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; | ||||
| import com.xingyuv.jushauth.config.AuthConfig; | ||||
| import com.xingyuv.jushauth.model.AuthResponse; | ||||
| import com.xingyuv.jushauth.model.AuthUser; | ||||
| import com.xingyuv.jushauth.request.AuthDefaultRequest; | ||||
| import com.xingyuv.jushauth.request.AuthRequest; | ||||
| import com.xingyuv.jushauth.utils.AuthStateUtils; | ||||
| import com.xingyuv.justauth.AuthRequestFactory; | ||||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | ||||
| import me.chanjar.weixin.common.error.WxErrorException; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.MockedStatic; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
| import org.springframework.context.annotation.Import; | ||||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| /** | ||||
|  * {@link SocialClientServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(SocialClientServiceImpl.class) | ||||
| public class SocialClientServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SocialClientServiceImpl socialClientService; | ||||
|  | ||||
|     @Resource | ||||
|     private SocialClientMapper socialClientMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private AuthRequestFactory authRequestFactory; | ||||
|  | ||||
|     @MockBean | ||||
|     private WxMpService wxMpService; | ||||
|     @MockBean | ||||
|     private WxMpProperties wxMpProperties; | ||||
|     @MockBean | ||||
|     private StringRedisTemplate stringRedisTemplate; | ||||
|     @MockBean | ||||
|     private WxMaService wxMaService; | ||||
|     @MockBean | ||||
|     private WxMaProperties wxMaProperties; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetAuthorizeUrl() { | ||||
|         try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) { | ||||
|             // 准备参数 | ||||
|             Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|             Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|             String redirectUri = "sss"; | ||||
|             // mock 获得对应的 AuthRequest 实现 | ||||
|             AuthRequest authRequest = mock(AuthRequest.class); | ||||
|             when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|             // mock 方法 | ||||
|             authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman"); | ||||
|             when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy"); | ||||
|  | ||||
|             // 调用 | ||||
|             String url = socialClientService.getAuthorizeUrl(socialType, userType, redirectUri); | ||||
|             // 断言 | ||||
|             assertEquals("https://www.iocoder.cn?redirect_uri=sss", url); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_success() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         String code = randomString(); | ||||
|         String state = randomString(); | ||||
|         // mock 方法(AuthRequest) | ||||
|         AuthRequest authRequest = mock(AuthRequest.class); | ||||
|         when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|         // mock 方法(AuthResponse) | ||||
|         AuthUser authUser = randomPojo(AuthUser.class); | ||||
|         AuthResponse<?> authResponse = new AuthResponse<>(2000, null, authUser); | ||||
|         when(authRequest.login(argThat(authCallback -> { | ||||
|             assertEquals(code, authCallback.getCode()); | ||||
|             assertEquals(state, authCallback.getState()); | ||||
|             return true; | ||||
|         }))).thenReturn(authResponse); | ||||
|  | ||||
|         // 调用 | ||||
|         AuthUser result = socialClientService.getAuthUser(socialType, userType, code, state); | ||||
|         // 断言 | ||||
|         assertSame(authUser, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_fail() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         String code = randomString(); | ||||
|         String state = randomString(); | ||||
|         // mock 方法(AuthRequest) | ||||
|         AuthRequest authRequest = mock(AuthRequest.class); | ||||
|         when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|         // mock 方法(AuthResponse) | ||||
|         AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null); | ||||
|         when(authRequest.login(argThat(authCallback -> { | ||||
|             assertEquals(code, authCallback.getCode()); | ||||
|             assertEquals(state, authCallback.getState()); | ||||
|             return true; | ||||
|         }))).thenReturn(authResponse); | ||||
|  | ||||
|         // 调用并断言 | ||||
|         assertServiceException( | ||||
|                 () -> socialClientService.getAuthUser(socialType, userType, code, state), | ||||
|                 SOCIAL_USER_AUTH_FAILURE, "模拟失败"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuildAuthRequest_clientNull() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|         Integer userType = randomPojo(SocialTypeEnum.class).getType(); | ||||
|         // mock 获得对应的 AuthRequest 实现 | ||||
|         AuthRequest authRequest = mock(AuthDefaultRequest.class); | ||||
|         AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); | ||||
|         when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|  | ||||
|         // 调用 | ||||
|         AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); | ||||
|         // 断言 | ||||
|         assertSame(authRequest, result); | ||||
|         assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuildAuthRequest_clientDisable() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|         Integer userType = randomPojo(SocialTypeEnum.class).getType(); | ||||
|         // mock 获得对应的 AuthRequest 实现 | ||||
|         AuthRequest authRequest = mock(AuthDefaultRequest.class); | ||||
|         AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); | ||||
|         when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(socialType)); | ||||
|         socialClientMapper.insert(client); | ||||
|  | ||||
|         // 调用 | ||||
|         AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); | ||||
|         // 断言 | ||||
|         assertSame(authRequest, result); | ||||
|         assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuildAuthRequest_clientEnable() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ||||
|         Integer userType = randomPojo(SocialTypeEnum.class).getType(); | ||||
|         // mock 获得对应的 AuthRequest 实现 | ||||
|         AuthConfig authConfig = mock(AuthConfig.class); | ||||
|         AuthRequest authRequest = mock(AuthDefaultRequest.class); | ||||
|         ReflectUtil.setFieldValue(authRequest, "config", authConfig); | ||||
|         when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(socialType)); | ||||
|         socialClientMapper.insert(client); | ||||
|  | ||||
|         // 调用 | ||||
|         AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); | ||||
|         // 断言 | ||||
|         assertSame(authRequest, result); | ||||
|         assertNotSame(authConfig, ReflectUtil.getFieldValue(authRequest, "config")); | ||||
|     } | ||||
|  | ||||
|     // =================== 微信公众号独有 =================== | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateWxMpJsapiSignature() throws WxErrorException { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         String url = randomString(); | ||||
|         // mock 方法 | ||||
|         WxJsapiSignature signature = randomPojo(WxJsapiSignature.class); | ||||
|         when(wxMpService.createJsapiSignature(eq(url))).thenReturn(signature); | ||||
|  | ||||
|         // 调用 | ||||
|         WxJsapiSignature result = socialClientService.createWxMpJsapiSignature(userType, url); | ||||
|         // 断言 | ||||
|         assertSame(signature, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMpService_clientNull() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         WxMpService result = socialClientService.getWxMpService(userType); | ||||
|         // 断言 | ||||
|         assertSame(wxMpService, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMpService_clientDisable() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); | ||||
|         socialClientMapper.insert(client); | ||||
|  | ||||
|         // 调用 | ||||
|         WxMpService result = socialClientService.getWxMpService(userType); | ||||
|         // 断言 | ||||
|         assertSame(wxMpService, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMpService_clientEnable() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); | ||||
|         socialClientMapper.insert(client); | ||||
|         // mock 方法 | ||||
|         WxMpProperties.ConfigStorage configStorage = mock(WxMpProperties.ConfigStorage.class); | ||||
|         when(wxMpProperties.getConfigStorage()).thenReturn(configStorage); | ||||
|  | ||||
|         // 调用 | ||||
|         WxMpService result = socialClientService.getWxMpService(userType); | ||||
|         // 断言 | ||||
|         assertNotSame(wxMpService, result); | ||||
|         assertEquals(client.getClientId(), result.getWxMpConfigStorage().getAppId()); | ||||
|         assertEquals(client.getClientSecret(), result.getWxMpConfigStorage().getSecret()); | ||||
|     } | ||||
|  | ||||
|     // =================== 微信小程序独有 =================== | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMaPhoneNumberInfo_success() throws WxErrorException { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         String phoneCode = randomString(); | ||||
|         // mock 方法 | ||||
|         WxMaUserService userService = mock(WxMaUserService.class); | ||||
|         when(wxMaService.getUserService()).thenReturn(userService); | ||||
|         WxMaPhoneNumberInfo phoneNumber = randomPojo(WxMaPhoneNumberInfo.class); | ||||
|         when(userService.getPhoneNoInfo(eq(phoneCode))).thenReturn(phoneNumber); | ||||
|  | ||||
|         // 调用 | ||||
|         WxMaPhoneNumberInfo result = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); | ||||
|         // 断言 | ||||
|         assertSame(phoneNumber, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMaPhoneNumberInfo_exception() throws WxErrorException { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         String phoneCode = randomString(); | ||||
|         // mock 方法 | ||||
|         WxMaUserService userService = mock(WxMaUserService.class); | ||||
|         when(wxMaService.getUserService()).thenReturn(userService); | ||||
|         WxErrorException wxErrorException = randomPojo(WxErrorException.class); | ||||
|         when(userService.getPhoneNoInfo(eq(phoneCode))).thenThrow(wxErrorException); | ||||
|  | ||||
|         // 调用并断言异常 | ||||
|         assertServiceException(() -> socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode), | ||||
|                 SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMaService_clientNull() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用 | ||||
|         WxMaService result = socialClientService.getWxMaService(userType); | ||||
|         // 断言 | ||||
|         assertSame(wxMaService, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMaService_clientDisable() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())); | ||||
|         socialClientMapper.insert(client); | ||||
|  | ||||
|         // 调用 | ||||
|         WxMaService result = socialClientService.getWxMaService(userType); | ||||
|         // 断言 | ||||
|         assertSame(wxMaService, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetWxMaService_clientEnable() { | ||||
|         // 准备参数 | ||||
|         Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||||
|         // mock 数据 | ||||
|         SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())); | ||||
|         socialClientMapper.insert(client); | ||||
|         // mock 方法 | ||||
|         WxMaProperties.ConfigStorage configStorage = mock(WxMaProperties.ConfigStorage.class); | ||||
|         when(wxMaProperties.getConfigStorage()).thenReturn(configStorage); | ||||
|  | ||||
|         // 调用 | ||||
|         WxMaService result = socialClientService.getWxMaService(userType); | ||||
|         // 断言 | ||||
|         assertNotSame(wxMaService, result); | ||||
|         assertEquals(client.getClientId(), result.getWxMaConfig().getAppid()); | ||||
|         assertEquals(client.getClientSecret(), result.getWxMaConfig().getSecret()); | ||||
|     } | ||||
|  | ||||
|     // =================== 客户端管理 =================== | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateSocialClient_success() { | ||||
|         // 准备参数 | ||||
|         SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, | ||||
|                 o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) | ||||
|                         .setUserType(randomEle(UserTypeEnum.values()).getValue()) | ||||
|                         .setStatus(randomCommonStatus())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long socialClientId = socialClientService.createSocialClient(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(socialClientId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SocialClientDO socialClient = socialClientMapper.selectById(socialClientId); | ||||
|         assertPojoEquals(reqVO, socialClient, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSocialClient_success() { | ||||
|         // mock 数据 | ||||
|         SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); | ||||
|         socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> { | ||||
|             o.setId(dbSocialClient.getId()); // 设置更新的 ID | ||||
|             o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) | ||||
|                     .setUserType(randomEle(UserTypeEnum.values()).getValue()) | ||||
|                     .setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         socialClientService.updateSocialClient(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SocialClientDO socialClient = socialClientMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, socialClient); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateSocialClient_notExists() { | ||||
|         // 准备参数 | ||||
|         SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> socialClientService.updateSocialClient(reqVO), SOCIAL_CLIENT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSocialClient_success() { | ||||
|         // mock 数据 | ||||
|         SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); | ||||
|         socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSocialClient.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         socialClientService.deleteSocialClient(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(socialClientMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteSocialClient_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> socialClientService.deleteSocialClient(id), SOCIAL_CLIENT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialClient() { | ||||
|         // mock 数据 | ||||
|         SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); | ||||
|         socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbSocialClient.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialClientDO socialClient = socialClientService.getSocialClient(id); | ||||
|         // 校验数据正确 | ||||
|         assertPojoEquals(dbSocialClient, socialClient); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialClientPage() { | ||||
|         // mock 数据 | ||||
|         SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class, o -> { // 等会查询到 | ||||
|             o.setName("芋头"); | ||||
|             o.setSocialType(SocialTypeEnum.GITEE.getType()); | ||||
|             o.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|             o.setClientId("yudao"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         socialClientMapper.insert(dbSocialClient); | ||||
|         // 测试 name 不匹配 | ||||
|         socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString()))); | ||||
|         // 测试 socialType 不匹配 | ||||
|         socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType()))); | ||||
|         // 测试 userType 不匹配 | ||||
|         socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); | ||||
|         // 测试 clientId 不匹配 | ||||
|         socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao"))); | ||||
|         // 测试 status 不匹配 | ||||
|         socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         SocialClientPageReqVO reqVO = new SocialClientPageReqVO(); | ||||
|         reqVO.setName("芋"); | ||||
|         reqVO.setSocialType(SocialTypeEnum.GITEE.getType()); | ||||
|         reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||||
|         reqVO.setClientId("yu"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<SocialClientDO> pageResult = socialClientService.getSocialClientPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbSocialClient, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,288 @@ | ||||
| package cn.iocoder.yudao.module.system.service.social; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | ||||
| import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||||
| import com.xingyuv.jushauth.model.AuthUser; | ||||
| 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.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.hutool.core.util.RandomUtil.randomLong; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.mockito.Mockito.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
|  * {@link SocialUserServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(SocialUserServiceImpl.class) | ||||
| public class SocialUserServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SocialUserServiceImpl socialUserService; | ||||
|  | ||||
|     @Resource | ||||
|     private SocialUserMapper socialUserMapper; | ||||
|     @Resource | ||||
|     private SocialUserBindMapper socialUserBindMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private SocialClientService socialClientService; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialUserList() { | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         // mock 获得社交用户 | ||||
|         SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType()); | ||||
|         socialUserMapper.insert(socialUser); // 可被查到 | ||||
|         socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到 | ||||
|         // mock 获得绑定 | ||||
|         socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到 | ||||
|                 .setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType()) | ||||
|                 .setSocialUserId(socialUser.getId())); | ||||
|         socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到 | ||||
|                 .setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType())); | ||||
|  | ||||
|         // 调用 | ||||
|         List<SocialUserDO> result = socialUserService.getSocialUserList(userId, userType); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertPojoEquals(socialUser, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBindSocialUser() { | ||||
|         // 准备参数 | ||||
|         SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO() | ||||
|                 .setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) | ||||
|                 .setSocialType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state"); | ||||
|         // mock 数据:获得社交用户 | ||||
|         SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getSocialType()) | ||||
|                 .setCode(reqDTO.getCode()).setState(reqDTO.getState()); | ||||
|         socialUserMapper.insert(socialUser); | ||||
|         // mock 数据:用户可能之前已经绑定过该社交类型 | ||||
|         socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) | ||||
|                 .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L)); | ||||
|         // mock 数据:社交用户可能之前绑定过别的用户 | ||||
|         socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue()) | ||||
|                 .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId())); | ||||
|  | ||||
|         // 调用 | ||||
|         String openid = socialUserService.bindSocialUser(reqDTO); | ||||
|         // 断言 | ||||
|         List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList(); | ||||
|         assertEquals(1, socialUserBinds.size()); | ||||
|         assertEquals(socialUser.getOpenid(), openid); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUnbindSocialUser_success() { | ||||
|         // 准备参数 | ||||
|         Long userId = 1L; | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         Integer type = SocialTypeEnum.GITEE.getType(); | ||||
|         String openid = "test_openid"; | ||||
|         // mock 数据:社交用户 | ||||
|         SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid); | ||||
|         socialUserMapper.insert(socialUser); | ||||
|         // mock 数据:社交绑定关系 | ||||
|         SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType) | ||||
|                 .setUserId(userId).setSocialType(type); | ||||
|         socialUserBindMapper.insert(socialUserBind); | ||||
|  | ||||
|         // 调用 | ||||
|         socialUserService.unbindSocialUser(userId, userType, type, openid); | ||||
|         // 断言 | ||||
|         assertEquals(0, socialUserBindMapper.selectCount(null).intValue()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUnbindSocialUser_notFound() { | ||||
|         // 调用,并断言 | ||||
|         assertServiceException( | ||||
|                 () -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(), | ||||
|                         SocialTypeEnum.GITEE.getType(), "test_openid"), | ||||
|                 SOCIAL_USER_NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialUser() { | ||||
|         // 准备参数 | ||||
|         Integer userType = UserTypeEnum.ADMIN.getValue(); | ||||
|         Integer type = SocialTypeEnum.GITEE.getType(); | ||||
|         String code = "tudou"; | ||||
|         String state = "yuanma"; | ||||
|         // mock 社交用户 | ||||
|         SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state); | ||||
|         socialUserMapper.insert(socialUserDO); | ||||
|         // mock 社交用户的绑定 | ||||
|         Long userId = randomLong(); | ||||
|         SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId) | ||||
|                 .setSocialType(type).setSocialUserId(socialUserDO.getId()); | ||||
|         socialUserBindMapper.insert(socialUserBind); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(userType, type, code, state); | ||||
|         // 断言 | ||||
|         assertEquals(userId, socialUser.getUserId()); | ||||
|         assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_exists() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.GITEE.getType(); | ||||
|         Integer userType = randomEle(SocialTypeEnum.values()).getType(); | ||||
|         String code = "tudou"; | ||||
|         String state = "yuanma"; | ||||
|         // mock 方法 | ||||
|         SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(socialType).setCode(code).setState(state); | ||||
|         socialUserMapper.insert(socialUser); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); | ||||
|         // 断言 | ||||
|         assertPojoEquals(socialUser, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_notNull() { | ||||
|         // mock 数据 | ||||
|         SocialUserDO socialUser = randomPojo(SocialUserDO.class, | ||||
|                 o -> o.setType(SocialTypeEnum.GITEE.getType()).setCode("tudou").setState("yuanma")); | ||||
|         socialUserMapper.insert(socialUser); | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.GITEE.getType(); | ||||
|         Integer userType = randomEle(SocialTypeEnum.values()).getType(); | ||||
|         String code = "tudou"; | ||||
|         String state = "yuanma"; | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); | ||||
|         // 断言 | ||||
|         assertPojoEquals(socialUser, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_insert() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.GITEE.getType(); | ||||
|         Integer userType = randomEle(SocialTypeEnum.values()).getType(); | ||||
|         String code = "tudou"; | ||||
|         String state = "yuanma"; | ||||
|         // mock 方法 | ||||
|         AuthUser authUser = randomPojo(AuthUser.class); | ||||
|         when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); | ||||
|         // 断言 | ||||
|         assertBindSocialUser(socialType, result, authUser); | ||||
|         assertEquals(code, result.getCode()); | ||||
|         assertEquals(state, result.getState()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testAuthSocialUser_update() { | ||||
|         // 准备参数 | ||||
|         Integer socialType = SocialTypeEnum.GITEE.getType(); | ||||
|         Integer userType = randomEle(SocialTypeEnum.values()).getType(); | ||||
|         String code = "tudou"; | ||||
|         String state = "yuanma"; | ||||
|         // mock 数据 | ||||
|         socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(socialType).setOpenid("test_openid")); | ||||
|         // mock 方法 | ||||
|         AuthUser authUser = randomPojo(AuthUser.class); | ||||
|         when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); | ||||
|         // 断言 | ||||
|         assertBindSocialUser(socialType, result, authUser); | ||||
|         assertEquals(code, result.getCode()); | ||||
|         assertEquals(state, result.getState()); | ||||
|     } | ||||
|  | ||||
|     private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) { | ||||
|         assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); | ||||
|         assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); | ||||
|         assertEquals(authUser.getNickname(), socialUser.getNickname()); | ||||
|         assertEquals(authUser.getAvatar(), socialUser.getAvatar()); | ||||
|         assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); | ||||
|         assertEquals(type, socialUser.getType()); | ||||
|         assertEquals(authUser.getUuid(), socialUser.getOpenid()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialUser_id() { | ||||
|         // mock 数据 | ||||
|         SocialUserDO socialUserDO = randomPojo(SocialUserDO.class); | ||||
|         socialUserMapper.insert(socialUserDO); | ||||
|         // 参数准备 | ||||
|         Long id = socialUserDO.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         SocialUserDO dbSocialUserDO = socialUserService.getSocialUser(id); | ||||
|         // 断言 | ||||
|         assertPojoEquals(socialUserDO, dbSocialUserDO); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetSocialUserPage() { | ||||
|         // mock 数据 | ||||
|         SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, o -> { // 等会查询到 | ||||
|             o.setType(SocialTypeEnum.GITEE.getType()); | ||||
|             o.setNickname("芋艿"); | ||||
|             o.setOpenid("yudaoyuanma"); | ||||
|             o.setCreateTime(buildTime(2020, 1, 15)); | ||||
|         }); | ||||
|         socialUserMapper.insert(dbSocialUser); | ||||
|         // 测试 type 不匹配 | ||||
|         socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setType(SocialTypeEnum.DINGTALK.getType()))); | ||||
|         // 测试 nickname 不匹配 | ||||
|         socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setNickname(randomString()))); | ||||
|         // 测试 openid 不匹配 | ||||
|         socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setOpenid("java"))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setCreateTime(buildTime(2020, 1, 21)))); | ||||
|         // 准备参数 | ||||
|         SocialUserPageReqVO reqVO = new SocialUserPageReqVO(); | ||||
|         reqVO.setType(SocialTypeEnum.GITEE.getType()); | ||||
|         reqVO.setNickname("芋"); | ||||
|         reqVO.setOpenid("yudao"); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2020, 1, 10, 2020, 1, 20)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<SocialUserDO> pageResult = socialUserService.getSocialUserPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbSocialUser, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,236 @@ | ||||
| package cn.iocoder.yudao.module.system.service.tenant; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; | ||||
| 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.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Arrays.asList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.verify; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
| * {@link TenantPackageServiceImpl} 的单元测试类 | ||||
| * | ||||
| * @author 芋道源码 | ||||
| */ | ||||
| @Import(TenantPackageServiceImpl.class) | ||||
| public class TenantPackageServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private TenantPackageServiceImpl tenantPackageService; | ||||
|  | ||||
|     @Resource | ||||
|     private TenantPackageMapper tenantPackageMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private TenantService tenantService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateTenantPackage_success() { | ||||
|         // 准备参数 | ||||
|         TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())) | ||||
|                 .setId(null); // 防止 id 被赋值 | ||||
|  | ||||
|         // 调用 | ||||
|         Long tenantPackageId = tenantPackageService.createTenantPackage(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(tenantPackageId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         TenantPackageDO tenantPackage = tenantPackageMapper.selectById(tenantPackageId); | ||||
|         assertPojoEquals(reqVO, tenantPackage, "id"); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateTenantPackage_success() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, o -> { | ||||
|             o.setId(dbTenantPackage.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|         }); | ||||
|         // mock 方法 | ||||
|         Long tenantId01 = randomLongId(); | ||||
|         Long tenantId02 = randomLongId(); | ||||
|         when(tenantService.getTenantListByPackageId(eq(reqVO.getId()))).thenReturn( | ||||
|                 asList(randomPojo(TenantDO.class, o -> o.setId(tenantId01)), | ||||
|                         randomPojo(TenantDO.class, o -> o.setId(tenantId02)))); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantPackageService.updateTenantPackage(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         TenantPackageDO tenantPackage = tenantPackageMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, tenantPackage); | ||||
|         // 校验调用租户的菜单 | ||||
|         verify(tenantService).updateTenantRoleMenu(eq(tenantId01), eq(reqVO.getMenuIds())); | ||||
|         verify(tenantService).updateTenantRoleMenu(eq(tenantId02), eq(reqVO.getMenuIds())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateTenantPackage_notExists() { | ||||
|         // 准备参数 | ||||
|         TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantPackageService.updateTenantPackage(reqVO), TENANT_PACKAGE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenantPackage_success() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbTenantPackage.getId(); | ||||
|         // mock 租户未使用该套餐 | ||||
|         when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(0L); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantPackageService.deleteTenantPackage(id); | ||||
|        // 校验数据不存在了 | ||||
|        assertNull(tenantPackageMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenantPackage_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenantPackage_used() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbTenantPackage.getId(); | ||||
|         // mock 租户在使用该套餐 | ||||
|         when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(1L); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_USED); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantPackagePage() { | ||||
|        // mock 数据 | ||||
|        TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, o -> { // 等会查询到 | ||||
|            o.setName("芋道源码"); | ||||
|            o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|            o.setRemark("源码解析"); | ||||
|            o.setCreateTime(buildTime(2022, 10, 10)); | ||||
|        }); | ||||
|        tenantPackageMapper.insert(dbTenantPackage); | ||||
|        // 测试 name 不匹配 | ||||
|        tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setName("源码"))); | ||||
|        // 测试 status 不匹配 | ||||
|        tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|        // 测试 remark 不匹配 | ||||
|        tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setRemark("解析"))); | ||||
|        // 测试 createTime 不匹配 | ||||
|        tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setCreateTime(buildTime(2022, 11, 11)))); | ||||
|        // 准备参数 | ||||
|        TenantPackagePageReqVO reqVO = new TenantPackagePageReqVO(); | ||||
|        reqVO.setName("芋道"); | ||||
|        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|        reqVO.setRemark("源码"); | ||||
|        reqVO.setCreateTime(buildBetweenTime(2022, 10, 9, 2022, 10, 11)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<TenantPackageDO> pageResult = tenantPackageService.getTenantPackagePage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbTenantPackage, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenantPackage_success() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         TenantPackageDO result = tenantPackageService.validTenantPackage(dbTenantPackage.getId()); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbTenantPackage, result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenantPackage_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantPackageService.validTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenantPackage_disable() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantPackageService.validTenantPackage(dbTenantPackage.getId()), | ||||
|                 TENANT_PACKAGE_DISABLE, dbTenantPackage.getName()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantPackage() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); | ||||
|         tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         TenantPackageDO result = tenantPackageService.getTenantPackage(dbTenantPackage.getId()); | ||||
|         // 断言 | ||||
|         assertPojoEquals(result, dbTenantPackage); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantPackageListByStatus() { | ||||
|         // mock 数据 | ||||
|         TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||||
|         tenantPackageMapper.insert(dbTenantPackage); | ||||
|         // 测试 status 不匹配 | ||||
|         tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, | ||||
|                 o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|  | ||||
|         // 调用 | ||||
|         List<TenantPackageDO> list = tenantPackageService.getTenantPackageListByStatus( | ||||
|                 CommonStatusEnum.ENABLE.getStatus()); | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbTenantPackage, list.get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,458 @@ | ||||
| package cn.iocoder.yudao.module.system.service.tenant; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.tenant.config.TenantProperties; | ||||
| import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; | ||||
| import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; | ||||
| import cn.iocoder.yudao.module.system.service.permission.MenuService; | ||||
| import cn.iocoder.yudao.module.system.service.permission.PermissionService; | ||||
| import cn.iocoder.yudao.module.system.service.permission.RoleService; | ||||
| import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; | ||||
| import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; | ||||
| import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| 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.time.LocalDateTime; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Arrays.asList; | ||||
| import static java.util.Collections.singleton; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.*; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| /** | ||||
|  * {@link TenantServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Import(TenantServiceImpl.class) | ||||
| public class TenantServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private TenantServiceImpl tenantService; | ||||
|  | ||||
|     @Resource | ||||
|     private TenantMapper tenantMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private TenantProperties tenantProperties; | ||||
|     @MockBean | ||||
|     private TenantPackageService tenantPackageService; | ||||
|     @MockBean | ||||
|     private AdminUserService userService; | ||||
|     @MockBean | ||||
|     private RoleService roleService; | ||||
|     @MockBean | ||||
|     private MenuService menuService; | ||||
|     @MockBean | ||||
|     private PermissionService permissionService; | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void setUp() { | ||||
|         // 清理租户上下文 | ||||
|         TenantContextHolder.clear(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantIdList() { | ||||
|         // mock 数据 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L)); | ||||
|         tenantMapper.insert(tenant); | ||||
|  | ||||
|         // 调用,并断言业务异常 | ||||
|         List<Long> result = tenantService.getTenantIdList(); | ||||
|         assertEquals(Collections.singletonList(1L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenant_notExists() { | ||||
|         assertServiceException(() -> tenantService.validTenant(randomLongId()), TENANT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenant_disable() { | ||||
|         // mock 数据 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         tenantMapper.insert(tenant); | ||||
|  | ||||
|         // 调用,并断言业务异常 | ||||
|         assertServiceException(() -> tenantService.validTenant(1L), TENANT_DISABLE, tenant.getName()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenant_expired() { | ||||
|         // mock 数据 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setExpireTime(buildTime(2020, 2, 2))); | ||||
|         tenantMapper.insert(tenant); | ||||
|  | ||||
|         // 调用,并断言业务异常 | ||||
|         assertServiceException(() -> tenantService.validTenant(1L), TENANT_EXPIRE, tenant.getName()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidTenant_success() { | ||||
|         // mock 数据 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setExpireTime(LocalDateTime.now().plusDays(1))); | ||||
|         tenantMapper.insert(tenant); | ||||
|  | ||||
|         // 调用,并断言业务异常 | ||||
|         tenantService.validTenant(1L); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateTenant() { | ||||
|         // mock 套餐 100L | ||||
|         TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L)); | ||||
|         when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage); | ||||
|         // mock 角色 200L | ||||
|         when(roleService.createRole(argThat(role -> { | ||||
|             assertEquals(RoleCodeEnum.TENANT_ADMIN.getName(), role.getName()); | ||||
|             assertEquals(RoleCodeEnum.TENANT_ADMIN.getCode(), role.getCode()); | ||||
|             assertEquals(0, role.getSort()); | ||||
|             assertEquals("系统自动生成", role.getRemark()); | ||||
|             return true; | ||||
|         }), eq(RoleTypeEnum.SYSTEM.getType()))).thenReturn(200L); | ||||
|         // mock 用户 300L | ||||
|         when(userService.createUser(argThat(user -> { | ||||
|             assertEquals("yunai", user.getUsername()); | ||||
|             assertEquals("yuanma", user.getPassword()); | ||||
|             assertEquals("芋道", user.getNickname()); | ||||
|             assertEquals("15601691300", user.getMobile()); | ||||
|             return true; | ||||
|         }))).thenReturn(300L); | ||||
|  | ||||
|         // 准备参数 | ||||
|         TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { | ||||
|             o.setContactName("芋道"); | ||||
|             o.setContactMobile("15601691300"); | ||||
|             o.setPackageId(100L); | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|             o.setWebsite("https://www.iocoder.cn"); | ||||
|             o.setUsername("yunai"); | ||||
|             o.setPassword("yuanma"); | ||||
|         }).setId(null); // 设置为 null,方便后面校验 | ||||
|  | ||||
|         // 调用 | ||||
|         Long tenantId = tenantService.createTenant(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(tenantId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         TenantDO tenant = tenantMapper.selectById(tenantId); | ||||
|         assertPojoEquals(reqVO, tenant, "id"); | ||||
|         assertEquals(300L, tenant.getContactUserId()); | ||||
|         // verify 分配权限 | ||||
|         verify(permissionService).assignRoleMenu(eq(200L), same(tenantPackage.getMenuIds())); | ||||
|         // verify 分配角色 | ||||
|         verify(permissionService).assignUserRole(eq(300L), eq(singleton(200L))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateTenant_success() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setStatus(randomCommonStatus())); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { | ||||
|             o.setId(dbTenant.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomCommonStatus()); | ||||
|             o.setWebsite(randomString()); | ||||
|         }); | ||||
|  | ||||
|         // mock 套餐 | ||||
|         TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setMenuIds(asSet(200L, 201L))); | ||||
|         when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage); | ||||
|         // mock 所有角色 | ||||
|         RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())); | ||||
|         role100.setTenantId(dbTenant.getId()); | ||||
|         RoleDO role101 = randomPojo(RoleDO.class, o -> o.setId(101L)); | ||||
|         role101.setTenantId(dbTenant.getId()); | ||||
|         when(roleService.getRoleList()).thenReturn(asList(role100, role101)); | ||||
|         // mock 每个角色的权限 | ||||
|         when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L)); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.updateTenant(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, tenant); | ||||
|         // verify 设置角色权限 | ||||
|         verify(permissionService).assignRoleMenu(eq(100L), eq(asSet(200L, 201L))); | ||||
|         verify(permissionService).assignRoleMenu(eq(101L), eq(asSet(201L))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateTenant_notExists() { | ||||
|         // 准备参数 | ||||
|         TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateTenant_system() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { | ||||
|             o.setId(dbTenant.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用,校验业务异常 | ||||
|         assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_CAN_NOT_UPDATE_SYSTEM); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenant_success() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbTenant.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.deleteTenant(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(tenantMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenant_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantService.deleteTenant(id), TENANT_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteTenant_system() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbTenant.getId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> tenantService.deleteTenant(id), TENANT_CAN_NOT_UPDATE_SYSTEM); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenant() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbTenant.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         TenantDO result = tenantService.getTenant(id); | ||||
|         // 校验存在 | ||||
|         assertPojoEquals(result, dbTenant); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantPage() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到 | ||||
|             o.setName("芋道源码"); | ||||
|             o.setContactName("芋艿"); | ||||
|             o.setContactMobile("15601691300"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2020, 12, 12)); | ||||
|         }); | ||||
|         tenantMapper.insert(dbTenant); | ||||
|         // 测试 name 不匹配 | ||||
|         tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setName(randomString()))); | ||||
|         // 测试 contactName 不匹配 | ||||
|         tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactName(randomString()))); | ||||
|         // 测试 contactMobile 不匹配 | ||||
|         tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactMobile(randomString()))); | ||||
|         // 测试 status 不匹配 | ||||
|         tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setCreateTime(buildTime(2021, 12, 12)))); | ||||
|         // 准备参数 | ||||
|         TenantPageReqVO reqVO = new TenantPageReqVO(); | ||||
|         reqVO.setName("芋道"); | ||||
|         reqVO.setContactName("艿"); | ||||
|         reqVO.setContactMobile("1560"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<TenantDO> pageResult = tenantService.getTenantPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbTenant, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantByName() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setName("芋道")); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         TenantDO result = tenantService.getTenantByName("芋道"); | ||||
|         // 校验存在 | ||||
|         assertPojoEquals(result, dbTenant); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantByWebsite() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsite("https://www.iocoder.cn")); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         TenantDO result = tenantService.getTenantByWebsite("https://www.iocoder.cn"); | ||||
|         // 校验存在 | ||||
|         assertPojoEquals(result, dbTenant); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantListByPackageId() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); | ||||
|         tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 | ||||
|         TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); | ||||
|         tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         List<TenantDO> result = tenantService.getTenantListByPackageId(1L); | ||||
|         assertEquals(1, result.size()); | ||||
|         assertPojoEquals(dbTenant1, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTenantCountByPackageId() { | ||||
|         // mock 数据 | ||||
|         TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); | ||||
|         tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 | ||||
|         TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); | ||||
|         tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         Long count = tenantService.getTenantCountByPackageId(1L); | ||||
|         assertEquals(1, count); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHandleTenantInfo_disable() { | ||||
|         // 准备参数 | ||||
|         TenantInfoHandler handler = mock(TenantInfoHandler.class); | ||||
|         // mock 禁用 | ||||
|         when(tenantProperties.getEnable()).thenReturn(false); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.handleTenantInfo(handler); | ||||
|         // 断言 | ||||
|         verify(handler, never()).handle(any()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHandleTenantInfo_success() { | ||||
|         // 准备参数 | ||||
|         TenantInfoHandler handler = mock(TenantInfoHandler.class); | ||||
|         // mock 未禁用 | ||||
|         when(tenantProperties.getEnable()).thenReturn(true); | ||||
|         // mock 租户 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         TenantContextHolder.setTenantId(dbTenant.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.handleTenantInfo(handler); | ||||
|         // 断言 | ||||
|         verify(handler).handle(argThat(argument -> { | ||||
|             assertPojoEquals(dbTenant, argument); | ||||
|             return true; | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHandleTenantMenu_disable() { | ||||
|         // 准备参数 | ||||
|         TenantMenuHandler handler = mock(TenantMenuHandler.class); | ||||
|         // mock 禁用 | ||||
|         when(tenantProperties.getEnable()).thenReturn(false); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.handleTenantMenu(handler); | ||||
|         // 断言 | ||||
|         verify(handler, never()).handle(any()); | ||||
|     } | ||||
|  | ||||
|     @Test // 系统租户的情况 | ||||
|     public void testHandleTenantMenu_system() { | ||||
|         // 准备参数 | ||||
|         TenantMenuHandler handler = mock(TenantMenuHandler.class); | ||||
|         // mock 未禁用 | ||||
|         when(tenantProperties.getEnable()).thenReturn(true); | ||||
|         // mock 租户 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         TenantContextHolder.setTenantId(dbTenant.getId()); | ||||
|         // mock 菜单 | ||||
|         when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), | ||||
|                 randomPojo(MenuDO.class, o -> o.setId(101L)))); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.handleTenantMenu(handler); | ||||
|         // 断言 | ||||
|         verify(handler).handle(asSet(100L, 101L)); | ||||
|     } | ||||
|  | ||||
|     @Test // 普通租户的情况 | ||||
|     public void testHandleTenantMenu_normal() { | ||||
|         // 准备参数 | ||||
|         TenantMenuHandler handler = mock(TenantMenuHandler.class); | ||||
|         // mock 未禁用 | ||||
|         when(tenantProperties.getEnable()).thenReturn(true); | ||||
|         // mock 租户 | ||||
|         TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(200L)); | ||||
|         tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 | ||||
|         TenantContextHolder.setTenantId(dbTenant.getId()); | ||||
|         // mock 菜单 | ||||
|         when(tenantPackageService.getTenantPackage(eq(200L))).thenReturn(randomPojo(TenantPackageDO.class, | ||||
|                 o -> o.setMenuIds(asSet(100L, 101L)))); | ||||
|  | ||||
|         // 调用 | ||||
|         tenantService.handleTenantMenu(handler); | ||||
|         // 断言 | ||||
|         verify(handler).handle(asSet(100L, 101L)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,762 @@ | ||||
| package cn.iocoder.yudao.module.system.service.user; | ||||
|  | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.infra.api.file.FileApi; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper; | ||||
| import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; | ||||
| import cn.iocoder.yudao.module.system.enums.common.SexEnum; | ||||
| import cn.iocoder.yudao.module.system.service.dept.DeptService; | ||||
| import cn.iocoder.yudao.module.system.service.dept.PostService; | ||||
| import cn.iocoder.yudao.module.system.service.permission.PermissionService; | ||||
| import cn.iocoder.yudao.module.system.service.tenant.TenantService; | ||||
| 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.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 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.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||
| import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singleton; | ||||
| import static java.util.Collections.singletonList; | ||||
| import static org.assertj.core.util.Lists.newArrayList; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.*; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| @Import(AdminUserServiceImpl.class) | ||||
| public class AdminUserServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private AdminUserServiceImpl userService; | ||||
|  | ||||
|     @Resource | ||||
|     private AdminUserMapper userMapper; | ||||
|     @Resource | ||||
|     private UserPostMapper userPostMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private DeptService deptService; | ||||
|     @MockBean | ||||
|     private PostService postService; | ||||
|     @MockBean | ||||
|     private PermissionService permissionService; | ||||
|     @MockBean | ||||
|     private PasswordEncoder passwordEncoder; | ||||
|     @MockBean | ||||
|     private TenantService tenantService; | ||||
|     @MockBean | ||||
|     private FileApi fileApi; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreatUser_success() { | ||||
|         // 准备参数 | ||||
|         UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { | ||||
|             o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); | ||||
|             o.setMobile(randomString()); | ||||
|             o.setPostIds(asSet(1L, 2L)); | ||||
|         }).setId(null); // 避免 id 被赋值 | ||||
|         // mock 账户额度充足 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(1)); | ||||
|         doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { | ||||
|             handler.handle(tenant); | ||||
|             return true; | ||||
|         })); | ||||
|         // mock deptService 的方法 | ||||
|         DeptDO dept = randomPojo(DeptDO.class, o -> { | ||||
|             o.setId(reqVO.getDeptId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | ||||
|         // mock postService 的方法 | ||||
|         List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> | ||||
|                 randomPojo(PostDO.class, o -> { | ||||
|                     o.setId(postId); | ||||
|                     o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|                 })); | ||||
|         when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); | ||||
|         // mock passwordEncoder 的方法 | ||||
|         when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma"); | ||||
|  | ||||
|         // 调用 | ||||
|         Long userId = userService.createUser(reqVO); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertPojoEquals(reqVO, user, "password", "id"); | ||||
|         assertEquals("yudaoyuanma", user.getPassword()); | ||||
|         assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus()); | ||||
|         // 断言关联岗位 | ||||
|         List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId()); | ||||
|         assertEquals(1L, userPosts.get(0).getPostId()); | ||||
|         assertEquals(2L, userPosts.get(1).getPostId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreatUser_max() { | ||||
|         // 准备参数 | ||||
|         UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class); | ||||
|         // mock 账户额度不足 | ||||
|         TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(-1)); | ||||
|         doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { | ||||
|             handler.handle(tenant); | ||||
|             return true; | ||||
|         })); | ||||
|  | ||||
|         // 调用,并断言异常 | ||||
|         assertServiceException(() -> userService.createUser(reqVO), USER_COUNT_MAX, -1); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUser_success() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L))); | ||||
|         userMapper.insert(dbUser); | ||||
|         userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L)); | ||||
|         userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L)); | ||||
|         // 准备参数 | ||||
|         UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { | ||||
|             o.setId(dbUser.getId()); | ||||
|             o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); | ||||
|             o.setMobile(randomString()); | ||||
|             o.setPostIds(asSet(2L, 3L)); | ||||
|         }); | ||||
|         // mock deptService 的方法 | ||||
|         DeptDO dept = randomPojo(DeptDO.class, o -> { | ||||
|             o.setId(reqVO.getDeptId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | ||||
|         // mock postService 的方法 | ||||
|         List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> | ||||
|                 randomPojo(PostDO.class, o -> { | ||||
|                     o.setId(postId); | ||||
|                     o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|                 })); | ||||
|         when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); | ||||
|  | ||||
|         // 调用 | ||||
|         userService.updateUser(reqVO); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(reqVO.getId()); | ||||
|         assertPojoEquals(reqVO, user, "password"); | ||||
|         // 断言关联岗位 | ||||
|         List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId()); | ||||
|         assertEquals(2L, userPosts.get(0).getPostId()); | ||||
|         assertEquals(3L, userPosts.get(1).getPostId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserLogin() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(o -> o.setLoginDate(null)); | ||||
|         userMapper.insert(user); | ||||
|         // 准备参数 | ||||
|         Long id = user.getId(); | ||||
|         String loginIp = randomString(); | ||||
|  | ||||
|         // 调用 | ||||
|         userService.updateUserLogin(id, loginIp); | ||||
|         // 断言 | ||||
|         AdminUserDO dbUser = userMapper.selectById(id); | ||||
|         assertEquals(loginIp, dbUser.getLoginIp()); | ||||
|         assertNotNull(dbUser.getLoginDate()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserProfile_success() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|         UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> { | ||||
|             o.setMobile(randomString()); | ||||
|             o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         userService.updateUserProfile(userId, reqVO); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertPojoEquals(reqVO, user); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserPassword_success() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou")); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|         UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> { | ||||
|             o.setOldPassword("tudou"); | ||||
|             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); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertEquals("encode:yuanma", user.getPassword()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserAvatar_success() throws Exception { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|         byte[] avatarFileBytes = randomBytes(10); | ||||
|         ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); | ||||
|         // mock 方法 | ||||
|         String avatar = randomString(); | ||||
|         when(fileApi.createFile(eq( avatarFileBytes))).thenReturn(avatar); | ||||
|  | ||||
|         // 调用 | ||||
|         userService.updateUserAvatar(userId, avatarFile); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertEquals(avatar, user.getAvatar()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserPassword02_success() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         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); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertEquals("encode:" + password, user.getPassword()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateUserStatus() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|         Integer status = randomCommonStatus(); | ||||
|  | ||||
|         // 调用 | ||||
|         userService.updateUserStatus(userId, status); | ||||
|         // 断言 | ||||
|         AdminUserDO user = userMapper.selectById(userId); | ||||
|         assertEquals(status, user.getStatus()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteUser_success(){ | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|  | ||||
|         // 调用数据 | ||||
|         userService.deleteUser(userId); | ||||
|         // 校验结果 | ||||
|         assertNull(userMapper.selectById(userId)); | ||||
|         // 校验调用次数 | ||||
|         verify(permissionService, times(1)).processUserDeleted(eq(userId)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserByUsername() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         String username = dbUser.getUsername(); | ||||
|  | ||||
|         // 调用 | ||||
|         AdminUserDO user = userService.getUserByUsername(username); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbUser, user); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserByMobile() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         String mobile = dbUser.getMobile(); | ||||
|  | ||||
|         // 调用 | ||||
|         AdminUserDO user = userService.getUserByMobile(mobile); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbUser, user); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserPage() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = initGetUserPageData(); | ||||
|         // 准备参数 | ||||
|         UserPageReqVO reqVO = new UserPageReqVO(); | ||||
|         reqVO.setUsername("tu"); | ||||
|         reqVO.setMobile("1560"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); | ||||
|         reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 | ||||
|         // mock 方法 | ||||
|         List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); | ||||
|         when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbUser, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 初始化 getUserPage 方法的测试数据 | ||||
|      */ | ||||
|     private AdminUserDO initGetUserPageData() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到 | ||||
|             o.setUsername("tudou"); | ||||
|             o.setMobile("15601691300"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2020, 12, 12)); | ||||
|             o.setDeptId(2L); | ||||
|         }); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 测试 username 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setUsername("dou"))); | ||||
|         // 测试 mobile 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setMobile("18818260888"))); | ||||
|         // 测试 status 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11)))); | ||||
|         // 测试 dept 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L))); | ||||
|         return dbUser; | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUser() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         Long userId = dbUser.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         AdminUserDO user = userService.getUser(userId); | ||||
|         // 断言 | ||||
|         assertPojoEquals(dbUser, user); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserListByDeptIds() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L)); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 测试 deptId 不匹配 | ||||
|         userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L))); | ||||
|         // 准备参数 | ||||
|         Collection<Long> deptIds = singleton(1L); | ||||
|  | ||||
|         // 调用 | ||||
|         List<AdminUserDO> list = userService.getUserListByDeptIds(deptIds); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertEquals(dbUser, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 情况一,校验不通过,导致插入失败 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testImportUserList_01() { | ||||
|         // 准备参数 | ||||
|         UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | ||||
|         }); | ||||
|         // mock 方法,模拟失败 | ||||
|         doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any()); | ||||
|  | ||||
|         // 调用 | ||||
|         UserImportRespVO respVO = userService.importUserList(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 testImportUserList_02() { | ||||
|         // 准备参数 | ||||
|         UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | ||||
|         }); | ||||
|         // mock deptService 的方法 | ||||
|         DeptDO dept = randomPojo(DeptDO.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"); | ||||
|  | ||||
|         // 调用 | ||||
|         UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); | ||||
|         // 断言 | ||||
|         assertEquals(1, respVO.getCreateUsernames().size()); | ||||
|         AdminUserDO 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 testImportUserList_03() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | ||||
|             o.setUsername(dbUser.getUsername()); | ||||
|         }); | ||||
|         // mock deptService 的方法 | ||||
|         DeptDO dept = randomPojo(DeptDO.class, o -> { | ||||
|             o.setId(importUser.getDeptId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | ||||
|  | ||||
|         // 调用 | ||||
|         UserImportRespVO respVO = userService.importUserList(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 testImportUserList_04() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO dbUser = randomAdminUserDO(); | ||||
|         userMapper.insert(dbUser); | ||||
|         // 准备参数 | ||||
|         UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | ||||
|             o.setUsername(dbUser.getUsername()); | ||||
|         }); | ||||
|         // mock deptService 的方法 | ||||
|         DeptDO dept = randomPojo(DeptDO.class, o -> { | ||||
|             o.setId(importUser.getDeptId()); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | ||||
|  | ||||
|         // 调用 | ||||
|         UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); | ||||
|         // 断言 | ||||
|         assertEquals(0, respVO.getCreateUsernames().size()); | ||||
|         assertEquals(1, respVO.getUpdateUsernames().size()); | ||||
|         AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0)); | ||||
|         assertPojoEquals(importUser, user); | ||||
|         assertEquals(0, respVO.getFailureUsernames().size()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUserExists_notExists() { | ||||
|         assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUsernameUnique_usernameExistsForCreate() { | ||||
|         // 准备参数 | ||||
|         String username = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateUsernameUnique(null, username), | ||||
|                 USER_USERNAME_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUsernameUnique_usernameExistsForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String username = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateUsernameUnique(id, username), | ||||
|                 USER_USERNAME_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateEmailUnique_emailExistsForCreate() { | ||||
|         // 准备参数 | ||||
|         String email = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateEmailUnique(null, email), | ||||
|                 USER_EMAIL_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateEmailUnique_emailExistsForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String email = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateEmailUnique(id, email), | ||||
|                 USER_EMAIL_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMobileUnique_mobileExistsForCreate() { | ||||
|         // 准备参数 | ||||
|         String mobile = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateMobileUnique(null, mobile), | ||||
|                 USER_MOBILE_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateMobileUnique_mobileExistsForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String mobile = randomString(); | ||||
|         // mock 数据 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateMobileUnique(id, mobile), | ||||
|                 USER_MOBILE_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateOldPassword_notExists() { | ||||
|         assertServiceException(() -> userService.validateOldPassword(randomLongId(), randomString()), | ||||
|                 USER_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateOldPassword_passwordFailed() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(); | ||||
|         userMapper.insert(user); | ||||
|         // 准备参数 | ||||
|         Long id = user.getId(); | ||||
|         String oldPassword = user.getPassword(); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> userService.validateOldPassword(id, oldPassword), | ||||
|                 USER_PASSWORD_FAILED); | ||||
|         // 校验调用 | ||||
|         verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUserListByPostIds() { | ||||
|         // 准备参数 | ||||
|         Collection<Long> postIds = asSet(10L, 20L); | ||||
|         // mock user1 数据 | ||||
|         AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L))); | ||||
|         userMapper.insert(user1); | ||||
|         userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L)); | ||||
|         userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L)); | ||||
|         // mock user2 数据 | ||||
|         AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L))); | ||||
|         userMapper.insert(user2); | ||||
|         userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L)); | ||||
|  | ||||
|         // 调用 | ||||
|         List<AdminUserDO> result = userService.getUserListByPostIds(postIds); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(user1, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserList() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(); | ||||
|         userMapper.insert(user); | ||||
|         // 测试 id 不匹配 | ||||
|         userMapper.insert(randomAdminUserDO()); | ||||
|         // 准备参数 | ||||
|         Collection<Long> ids = singleton(user.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<AdminUserDO> result = userService.getUserList(ids); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(user, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserMap() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(); | ||||
|         userMapper.insert(user); | ||||
|         // 测试 id 不匹配 | ||||
|         userMapper.insert(randomAdminUserDO()); | ||||
|         // 准备参数 | ||||
|         Collection<Long> ids = singleton(user.getId()); | ||||
|  | ||||
|         // 调用 | ||||
|         Map<Long, AdminUserDO> result = userService.getUserMap(ids); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(user, result.get(user.getId())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserListByNickname() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(o -> o.setNickname("芋头")); | ||||
|         userMapper.insert(user); | ||||
|         // 测试 nickname 不匹配 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setNickname("源码"))); | ||||
|         // 准备参数 | ||||
|         String nickname = "芋"; | ||||
|  | ||||
|         // 调用 | ||||
|         List<AdminUserDO> result = userService.getUserListByNickname(nickname); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(user, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetUserListByStatus() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||||
|         userMapper.insert(user); | ||||
|         // 测试 status 不匹配 | ||||
|         userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         Integer status = CommonStatusEnum.DISABLE.getStatus(); | ||||
|  | ||||
|         // 调用 | ||||
|         List<AdminUserDO> result = userService.getUserListByStatus(status); | ||||
|         // 断言 | ||||
|         assertEquals(1, result.size()); | ||||
|         assertEquals(user, result.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUserList_success() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         userMapper.insert(userDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(userDO.getId()); | ||||
|  | ||||
|         // 调用,无需断言 | ||||
|         userService.validateUserList(ids); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUserList_notFound() { | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(randomLongId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> userService.validateUserList(ids), USER_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testValidateUserList_notEnable() { | ||||
|         // mock 数据 | ||||
|         AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); | ||||
|         userMapper.insert(userDO); | ||||
|         // 准备参数 | ||||
|         List<Long> ids = singletonList(userDO.getId()); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> userService.validateUserList(ids), USER_IS_DISABLE, | ||||
|                 userDO.getNickname()); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static AdminUserDO randomAdminUserDO(Consumer<AdminUserDO>... consumers) { | ||||
|         Consumer<AdminUserDO> consumer = (o) -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|             o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | ||||
|         }; | ||||
|         return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| 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;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 | ||||
|     driver-class-name: org.h2.Driver | ||||
|     username: sa | ||||
|     password: | ||||
|     druid: | ||||
|       async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 | ||||
|       initial-size: 1 # 单元测试,配置为 1,提升启动速度 | ||||
|   sql: | ||||
|     init: | ||||
|       schema-locations: classpath:/sql/create_tables.sql | ||||
|  | ||||
|   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 | ||||
|   redis: | ||||
|     host: 127.0.0.1 # 地址 | ||||
|     port: 16379 # 端口(单元测试,使用 16379 端口) | ||||
|     database: 0 # 数据库索引 | ||||
|  | ||||
|  | ||||
| mybatis: | ||||
|   lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 | ||||
|  | ||||
| --- #################### 定时任务相关配置 #################### | ||||
|  | ||||
| --- #################### 配置中心相关配置 #################### | ||||
|  | ||||
| --- #################### 服务保障相关配置 #################### | ||||
|  | ||||
| # Lock4j 配置项(单元测试,禁用 Lock4j) | ||||
|  | ||||
| # Resilience4j 配置项 | ||||
|  | ||||
| --- #################### 监控相关配置 #################### | ||||
|  | ||||
| --- #################### 芋道相关配置 #################### | ||||
|  | ||||
| # 芋道配置项,设置当前项目所有自定义的配置 | ||||
| yudao: | ||||
|   info: | ||||
|     base-package: cn.iocoder.yudao.module | ||||
|   captcha: | ||||
|     timeout: 5m | ||||
|     width: 160 | ||||
|     height: 60 | ||||
|     enable: true | ||||
| @ -0,0 +1,4 @@ | ||||
| <configuration> | ||||
|     <!-- 引用 Spring Boot 的 logback 基础配置 --> | ||||
|     <include resource="org/springframework/boot/logging/logback/defaults.xml" /> | ||||
| </configuration> | ||||
| @ -0,0 +1,35 @@ | ||||
| DELETE FROM "system_dept"; | ||||
| DELETE FROM "system_dict_data"; | ||||
| DELETE FROM "system_role"; | ||||
| DELETE FROM "system_role_menu"; | ||||
| DELETE FROM "system_menu"; | ||||
| DELETE FROM "system_user_role"; | ||||
| DELETE FROM "system_dict_type"; | ||||
| DELETE FROM "system_user_session"; | ||||
| DELETE FROM "system_post"; | ||||
| DELETE FROM "system_user_post"; | ||||
| DELETE FROM "system_notice"; | ||||
| DELETE FROM "system_login_log"; | ||||
| DELETE FROM "system_operate_log"; | ||||
| DELETE FROM "system_users"; | ||||
| DELETE FROM "system_sms_channel"; | ||||
| DELETE FROM "system_sms_template"; | ||||
| DELETE FROM "system_sms_log"; | ||||
| DELETE FROM "system_sms_code"; | ||||
| DELETE FROM "system_error_code"; | ||||
| DELETE FROM "system_social_client"; | ||||
| DELETE FROM "system_social_user"; | ||||
| DELETE FROM "system_social_user_bind"; | ||||
| DELETE FROM "system_tenant"; | ||||
| DELETE FROM "system_tenant_package"; | ||||
| DELETE FROM "system_sensitive_word"; | ||||
| DELETE FROM "system_oauth2_client"; | ||||
| DELETE FROM "system_oauth2_approve"; | ||||
| DELETE FROM "system_oauth2_access_token"; | ||||
| DELETE FROM "system_oauth2_refresh_token"; | ||||
| DELETE FROM "system_oauth2_code"; | ||||
| DELETE FROM "system_mail_account"; | ||||
| DELETE FROM "system_mail_template"; | ||||
| DELETE FROM "system_mail_log"; | ||||
| DELETE FROM "system_notify_template"; | ||||
| DELETE FROM "system_notify_message"; | ||||
| @ -0,0 +1,648 @@ | ||||
| CREATE TABLE IF NOT EXISTS "system_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_user_id" bigint 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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '部门表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_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', | ||||
|     "color_type" varchar(100) NOT NULL DEFAULT '', | ||||
|     "css_class" varchar(100) NOT NULL DEFAULT '', | ||||
|     "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 "system_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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '角色信息表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '角色和菜单关联表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_menu" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar(50) NOT NULL, | ||||
|     "permission" varchar(100) NOT NULL DEFAULT '', | ||||
|     "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, | ||||
|     "component_name" varchar(255) DEFAULT NULL, | ||||
|     "status" tinyint NOT NULL DEFAULT '0', | ||||
|     "visible" bit NOT NULL DEFAULT TRUE, | ||||
|     "keep_alive" bit NOT NULL DEFAULT TRUE, | ||||
|     "always_show" bit NOT NULL DEFAULT TRUE, | ||||
|     "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 "system_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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '用户和角色关联表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_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, | ||||
|     "deleted_time" timestamp NOT NULL, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '字典类型表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS `system_user_session` ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     `token` varchar(32) NOT NULL, | ||||
|     `user_id` bigint DEFAULT NULL, | ||||
|     "user_type" tinyint NOT 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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY (`id`) | ||||
| ) COMMENT '用户在线 Session'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '岗位信息表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS `system_user_post`( | ||||
|     "id"          bigint    NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "user_id"     bigint             DEFAULT NULL, | ||||
|     "post_id"     bigint             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, | ||||
|     "tenant_id"   bigint    not null default '0', | ||||
|     PRIMARY KEY (`id`) | ||||
| ) COMMENT ='用户岗位表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_notice" ( | ||||
| 	"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
| 	"title" varchar(50) NOT NULL COMMENT '公告标题', | ||||
| 	"content" text NOT NULL COMMENT '公告内容', | ||||
| 	"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 '是否删除', | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY("id") | ||||
| ) COMMENT '通知公告表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS `system_login_log` ( | ||||
|     `id`          bigint(20)   NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     `log_type`    bigint(4)    NOT NULL, | ||||
|     "user_id" bigint not null default '0', | ||||
|     "user_type" tinyint 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 `system_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, | ||||
|     "user_type" tinyint not null default '0', | ||||
|     `module`           varchar(50)   NOT NULL, | ||||
|     `name`             varchar(50)   NOT NULL, | ||||
|     `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', | ||||
|     "tenant_id"         bigint not null default  '0', | ||||
|     PRIMARY KEY (`id`) | ||||
| ) COMMENT ='操作日志记录'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_users" ( | ||||
|     "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, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     primary key ("id") | ||||
| ) comment '用户信息表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_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 "system_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 "system_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 "system_sms_code" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "mobile" varchar(11) NOT NULL, | ||||
|     "code" varchar(11) NOT NULL, | ||||
|     "scene" bigint NOT NULL, | ||||
|     "create_ip" varchar NOT NULL, | ||||
|     "today_index" int NOT NULL, | ||||
|     "used" bit NOT NULL DEFAULT FALSE, | ||||
|     "used_time" timestamp DEFAULT NULL, | ||||
|     "used_ip" varchar 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 "system_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 '错误码表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_social_client" ( | ||||
|   "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|   "name" varchar(255) NOT NULL, | ||||
|   "social_type" int NOT NULL, | ||||
|   "user_type" int NOT NULL, | ||||
|   "client_id" varchar(255) NOT NULL, | ||||
|   "client_secret" varchar(255) NOT NULL, | ||||
|   "agent_id" varchar(255) NOT NULL, | ||||
|   "status" int 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 NOT NULL DEFAULT FALSE, | ||||
|   "tenant_id" bigint not null default  '0', | ||||
|   PRIMARY KEY ("id") | ||||
| ) COMMENT '社交客户端表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_social_user" ( | ||||
|    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|    "type" tinyint NOT NULL, | ||||
|    "openid" varchar(64) NOT NULL, | ||||
|    "token" varchar(256) DEFAULT NULL, | ||||
|    "raw_token_info" varchar(1024) NOT NULL, | ||||
|    "nickname" varchar(32) NOT NULL, | ||||
|    "avatar" varchar(255) DEFAULT NULL, | ||||
|    "raw_user_info" varchar(1024) NOT NULL, | ||||
|    "code" varchar(64) NOT NULL, | ||||
|    "state" varchar(64), | ||||
|    "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 "system_social_user_bind" ( | ||||
|    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|    "user_id" bigint NOT NULL, | ||||
|    "user_type" tinyint NOT NULL, | ||||
|    "social_type" tinyint NOT NULL, | ||||
|    "social_user_id" number 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 "system_tenant" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar(63) NOT NULL, | ||||
|     "contact_user_id" bigint NOT NULL DEFAULT '0', | ||||
|     "contact_name" varchar(255) NOT NULL, | ||||
|     "contact_mobile" varchar(255), | ||||
|     "status" tinyint NOT NULL, | ||||
|     "website" varchar(63) DEFAULT '', | ||||
|     "package_id"  bigint NOT NULL, | ||||
|     "expire_time" timestamp NOT NULL, | ||||
|     "account_count" int 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 "system_tenant_package" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar(30) NOT NULL, | ||||
|     "status" tinyint NOT NULL, | ||||
|     "remark" varchar(256), | ||||
|     "menu_ids" varchar(2048) 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 NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '租户套餐表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_sensitive_word" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar(255) NOT NULL, | ||||
|     "tags" varchar(1024) NOT NULL, | ||||
|     "status" bit NOT NULL DEFAULT FALSE, | ||||
|     "description" varchar(512), | ||||
|     "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 NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '系统敏感词'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_oauth2_client" ( | ||||
|   "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|   "client_id" varchar NOT NULL, | ||||
|   "secret" varchar NOT NULL, | ||||
|   "name" varchar NOT NULL, | ||||
|   "logo" varchar NOT NULL, | ||||
|   "description" varchar, | ||||
|   "status" int NOT NULL, | ||||
|   "access_token_validity_seconds" int NOT NULL, | ||||
|   "refresh_token_validity_seconds" int NOT NULL, | ||||
|   "redirect_uris" varchar NOT NULL, | ||||
|   "authorized_grant_types" varchar NOT NULL, | ||||
|   "scopes" varchar NOT NULL DEFAULT '', | ||||
|   "auto_approve_scopes" varchar NOT NULL DEFAULT '', | ||||
|   "authorities" varchar NOT NULL DEFAULT '', | ||||
|   "resource_ids" varchar NOT NULL DEFAULT '', | ||||
|   "additional_information" varchar NOT NULL DEFAULT '', | ||||
|   "creator" varchar DEFAULT '', | ||||
|   "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|   "updater" varchar DEFAULT '', | ||||
|   "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|   "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|   PRIMARY KEY ("id") | ||||
| ) COMMENT 'OAuth2 客户端表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_oauth2_approve" ( | ||||
|   "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|   "user_id" bigint NOT NULL, | ||||
|   "user_type" tinyint NOT NULL, | ||||
|   "client_id" varchar NOT NULL, | ||||
|   "scope" varchar NOT NULL, | ||||
|   "approved" bit NOT NULL DEFAULT FALSE, | ||||
|   "expires_time" datetime NOT NULL, | ||||
|   "creator" varchar DEFAULT '', | ||||
|   "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|   "updater" varchar DEFAULT '', | ||||
|   "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|   "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|   PRIMARY KEY ("id") | ||||
| ) COMMENT 'OAuth2 批准表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_oauth2_access_token" ( | ||||
|    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|    "user_id" bigint NOT NULL, | ||||
|    "user_type" tinyint NOT NULL, | ||||
|    "access_token" varchar NOT NULL, | ||||
|    "refresh_token" varchar NOT NULL, | ||||
|    "client_id" varchar NOT NULL, | ||||
|    "scopes" varchar NOT NULL, | ||||
|    "approved" bit NOT NULL DEFAULT FALSE, | ||||
|    "expires_time" datetime NOT NULL, | ||||
|    "creator" varchar DEFAULT '', | ||||
|    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|    "updater" varchar DEFAULT '', | ||||
|    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|    "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|    "tenant_id" bigint NOT NULL, | ||||
|    PRIMARY KEY ("id") | ||||
| ) COMMENT 'OAuth2 访问令牌'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_oauth2_refresh_token" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "user_id" bigint NOT NULL, | ||||
|     "user_type" tinyint NOT NULL, | ||||
|     "refresh_token" varchar NOT NULL, | ||||
|     "client_id" varchar NOT NULL, | ||||
|     "scopes" varchar NOT NULL, | ||||
|     "approved" bit NOT NULL DEFAULT FALSE, | ||||
|     "expires_time" datetime NOT NULL, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT 'OAuth2 刷新令牌'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_oauth2_code" ( | ||||
|      "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|      "user_id" bigint NOT NULL, | ||||
|      "user_type" tinyint NOT NULL, | ||||
|      "code" varchar NOT NULL, | ||||
|      "client_id" varchar NOT NULL, | ||||
|      "scopes" varchar NOT NULL, | ||||
|      "expires_time" datetime NOT NULL, | ||||
|      "redirect_uri" varchar NOT NULL, | ||||
|      "state" varchar NOT NULL, | ||||
|      "creator" varchar DEFAULT '', | ||||
|      "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|      "updater" varchar DEFAULT '', | ||||
|      "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|      "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|      PRIMARY KEY ("id") | ||||
| ) COMMENT 'OAuth2 刷新令牌'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_mail_account" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "mail" varchar NOT NULL, | ||||
|     "username" varchar NOT NULL, | ||||
|     "password" varchar NOT NULL, | ||||
|     "host" varchar NOT NULL, | ||||
|     "port" int NOT NULL, | ||||
|     "ssl_enable" bit NOT NULL, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '邮箱账号表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_mail_template" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar NOT NULL, | ||||
|     "code" varchar NOT NULL, | ||||
|     "account_id" bigint NOT NULL, | ||||
|     "nickname" varchar, | ||||
|     "title" varchar NOT NULL, | ||||
|     "content" varchar NOT NULL, | ||||
|     "params" varchar NOT NULL, | ||||
|     "status" varchar NOT NULL, | ||||
|     "remark" varchar, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '邮件模版表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_mail_log" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "user_id" bigint, | ||||
|     "user_type" varchar, | ||||
|     "to_mail" varchar NOT NULL, | ||||
|     "account_id" bigint NOT NULL, | ||||
|     "from_mail" varchar NOT NULL, | ||||
|     "template_id" bigint NOT NULL, | ||||
|     "template_code" varchar NOT NULL, | ||||
|     "template_nickname" varchar, | ||||
|     "template_title" varchar NOT NULL, | ||||
|     "template_content" varchar NOT NULL, | ||||
|     "template_params" varchar NOT NULL, | ||||
|     "send_status" varchar NOT NULL, | ||||
|     "send_time" datetime, | ||||
|     "send_message_id" varchar, | ||||
|     "send_exception" varchar, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '邮件日志表'; | ||||
|  | ||||
| -- 将该建表 SQL 语句,添加到 yudao-module-system-biz 模块的 test/resources/sql/create_tables.sql 文件里 | ||||
| CREATE TABLE IF NOT EXISTS "system_notify_template" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "name" varchar NOT NULL, | ||||
|     "code" varchar NOT NULL, | ||||
|     "nickname" varchar NOT NULL, | ||||
|     "content" varchar NOT NULL, | ||||
|     "type" varchar NOT NULL, | ||||
|     "params" varchar, | ||||
|     "status" varchar NOT NULL, | ||||
|     "remark" varchar, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '站内信模板表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "system_notify_message" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "user_id" bigint NOT NULL, | ||||
|     "user_type" varchar NOT NULL, | ||||
|     "template_id" bigint NOT NULL, | ||||
|     "template_code" varchar NOT NULL, | ||||
|     "template_nickname" varchar NOT NULL, | ||||
|     "template_content" varchar NOT NULL, | ||||
|     "template_type" int NOT NULL, | ||||
|     "template_params" varchar NOT NULL, | ||||
|     "read_status" bit NOT NULL, | ||||
|     "read_time" varchar, | ||||
|     "creator" varchar DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updater" varchar DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     "tenant_id" bigint not null default  '0', | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '站内信消息表'; | ||||
		Reference in New Issue
	
	Block a user
	 YangJ
					YangJ