diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java index 40db4ee0f..72da3cf89 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.service.mail; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.mail.MailAccount; import cn.hutool.extra.mail.MailUtil; -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.convert.mail.MailAccountConvert; @@ -20,15 +19,13 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** - * 邮箱模版 服务实现类 + * 邮箱发送 Service 实现类 * * @author wangjingyi * @since 2022-03-21 @@ -82,14 +79,13 @@ public class MailSendServiceImpl implements MailSendService { public Long sendSingleMail(String mail, Long userId, Integer userType, String templateCode, Map templateParams) { // 校验邮箱模版是否合法 - MailTemplateDO template = checkMailTemplateValid(templateCode); + MailTemplateDO template = validateMailTemplate(templateCode); // 校验邮箱账号是否合法 - MailAccountDO account = checkMailAccountValid(template.getAccountId()); + MailAccountDO account = validateMailAccount(template.getAccountId()); // 校验邮箱是否存在 - mail = checkMail(mail); - // 校验参数模版 - checkTemplateParams(template, templateParams); + mail = validateMail(mail); + validateTemplateParams(template, templateParams); // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); @@ -107,7 +103,7 @@ public class MailSendServiceImpl implements MailSendService { @Override public void doSendMail(MailSendMessage message) { // 1. 创建发送账号 - MailAccountDO account = checkMailAccountValid(message.getAccountId()); + MailAccountDO account = validateMailAccount(message.getAccountId()); MailAccount mailAccount = MailAccountConvert.INSTANCE.convert(account, message.getNickname()); // 2. 发送邮件 try { @@ -122,7 +118,7 @@ public class MailSendServiceImpl implements MailSendService { } @VisibleForTesting - public MailTemplateDO checkMailTemplateValid(String templateCode) { + MailTemplateDO validateMailTemplate(String templateCode) { // 获得邮件模板。考虑到效率,从缓存中获取 MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode); // 邮件模板不存在 @@ -133,7 +129,7 @@ public class MailSendServiceImpl implements MailSendService { } @VisibleForTesting - public MailAccountDO checkMailAccountValid(Long accountId) { + MailAccountDO validateMailAccount(Long accountId) { // 获得邮箱账号。考虑到效率,从缓存中获取 MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId); // 邮箱账号不存在 @@ -144,7 +140,7 @@ public class MailSendServiceImpl implements MailSendService { } @VisibleForTesting - public String checkMail(String mail) { + String validateMail(String mail) { if (StrUtil.isEmpty(mail)) { throw exception(MAIL_SEND_MAIL_NOT_EXISTS); } @@ -152,13 +148,13 @@ public class MailSendServiceImpl implements MailSendService { } /** - * 校验参数模板 + * 校验邮件参数是否确实 * * @param template 邮箱模板 - * @param templateParams 原始参数 + * @param templateParams 参数列表 */ @VisibleForTesting - public void checkTemplateParams(MailTemplateDO template, Map templateParams) { + void validateTemplateParams(MailTemplateDO template, Map templateParams) { template.getParams().forEach(key -> { Object value = templateParams.get(key); if (value == null) { diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java index 64634bf30..a0e41f75c 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java @@ -13,6 +13,7 @@ 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 static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; @@ -21,7 +22,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic 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.verify; +import static org.mockito.Mockito.when; /** * {@link MailAccountServiceImpl} 的单元测试类 @@ -107,6 +110,8 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 // 准备参数 Long id = dbMailAccount.getId(); + // mock 方法(无关联模版) + when(mailTemplateService.countByAccountId(eq(id))).thenReturn(0L); // 调用 mailAccountService.deleteMailAccount(id); @@ -115,6 +120,21 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { verify(mailProducer).sendMailAccountRefreshMessage(); } + @Test + public void testGetMailAccountFromCache() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 + mailAccountService.initLocalCache(); + // 准备参数 + Long id = dbMailAccount.getId(); + + // 调用 + MailAccountDO mailAccount = mailAccountService.getMailAccountFromCache(id); + // 断言 + assertPojoEquals(dbMailAccount, mailAccount); + } + @Test public void testDeleteMailAccount_notExists() { // 准备参数 @@ -149,4 +169,35 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { 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 list = mailAccountService.getMailAccountList(); + // 断言 + assertEquals(2, list.size()); + assertPojoEquals(dbMailAccount01, list.get(0)); + assertPojoEquals(dbMailAccount02, list.get(1)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java index ccf4ba737..53acd0ef0 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java @@ -116,6 +116,20 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { 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 数据 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java index 2d9dc2b65..911e91c89 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java @@ -6,14 +6,20 @@ 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; @@ -23,6 +29,7 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic 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.*; @@ -31,6 +38,10 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { @InjectMocks private MailSendServiceImpl mailSendService; + @Mock + private AdminUserService adminUserService; + @Mock + private MemberService memberService; @Mock private MailAccountService mailAccountService; @Mock @@ -55,6 +66,82 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { System.out.println("发送结果:" + messageId); } + @Test + public void testSendSingleMailToAdmin() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.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 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(template.getTitle()), eq(content)); + } + + @Test + public void testSendSingleMailToMember() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.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 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(template.getTitle()), eq(content)); + } + /** * 发送成功,当短信模板开启时 */ @@ -64,7 +151,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { String mail = randomEmail(); Long userId = randomLongId(); Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); + String templateCode = RandomUtils.randomString(); Map templateParams = MapUtil.builder().put("code", "1234") .put("op", "login").build(); // mock MailTemplateService 的方法 @@ -74,7 +161,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { o.setParams(Lists.newArrayList("code", "op")); }); when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); + String content = RandomUtils.randomString(); when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) .thenReturn(content); // mock MailAccountService 的方法 @@ -103,7 +190,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { String mail = randomEmail(); Long userId = randomLongId(); Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); + String templateCode = RandomUtils.randomString(); Map templateParams = MapUtil.builder().put("code", "1234") .put("op", "login").build(); // mock MailTemplateService 的方法 @@ -113,7 +200,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { o.setParams(Lists.newArrayList("code", "op")); }); when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); + String content = RandomUtils.randomString(); when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) .thenReturn(content); // mock MailAccountService 的方法 @@ -134,18 +221,18 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { } @Test - public void testCheckMailTemplateValid_notExists() { + public void testValidateMailTemplateValid_notExists() { // 准备参数 - String templateCode = randomString(); + String templateCode = RandomUtils.randomString(); // mock 方法 // 调用,并断言异常 - assertServiceException(() -> mailSendService.checkMailTemplateValid(templateCode), + assertServiceException(() -> mailSendService.validateMailTemplate(templateCode), MAIL_TEMPLATE_NOT_EXISTS); } @Test - public void testBuildTemplateParams_paramMiss() { + public void testValidateTemplateParams_paramMiss() { // 准备参数 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> o.setParams(Lists.newArrayList("code"))); @@ -153,18 +240,80 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { // mock 方法 // 调用,并断言异常 - assertServiceException(() -> mailSendService.checkTemplateParams(template, templateParams), + assertServiceException(() -> mailSendService.validateTemplateParams(template, templateParams), MAIL_SEND_TEMPLATE_PARAM_MISS, "code"); } @Test - public void testCheckMail_notExists() { + public void testValidateMail_notExists() { // 准备参数 // mock 方法 // 调用,并断言异常 - assertServiceException(() -> mailSendService.checkMail(null), + assertServiceException(() -> mailSendService.validateMail(null), MAIL_SEND_MAIL_NOT_EXISTS); } + @Test + public void testDoSendMail_success() { + try (MockedStatic 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 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)); + } + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java index a133fadab..017dbe0ae 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java @@ -14,6 +14,8 @@ import org.springframework.boot.test.mock.mockito.MockBean; 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; @@ -25,6 +27,7 @@ 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.*; +import static org.mockito.Mockito.verify; /** * {@link MailTemplateServiceImpl} 的单元测试类 @@ -70,6 +73,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); assertPojoEquals(reqVO, mailTemplate); + verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -87,6 +91,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, mailTemplate); + verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -108,8 +113,9 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 调用 mailTemplateService.deleteMailTemplate(id); - // 校验数据不存在了 - assertNull(mailTemplateMapper.selectById(id)); + // 校验数据不存在了 + assertNull(mailTemplateMapper.selectById(id)); + verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -158,4 +164,77 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { 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 list = mailTemplateService.getMailTemplateList(); + // 断言 + assertEquals(2, list.size()); + assertEquals(dbMailTemplate01, list.get(0)); + assertEquals(dbMailTemplate02, list.get(1)); + } + + @Test + public void testGetTemplate() { + // 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); + mailTemplateService.initLocalCache(); + // 准备参数 + String code = dbMailTemplate.getCode(); + + // 调用 + MailTemplateDO mailTemplate = mailTemplateService.getMailTemplateByCodeFromCache(code); + // 断言 + assertPojoEquals(dbMailTemplate, mailTemplate); + } + + @Test + public void testFormatMailTemplateContent() { + // 准备参数 + Map 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.countByAccountId(accountId); + // 断言 + assertEquals(1, count); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java new file mode 100644 index 000000000..0f7d0f9b7 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java @@ -0,0 +1,288 @@ +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.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; +import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.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.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 smsService; + + @Mock + private AdminUserService adminUserService; + @Mock + private MemberService memberService; + @Mock + private SmsChannelService smsChannelService; + @Mock + private SmsTemplateService smsTemplateService; + @Mock + private SmsLogService smsLogService; + @Mock + private SmsProducer smsProducer; + + @Mock + private SmsClientFactory smsClientFactory; + + @Test + public void testSendSingleSmsToAdmin() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = randomString(); + Map templateParams = MapUtil.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 = smsService.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 templateParams = MapUtil.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 = smsService.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 templateParams = MapUtil.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 = smsService.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 templateParams = MapUtil.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 = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); + // 断言 + assertEquals(smsLogId, resultSmsLogId); + // 断言调用 + verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(), + anyLong(), any(), anyList()); + } + + @Test + public void testCheckSmsTemplateValid_notExists() { + // 准备参数 + String templateCode = randomString(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsService.validateSmsTemplate(templateCode), + SMS_SEND_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testBuildTemplateParams_paramMiss() { + // 准备参数 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, + o -> o.setParams(Lists.newArrayList("code"))); + Map templateParams = new HashMap<>(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsService.buildTemplateParams(template, templateParams), + SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code"); + } + + @Test + public void testCheckMobile_notExists() { + // 准备参数 + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsService.validateMobile(null), + SMS_SEND_MOBILE_NOT_EXISTS); + } + + @Test + @SuppressWarnings("unchecked") + public void testDoSendSms() { + // 准备参数 + SmsSendMessage message = randomPojo(SmsSendMessage.class); + // mock SmsClientFactory 的方法 + SmsClient smsClient = spy(SmsClient.class); + when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); + // mock SmsClient 的方法 + SmsCommonResult sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class); + when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), + eq(message.getTemplateParams()))).thenReturn(sendResult); + + // 调用 + smsService.doSendSms(message); + // 断言 + verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), + eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()), + eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo())); + } + + @Test + public void testReceiveSmsStatus() throws Throwable { + // 准备参数 + String channelCode = randomString(); + String text = randomString(); + // mock SmsClientFactory 的方法 + SmsClient smsClient = spy(SmsClient.class); + when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient); + // mock SmsClient 的方法 + List receiveResults = randomPojoList(SmsReceiveRespDTO.class); + + // 调用 + smsService.receiveSmsStatus(channelCode, text); + // 断言 + receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()), + eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode()))); + } + +}