diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java index 910315751..3297db414 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java @@ -23,7 +23,7 @@ public class SocialUserPageReqVO extends PageParam { @Schema(description = "用户昵称", example = "李四") private String nickname; - @Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w\n") + @Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w") private String openid; @Schema(description = "创建时间") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java index e13f08529..23b3cea5c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java @@ -142,7 +142,8 @@ public class SocialClientServiceImpl implements SocialClientService { * @param userType 用户类型 * @return AuthRequest 对象 */ - private AuthRequest buildAuthRequest(Integer socialType, Integer userType) { + @VisibleForTesting + AuthRequest buildAuthRequest(Integer socialType, Integer userType) { // 1. 先查找默认的配置项,从 application-*.yaml 中读取 AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource()); Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType)); @@ -180,7 +181,8 @@ public class SocialClientServiceImpl implements SocialClientService { * @param userType 用户类型 * @return WxMpService 对象 */ - private WxMpService getWxMpService(Integer userType) { + @VisibleForTesting + WxMpService getWxMpService(Integer userType) { // 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象 SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( SocialTypeEnum.WECHAT_MP.getType(), userType); @@ -198,7 +200,7 @@ public class SocialClientServiceImpl implements SocialClientService { * @param clientSecret 微信公众号 secret * @return WxMpService 对象 */ - private WxMpService buildWxMpService(String clientId, String clientSecret) { + public WxMpService buildWxMpService(String clientId, String clientSecret) { // 第一步,创建 WxMpRedisConfigImpl 对象 WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( new RedisTemplateWxRedisOps(stringRedisTemplate), @@ -231,7 +233,8 @@ public class SocialClientServiceImpl implements SocialClientService { * @param userType 用户类型 * @return WxMpService 对象 */ - private WxMaService getWxMaService(Integer userType) { + @VisibleForTesting + WxMaService getWxMaService(Integer userType) { // 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象 SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( SocialTypeEnum.WECHAT_MINI_APP.getType(), userType); @@ -311,7 +314,6 @@ public class SocialClientServiceImpl implements SocialClientService { * @param userType 用户类型 * @param socialType 社交类型 */ - @VisibleForTesting private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) { SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( socialType, userType); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java index 8485158d1..3d41b24d0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java @@ -59,7 +59,7 @@ public class SocialUserServiceImpl implements SocialUserService { } @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public String bindSocialUser(SocialUserBindReqDTO reqDTO) { // 获得社交用户 SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(), @@ -108,7 +108,6 @@ public class SocialUserServiceImpl implements SocialUserService { return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId()); } - // TODO 芋艿:调整下单测 /** * 授权获得对应的社交用户 * 如果授权失败,则会抛出 {@link ServiceException} 异常 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java index 601255b8b..7e094d896 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java @@ -1,33 +1,53 @@ 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 org.junit.jupiter.api.Disabled; +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.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_CLIENT_NOT_EXISTS; +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.*; -// TODO 芋艿:单测后续补充下; /** * {@link SocialClientServiceImpl} 的单元测试类 * * @author 芋道源码 */ @Import(SocialClientServiceImpl.class) -@Disabled public class SocialClientServiceImplTest extends BaseDbUnitTest { @Resource @@ -36,10 +56,305 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest { @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 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) + SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, + o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) + .setUserType(randomEle(UserTypeEnum.values()).getValue()) + .setStatus(randomCommonStatus())) .setId(null); // 防止 id 被赋值 // 调用 @@ -59,6 +374,9 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest { // 准备参数 SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> { o.setId(dbSocialClient.getId()); // 设置更新的 ID + o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) + .setUserType(randomEle(UserTypeEnum.values()).getValue()) + .setStatus(randomCommonStatus()); }); // 调用 @@ -101,40 +419,47 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest { } @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + 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(null); - o.setSocialType(null); - o.setUserType(null); - o.setClientId(null); - o.setClientSecret(null); - o.setStatus(null); - o.setCreateTime(null); + 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(null))); + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString()))); // 测试 socialType 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(null))); + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType()))); // 测试 userType 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(null))); + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); // 测试 clientId 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId(null))); - // 测试 clientSecret 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientSecret(null))); + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao"))); // 测试 status 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setCreateTime(null))); + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 准备参数 SocialClientPageReqVO reqVO = new SocialClientPageReqVO(); - reqVO.setName(null); - reqVO.setSocialType(null); - reqVO.setUserType(null); - reqVO.setClientId(null); - reqVO.setStatus(null); + reqVO.setName("芋"); + reqVO.setSocialType(SocialTypeEnum.GITEE.getType()); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setClientId("yu"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 调用 PageResult pageResult = socialClientService.getSocialClientPage(reqVO); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java index 8a4f3df5e..224186b0f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java @@ -1,21 +1,17 @@ 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.enums.AuthResponseStatus; -import com.xingyuv.jushauth.model.AuthCallback; -import com.xingyuv.jushauth.model.AuthResponse; import com.xingyuv.jushauth.model.AuthUser; -import com.xingyuv.jushauth.request.AuthRequest; -import com.xingyuv.justauth.AuthRequestFactory; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -23,18 +19,27 @@ import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; -import static cn.hutool.core.util.RandomUtil.*; +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.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE; +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.*; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; +/** + * {@link SocialUserServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(SocialUserServiceImpl.class) -@Disabled // TODO 芋艿:后续统一修复 public class SocialUserServiceImplTest extends BaseDbUnitTest { @Resource @@ -46,119 +51,7 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest { private SocialUserBindMapper socialUserBindMapper; @MockBean - private AuthRequestFactory authRequestFactory; - - // TODO 芋艿:后续统一修复 -// @Test -// public void testGetAuthorizeUrl() { -// try (MockedStatic authStateUtilsMock = mockStatic(AuthStateUtils.class)) { -// // 准备参数 -// Integer type = SocialTypeEnum.WECHAT_MP.getType(); -// 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 = socialUserService.getAuthorizeUrl(type, redirectUri); -// // 断言 -// assertEquals("https://www.iocoder.cn?redirect_uri=sss", url); -// } -// } - - @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_authFailure() { - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - // mock 方法 - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(anyString())).thenReturn(authRequest); - AuthResponse authResponse = new AuthResponse<>(0, "模拟失败", null); - when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse); - - // 调用并断言 - assertServiceException( - () -> socialUserService.authSocialUser(socialType, userType, randomString(10), randomString(10)), - SOCIAL_USER_AUTH_FAILURE, "模拟失败"); - } - - @Test - public void testAuthSocialUser_insert() { - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - String code = "tudou"; - String state = "yuanma"; - // mock 方法 - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest); - AuthUser authUser = randomPojo(AuthUser.class); - AuthResponse authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser); - when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse); - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertBindSocialUser(socialType, result, authResponse.getData()); - 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 方法 - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest); - AuthUser authUser = randomPojo(AuthUser.class); - authUser.getToken().setOpenId("test_openid"); - AuthResponse authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser); - when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse); - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertBindSocialUser(socialType, result, authResponse.getData()); - 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()); - } + private SocialClientService socialClientService; @Test public void testGetSocialUserList() { @@ -260,4 +153,136 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest { 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 pageResult = socialUserService.getSocialUserPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSocialUser, pageResult.getList().get(0)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql index ffe0f2609..78d4e83dd 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql @@ -361,13 +361,14 @@ CREATE TABLE IF NOT EXISTS "system_social_client" ( "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, + "tenant_id" bigint not null default '0', PRIMARY KEY ("id") ) COMMENT '社交客户端表';