Commit 495f121463febd9a0c7ce280444757277d090f0b

Authored by YunaiV
1 parent f87c4f75

完善 OAuth2OpenControllerTest 单元测试

yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
@@ -45,7 +45,7 @@ public class RandomUtils { @@ -45,7 +45,7 @@ public class RandomUtils {
45 } 45 }
46 // 如果是 type、status 结尾的字段,返回 tinyint 范围 46 // 如果是 type、status 结尾的字段,返回 tinyint 范围
47 if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), 47 if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(),
48 - "type", "status", "category")) { 48 + "type", "status", "category", "scope")) {
49 return RandomUtil.randomInt(0, TINYINT_MAX + 1); 49 return RandomUtil.randomInt(0, TINYINT_MAX + 1);
50 } 50 }
51 return RandomUtil.randomInt(); 51 return RandomUtil.randomInt();
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java
@@ -52,8 +52,6 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti @@ -52,8 +52,6 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
52 * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 52 * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。
53 * scope 的使用示例,可见 {@link OAuth2UserController} 类 53 * scope 的使用示例,可见 {@link OAuth2UserController} 类
54 * 54 *
55 - *  
56 - *  
57 * @author 芋道源码 55 * @author 芋道源码
58 */ 56 */
59 @Api(tags = "管理后台 - OAuth2.0 授权") 57 @Api(tags = "管理后台 - OAuth2.0 授权")
@@ -185,8 +183,7 @@ public class OAuth2OpenController { @@ -185,8 +183,7 @@ public class OAuth2OpenController {
185 // 0. 校验用户已经登录。通过 Spring Security 实现 183 // 0. 校验用户已经登录。通过 Spring Security 实现
186 184
187 // 1. 获得 Client 客户端的信息 185 // 1. 获得 Client 客户端的信息
188 - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,  
189 - null, null, null); 186 + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId);
190 // 2. 获得用户已经授权的信息 187 // 2. 获得用户已经授权的信息
191 List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); 188 List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId);
192 // 拼接返回 189 // 拼接返回
@@ -223,7 +220,6 @@ public class OAuth2OpenController { @@ -223,7 +220,6 @@ public class OAuth2OpenController {
223 @SuppressWarnings("unchecked") 220 @SuppressWarnings("unchecked")
224 Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class); 221 Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class);
225 scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); 222 scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap());
226 - // TODO 芋艿:针对 approved + scopes 在看看 spring security 的实现  
227 // 0. 校验用户已经登录。通过 Spring Security 实现 223 // 0. 校验用户已经登录。通过 Spring Security 实现
228 224
229 // 1.1 校验 responseType 是否满足 code 或者 token 值 225 // 1.1 校验 responseType 是否满足 code 或者 token 值
@@ -262,7 +258,7 @@ public class OAuth2OpenController { @@ -262,7 +258,7 @@ public class OAuth2OpenController {
262 if (StrUtil.equalsAny(responseType, "token")) { 258 if (StrUtil.equalsAny(responseType, "token")) {
263 return OAuth2GrantTypeEnum.IMPLICIT; 259 return OAuth2GrantTypeEnum.IMPLICIT;
264 } 260 }
265 - throw exception0(BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); 261 + throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token");
266 } 262 }
267 263
268 private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, 264 private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client,
@@ -279,7 +275,7 @@ public class OAuth2OpenController { @@ -279,7 +275,7 @@ public class OAuth2OpenController {
279 private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, 275 private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client,
280 List<String> scopes, String redirectUri, String state) { 276 List<String> scopes, String redirectUri, String state) {
281 // 1. 创建 code 授权码 277 // 1. 创建 code 授权码
282 - String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId,getUserType(), client.getClientId(), scopes, 278 + String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes,
283 redirectUri, state); 279 redirectUri, state);
284 // 2. 拼接重定向的 URL 280 // 2. 拼接重定向的 URL
285 return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); 281 return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java
@@ -7,7 +7,7 @@ import lombok.AllArgsConstructor; @@ -7,7 +7,7 @@ import lombok.AllArgsConstructor;
7 import lombok.Data; 7 import lombok.Data;
8 import lombok.NoArgsConstructor; 8 import lombok.NoArgsConstructor;
9 9
10 -import java.util.Set; 10 +import java.util.List;
11 11
12 @ApiModel("管理后台 - 【开放接口】校验令牌 Response VO") 12 @ApiModel("管理后台 - 【开放接口】校验令牌 Response VO")
13 @Data 13 @Data
@@ -28,13 +28,13 @@ public class OAuth2OpenCheckTokenRespVO { @@ -28,13 +28,13 @@ public class OAuth2OpenCheckTokenRespVO {
28 @ApiModelProperty(value = "客户端编号", required = true, example = "car") 28 @ApiModelProperty(value = "客户端编号", required = true, example = "car")
29 private String clientId; 29 private String clientId;
30 @ApiModelProperty(value = "授权范围", required = true, example = "user_info") 30 @ApiModelProperty(value = "授权范围", required = true, example = "user_info")
31 - private Set<String> scopes; 31 + private List<String> scopes;
32 32
33 @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") 33 @ApiModelProperty(value = "访问令牌", required = true, example = "tudou")
34 @JsonProperty("access_token") 34 @JsonProperty("access_token")
35 private String accessToken; 35 private String accessToken;
36 36
37 @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒") 37 @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒")
38 - @JsonProperty("exp")  
39 private Long exp; 38 private Long exp;
  39 +
40 } 40 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java
@@ -61,11 +61,13 @@ public class OAuth2Utils { @@ -61,11 +61,13 @@ public class OAuth2Utils {
61 if (CollUtil.isNotEmpty(scopes)) { 61 if (CollUtil.isNotEmpty(scopes)) {
62 vars.put("scope", buildScopeStr(scopes)); 62 vars.put("scope", buildScopeStr(scopes));
63 } 63 }
64 - for (String key : additionalInformation.keySet()) {  
65 - Object value = additionalInformation.get(key);  
66 - if (value != null) {  
67 - keys.put("extra_" + key, key);  
68 - vars.put("extra_" + key, value); 64 + if (CollUtil.isNotEmpty(additionalInformation)) {
  65 + for (String key : additionalInformation.keySet()) {
  66 + Object value = additionalInformation.get(key);
  67 + if (value != null) {
  68 + keys.put("extra_" + key, key);
  69 + vars.put("extra_" + key, value);
  70 + }
69 } 71 }
70 } 72 }
71 // Do not include the refresh token (even if there is one) 73 // Do not include the refresh token (even if there is one)
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.oauth2;
  2 +
  3 +import cn.hutool.core.collection.ListUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  6 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  7 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  8 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  10 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  11 +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO;
  12 +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO;
  13 +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO;
  14 +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
  15 +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
  16 +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
  17 +import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum;
  18 +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService;
  19 +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService;
  20 +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService;
  21 +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
  22 +import org.assertj.core.util.Lists;
  23 +import org.junit.jupiter.api.Test;
  24 +import org.mockito.InjectMocks;
  25 +import org.mockito.Mock;
  26 +
  27 +import javax.servlet.http.HttpServletRequest;
  28 +import java.time.Duration;
  29 +import java.util.ArrayList;
  30 +import java.util.Date;
  31 +import java.util.LinkedHashMap;
  32 +import java.util.List;
  33 +
  34 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  35 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
  36 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  37 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  38 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  39 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
  40 +import static java.util.Arrays.asList;
  41 +import static org.junit.jupiter.api.Assertions.*;
  42 +import static org.mockito.ArgumentMatchers.eq;
  43 +import static org.mockito.ArgumentMatchers.isNull;
  44 +import static org.mockito.Mockito.mock;
  45 +import static org.mockito.Mockito.when;
  46 +
  47 +/**
  48 + * {@link OAuth2OpenController} 的单元测试
  49 + *
  50 + * @author 芋道源码
  51 + */
  52 +public class OAuth2OpenControllerTest extends BaseMockitoUnitTest {
  53 +
  54 + @InjectMocks
  55 + private OAuth2OpenController oauth2OpenController;
  56 +
  57 + @Mock
  58 + private OAuth2GrantService oauth2GrantService;
  59 + @Mock
  60 + private OAuth2ClientService oauth2ClientService;
  61 + @Mock
  62 + private OAuth2ApproveService oauth2ApproveService;
  63 + @Mock
  64 + private OAuth2TokenService oauth2TokenService;
  65 +
  66 + @Test
  67 + public void testPostAccessToken_authorizationCode() {
  68 + // 准备参数
  69 + String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType();
  70 + String code = randomString();
  71 + String redirectUri = randomString();
  72 + String state = randomString();
  73 + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
  74 + // mock 方法(client)
  75 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
  76 + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client);
  77 +
  78 + // mock 方法(访问令牌)
  79 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
  80 + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完
  81 + when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"),
  82 + eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO);
  83 +
  84 + // 调用
  85 + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
  86 + code, redirectUri, state, null, null, null, null);
  87 + // 断言
  88 + assertEquals(0, result.getCode());
  89 + assertPojoEquals(accessTokenDO, result.getData());
  90 + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒
  91 + }
  92 +
  93 + @Test
  94 + public void testPostAccessToken_password() {
  95 + // 准备参数
  96 + String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType();
  97 + String username = randomString();
  98 + String password = randomString();
  99 + String scope = "write read";
  100 + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
  101 + // mock 方法(client)
  102 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
  103 + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"),
  104 + eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client);
  105 +
  106 + // mock 方法(访问令牌)
  107 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
  108 + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完
  109 + when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"),
  110 + eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO);
  111 +
  112 + // 调用
  113 + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
  114 + null, null, null, username, password, scope, null);
  115 + // 断言
  116 + assertEquals(0, result.getCode());
  117 + assertPojoEquals(accessTokenDO, result.getData());
  118 + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒
  119 + }
  120 +
  121 + @Test
  122 + public void testPostAccessToken_refreshToken() {
  123 + // 准备参数
  124 + String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType();
  125 + String refreshToken = randomString();
  126 + String password = randomString();
  127 + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
  128 + // mock 方法(client)
  129 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
  130 + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"),
  131 + eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client);
  132 +
  133 + // mock 方法(访问令牌)
  134 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
  135 + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完
  136 + when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO);
  137 +
  138 + // 调用
  139 + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
  140 + null, null, null, null, password, null, refreshToken);
  141 + // 断言
  142 + assertEquals(0, result.getCode());
  143 + assertPojoEquals(accessTokenDO, result.getData());
  144 + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒
  145 + }
  146 +
  147 + @Test
  148 + public void testPostAccessToken_implicit() {
  149 + // 调用,并断言
  150 + assertServiceException(() -> oauth2OpenController.postAccessToken(null,
  151 + OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null,
  152 + null, null, null, null),
  153 + new ErrorCode(400, "Token 接口不支持 implicit 授权模式"));
  154 + }
  155 +
  156 + @Test
  157 + public void testRevokeToken() {
  158 + // 准备参数
  159 + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret");
  160 + String token = randomString();
  161 + // mock 方法(client)
  162 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id");
  163 + when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"),
  164 + eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client);
  165 + // mock 方法(移除)
  166 + when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true);
  167 +
  168 + // 调用
  169 + CommonResult<Boolean> result = oauth2OpenController.revokeToken(request, token);
  170 + // 断言
  171 + assertEquals(0, result.getCode());
  172 + assertTrue(result.getData());
  173 + }
  174 +
  175 + @Test
  176 + public void testCheckToken() {
  177 + // 准备参数
  178 + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret");
  179 + String token = randomString();
  180 + // mock 方法
  181 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(new Date(1653485731195L));
  182 + when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO);
  183 +
  184 + // 调用
  185 + CommonResult<OAuth2OpenCheckTokenRespVO> result = oauth2OpenController.checkToken(request, token);
  186 + // 断言
  187 + assertEquals(0, result.getCode());
  188 + assertPojoEquals(accessTokenDO, result.getData());
  189 + assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒
  190 + }
  191 +
  192 + @Test
  193 + public void testAuthorize() {
  194 + // 准备参数
  195 + String clientId = randomString();
  196 + // mock 方法(client)
  197 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all"));
  198 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client);
  199 + // mock 方法(approve)
  200 + List<OAuth2ApproveDO> approves = asList(
  201 + randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true),
  202 + randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false));
  203 + when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves);
  204 +
  205 + // 调用
  206 + CommonResult<OAuth2OpenAuthorizeInfoRespVO> result = oauth2OpenController.authorize(clientId);
  207 + // 断言
  208 + assertEquals(0, result.getCode());
  209 + assertPojoEquals(client, result.getData().getClient());
  210 + assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0));
  211 + assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1));
  212 + assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2));
  213 + }
  214 +
  215 + @Test
  216 + public void testApproveOrDeny_grantTypeError() {
  217 + // 调用,并断言
  218 + assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null,
  219 + null, null, null, null),
  220 + new ErrorCode(400, "response_type 参数值只允许 code 和 token"));
  221 + }
  222 +
  223 + @Test // autoApprove = true,但是不通过
  224 + public void testApproveOrDeny_autoApproveNo() {
  225 + // 准备参数
  226 + String responseType = "code";
  227 + String clientId = randomString();
  228 + String scope = "{\"read\": true, \"write\": false}";
  229 + String redirectUri = randomString();
  230 + String state = randomString();
  231 + // mock 方法
  232 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class);
  233 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"),
  234 + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
  235 +
  236 + // 调用
  237 + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
  238 + scope, redirectUri, true, state);
  239 + // 断言
  240 + assertEquals(0, result.getCode());
  241 + assertNull(result.getData());
  242 + }
  243 +
  244 + @Test // autoApprove = false,但是不通过
  245 + public void testApproveOrDeny_ApproveNo() {
  246 + // 准备参数
  247 + String responseType = "token";
  248 + String clientId = randomString();
  249 + String scope = "{\"read\": true, \"write\": false}";
  250 + String redirectUri = "https://www.iocoder.cn";
  251 + String state = "test";
  252 + // mock 方法
  253 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class);
  254 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"),
  255 + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
  256 +
  257 + // 调用
  258 + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
  259 + scope, redirectUri, false, state);
  260 + // 断言
  261 + assertEquals(0, result.getCode());
  262 + assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData());
  263 + }
  264 +
  265 + @Test // autoApprove = true,通过 + token
  266 + public void testApproveOrDeny_autoApproveWithToken() {
  267 + // 准备参数
  268 + String responseType = "token";
  269 + String clientId = randomString();
  270 + String scope = "{\"read\": true, \"write\": false}";
  271 + String redirectUri = "https://www.iocoder.cn";
  272 + String state = "test";
  273 + // mock 方法(client)
  274 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null);
  275 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"),
  276 + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
  277 + // mock 方法(场景一)
  278 + when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
  279 + eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true);
  280 + // mock 方法(访问令牌)
  281 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
  282 + .setAccessToken("test_access_token").setExpiresTime(addTime(Duration.ofMillis(30010L)));
  283 + when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
  284 + eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO);
  285 +
  286 + // 调用
  287 + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
  288 + scope, redirectUri, true, state);
  289 + // 断言
  290 + assertEquals(0, result.getCode());
  291 + assertEquals("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read", result.getData());
  292 + }
  293 +
  294 + @Test // autoApprove = false,通过 + code
  295 + public void testApproveOrDeny_approveWithCode() {
  296 + // 准备参数
  297 + String responseType = "code";
  298 + String clientId = randomString();
  299 + String scope = "{\"read\": true, \"write\": false}";
  300 + String redirectUri = "https://www.iocoder.cn";
  301 + String state = "test";
  302 + // mock 方法(client)
  303 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null);
  304 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"),
  305 + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
  306 + // mock 方法(场景二)
  307 + when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId),
  308 + eq(MapUtil.builder(new LinkedHashMap<String, Boolean>()).put("read", true).put("write", false).build())))
  309 + .thenReturn(true);
  310 + // mock 方法(访问令牌)
  311 + String authorizationCode = "test_code";
  312 + when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
  313 + eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode);
  314 +
  315 + // 调用
  316 + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
  317 + scope, redirectUri, false, state);
  318 + // 断言
  319 + assertEquals(0, result.getCode());
  320 + assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData());
  321 + }
  322 +
  323 + private HttpServletRequest mockRequest(String clientId, String secret) {
  324 + HttpServletRequest request = mock(HttpServletRequest.class);
  325 + when(request.getParameter(eq("client_id"))).thenReturn(clientId);
  326 + when(request.getParameter(eq("client_secret"))).thenReturn(secret);
  327 + return request;
  328 + }
  329 +
  330 +}
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java
@@ -3,15 +3,17 @@ package cn.iocoder.yudao.module.system.service.permission; @@ -3,15 +3,17 @@ package cn.iocoder.yudao.module.system.service.permission;
3 import cn.hutool.core.util.RandomUtil; 3 import cn.hutool.core.util.RandomUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; 6 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  7 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
  9 +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; 13 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper;
  14 +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
