package com.iailab.module.system.service.sms; import cn.hutool.core.map.MapUtil; import com.iailab.framework.common.enums.CommonStatusEnum; import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.collection.ArrayUtils; import com.iailab.framework.common.util.object.ObjectUtils; import com.iailab.module.system.framework.sms.core.client.SmsClient; import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import com.iailab.framework.test.core.ut.BaseDbUnitTest; import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; import com.iailab.module.system.dal.mysql.sms.SmsTemplateMapper; import com.iailab.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 com.iailab.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; import static com.iailab.framework.common.util.date.LocalDateTimeUtils.buildTime; import static com.iailab.framework.test.core.util.AssertUtils.assertPojoEquals; import static com.iailab.framework.test.core.util.AssertUtils.assertServiceException; import static com.iailab.framework.test.core.util.RandomUtils.*; import static com.iailab.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 params = MapUtil.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 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("iailab"); 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 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... consumers) { Consumer consumer = (o) -> { o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 }; return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers)); } }