12 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; 15 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
13 import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; 16 import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
14 -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;  
15 import org.junit.jupiter.api.Test; 17 import org.junit.jupiter.api.Test;
16 import org.springframework.boot.test.mock.mockito.MockBean; 18 import org.springframework.boot.test.mock.mockito.MockBean;
17 import org.springframework.context.annotation.Import; 19 import org.springframework.context.annotation.Import;
@@ -20,6 +22,7 @@ import javax.annotation.Resource; @@ -20,6 +22,7 @@ import javax.annotation.Resource;
20 import java.util.*; 22 import java.util.*;
21 import java.util.stream.Collectors; 23 import java.util.stream.Collectors;
22 24
  25 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
23 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max; 26 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
24 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
25 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -155,61 +158,66 @@ public class RoleServiceTest extends BaseDbUnitTest { @@ -155,61 +158,66 @@ public class RoleServiceTest extends BaseDbUnitTest {
155 } 158 }
156 159
157 @Test 160 @Test
158 - public void testGetRoles_success() {  
159 - Map<Long, RoleDO> idRoleMap = new HashMap<>();  
160 - // 验证查询状态为1的角色  
161 - RoleDO roleDO1 = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);  
162 - roleMapper.insert(roleDO1);  
163 - idRoleMap.put(roleDO1.getId(), roleDO1);  
164 -  
165 - RoleDO roleDO2 = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);  
166 - roleMapper.insert(roleDO2);  
167 - idRoleMap.put(roleDO2.getId(), roleDO2);  
168 -  
169 - // 以下是排除的角色  
170 - RoleDO roleDO3 = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2);  
171 - roleMapper.insert(roleDO3);  
172 -  
173 - //调用  
174 - List<RoleDO> roles = roleService.getRoles(Arrays.asList(1));  
175 -  
176 - //断言  
177 - assertEquals(2, roles.size());  
178 - roles.stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); 161 + public void testGetRoles() {
  162 + // mock 数据
  163 + RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到
  164 + o.setName("土豆");
  165 + o.setCode("tudou");
  166 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  167 + o.setCreateTime(DateUtils.buildTime(2022, 2, 8));
  168 + });
  169 + roleMapper.insert(dbRole);
  170 + // 测试 name 不匹配
  171 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯")));
  172 + // 测试 code 不匹配
  173 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong")));
  174 + // 测试 createTime 不匹配
  175 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16))));
  176 + // 准备参数
  177 + RoleExportReqVO reqVO = new RoleExportReqVO();
  178 + reqVO.setName("土豆");
  179 + reqVO.setCode("tu");
  180 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  181 + reqVO.setBeginTime(DateUtils.buildTime(2022, 2, 1));
  182 + reqVO.setEndTime(DateUtils.buildTime(2022, 2, 12));
179 183
  184 + // 调用
  185 + List<RoleDO> list = roleService.getRoleList(reqVO);
  186 + // 断言
  187 + assertEquals(1, list.size());
  188 + assertPojoEquals(dbRole, list.get(0));
180 } 189 }
181 190
182 @Test 191 @Test
183 - public void testGetRolePage_success() {  
184 - Map<Long, RoleDO> idRoleMap = new HashMap<>();  
185 - // 验证名称包含"role", 状态为1,code为"code"的角色  
186 - // 第一页  
187 - RoleDO roleDO = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");  
188 - roleMapper.insert(roleDO);  
189 - idRoleMap.put(roleDO.getId(), roleDO);  
190 - // 第二页  
191 - roleDO = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");  
192 - roleMapper.insert(roleDO);  
193 -  
194 - // 以下是排除的角色  
195 - roleDO = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2, "code");  
196 - roleMapper.insert(roleDO);  
197 - roleDO = createRoleDO("role4", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "xxxxx");  
198 - roleMapper.insert(roleDO);  
199 -  
200 - //调用  
201 - RolePageReqVO reqVO = randomPojo(RolePageReqVO.class, o -> {  
202 - o.setName("role");  
203 - o.setCode("code");  
204 - o.setStatus(1);  
205 - o.setPageNo(1);  
206 - o.setPageSize(1);  
207 - o.setBeginTime(null);  
208 - o.setEndTime(null); 192 + public void testGetRolePage() {
  193 + // mock 数据
  194 + RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到
  195 + o.setName("土豆");
  196 + o.setCode("tudou");
  197 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  198 + o.setCreateTime(DateUtils.buildTime(2022, 2, 8));
209 }); 199 });
210 - PageResult<RoleDO> result = roleService.getRolePage(reqVO);  
211 - assertEquals(2, result.getTotal());  
212 - result.getList().stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); 200 + roleMapper.insert(dbRole);
  201 + // 测试 name 不匹配
  202 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯")));
  203 + // 测试 code 不匹配
  204 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong")));
  205 + // 测试 createTime 不匹配
  206 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16))));
  207 + // 准备参数
  208 + RolePageReqVO reqVO = new RolePageReqVO();
  209 + reqVO.setName("土豆");
  210 + reqVO.setCode("tu");
  211 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  212 + reqVO.setBeginTime(DateUtils.buildTime(2022, 2, 1));
  213 + reqVO.setEndTime(DateUtils.buildTime(2022, 2, 12));
  214 +
  215 + // 调用
  216 + PageResult<RoleDO> pageResult = roleService.getRolePage(reqVO);
  217 + // 断言
  218 + assertEquals(1, pageResult.getTotal());
  219 + assertEquals(1, pageResult.getList().size());
  220 + assertPojoEquals(dbRole, pageResult.getList().get(0));
213 } 221 }
214 222
215 @Test 223 @Test