Commit 65d2dffe1a8c9eea59c7c4242c8693735c9fa621

Authored by YunaiV
1 parent b3ab1d92

完善 OAuth2ApproveServiceImplTest 单元测试

yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
@@ -2,13 +2,16 @@ package cn.iocoder.yudao.framework.test.core.util; @@ -2,13 +2,16 @@ package cn.iocoder.yudao.framework.test.core.util;
2 2
3 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
4 import cn.hutool.core.util.RandomUtil; 4 import cn.hutool.core.util.RandomUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 -import cn.iocoder.yudao.framework.common.util.collection.SetUtils;  
7 import uk.co.jemos.podam.api.PodamFactory; 7 import uk.co.jemos.podam.api.PodamFactory;
8 import uk.co.jemos.podam.api.PodamFactoryImpl; 8 import uk.co.jemos.podam.api.PodamFactoryImpl;
9 9
10 import java.lang.reflect.Type; 10 import java.lang.reflect.Type;
11 -import java.util.*; 11 +import java.util.Arrays;
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +import java.util.Set;
12 import java.util.function.Consumer; 15 import java.util.function.Consumer;
13 import java.util.stream.Collectors; 16 import java.util.stream.Collectors;
14 import java.util.stream.Stream; 17 import java.util.stream.Stream;
@@ -22,7 +25,6 @@ public class RandomUtils { @@ -22,7 +25,6 @@ public class RandomUtils {
22 25
23 private static final int RANDOM_STRING_LENGTH = 10; 26 private static final int RANDOM_STRING_LENGTH = 10;
24 27
25 - private static final Set<String> TINYINT_FIELDS = SetUtils.asSet("type", "category");  
26 private static final int TINYINT_MAX = 127; 28 private static final int TINYINT_MAX = 127;
27 29
28 private static final int RANDOM_DATE_MAX = 30; 30 private static final int RANDOM_DATE_MAX = 30;
@@ -41,9 +43,10 @@ public class RandomUtils { @@ -41,9 +43,10 @@ public class RandomUtils {
41 if (attributeMetadata.getAttributeName().equals("status")) { 43 if (attributeMetadata.getAttributeName().equals("status")) {
42 return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); 44 return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
43 } 45 }
44 - // 针对部分字段,使用 tinyint 范围  
45 - if (TINYINT_FIELDS.contains(attributeMetadata.getAttributeName())) {  
46 - return RandomUtil.randomInt(1, TINYINT_MAX + 1); 46 + // 如果是 type、status 结尾的字段,返回 tinyint 范围
  47 + if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(),
  48 + "type", "status", "category")) {
  49 + return RandomUtil.randomInt(0, TINYINT_MAX + 1);
47 } 50 }
48 return RandomUtil.randomInt(); 51 return RandomUtil.randomInt();
49 }); 52 });
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java
@@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; @@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils;
6 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
8 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; 8 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper;
  9 +import com.google.common.annotations.VisibleForTesting;
9 import org.springframework.stereotype.Service; 10 import org.springframework.stereotype.Service;
  11 +import org.springframework.transaction.annotation.Transactional;
10 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
11 13
12 import javax.annotation.Resource; 14 import javax.annotation.Resource;
@@ -35,6 +37,7 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { @@ -35,6 +37,7 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService {
35 private OAuth2ApproveMapper oauth2ApproveMapper; 37 private OAuth2ApproveMapper oauth2ApproveMapper;
36 38
37 @Override 39 @Override
  40 + @Transactional
38 public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection<String> requestedScopes) { 41 public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection<String> requestedScopes) {
39 // 第一步,基于 Client 的自动授权计算,如果 scopes 都在自动授权中,则返回 true 通过 42 // 第一步,基于 Client 的自动授权计算,如果 scopes 都在自动授权中,则返回 true 通过
40 OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); 43 OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
@@ -49,14 +52,14 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { @@ -49,14 +52,14 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService {
49 } 52 }
50 53
51 // 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true 54 // 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true
52 - List<OAuth2ApproveDO> approveDOs = oauth2ApproveMapper.selectListByUserIdAndUserTypeAndClientId(  
53 - userId, userType, clientId); 55 + List<OAuth2ApproveDO> approveDOs = getApproveList(userId, userType, clientId);
54 Set<String> scopes = convertSet(approveDOs, OAuth2ApproveDO::getScope, 56 Set<String> scopes = convertSet(approveDOs, OAuth2ApproveDO::getScope,
55 - o -> o.getApproved() && !DateUtils.isExpired(o.getExpiresTime())); // 只保留未过期的 57 + OAuth2ApproveDO::getApproved); // 只保留未过期的 + 同意的
56 return CollUtil.containsAll(scopes, requestedScopes); 58 return CollUtil.containsAll(scopes, requestedScopes);
57 } 59 }
58 60
59 @Override 61 @Override
  62 + @Transactional
60 public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map<String, Boolean> requestedScopes) { 63 public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map<String, Boolean> requestedScopes) {
61 // 如果 requestedScopes 为空,说明没有要求,则返回 true 通过 64 // 如果 requestedScopes 为空,说明没有要求,则返回 true 通过
62 if (CollUtil.isEmpty(requestedScopes)) { 65 if (CollUtil.isEmpty(requestedScopes)) {
@@ -83,8 +86,9 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { @@ -83,8 +86,9 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService {
83 return approveDOs; 86 return approveDOs;
84 } 87 }
85 88
86 - private void saveApprove(Long userId, Integer userType, String clientId,  
87 - String scope, Boolean approved, Date expireTime) { 89 + @VisibleForTesting
  90 + void saveApprove(Long userId, Integer userType, String clientId,
  91 + String scope, Boolean approved, Date expireTime) {
88 // 先更新 92 // 先更新
89 OAuth2ApproveDO approveDO = new OAuth2ApproveDO().setUserId(userId).setUserType(userType) 93 OAuth2ApproveDO approveDO = new OAuth2ApproveDO().setUserId(userId).setUserType(userType)
90 .setClientId(clientId).setScope(scope).setApproved(approved).setExpiresTime(expireTime); 94 .setClientId(clientId).setScope(scope).setApproved(approved).setExpiresTime(expireTime);
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.oauth2;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  5 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  6 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
  9 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper;
  10 +import org.assertj.core.util.Lists;
  11 +import org.junit.jupiter.api.Test;
  12 +import org.springframework.boot.test.mock.mockito.MockBean;
  13 +import org.springframework.context.annotation.Import;
  14 +
  15 +import javax.annotation.Resource;
  16 +import java.time.Duration;
  17 +import java.util.*;
  18 +
  19 +import static cn.hutool.core.util.RandomUtil.*;
  20 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
  21 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  22 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
  23 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  24 +import static org.junit.jupiter.api.Assertions.*;
  25 +import static org.mockito.ArgumentMatchers.eq;
  26 +import static org.mockito.Mockito.when;
  27 +
  28 +/**
  29 + * {@link OAuth2ApproveServiceImpl} 的单元测试类
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Import(OAuth2ApproveServiceImpl.class)
  34 +public class OAuth2ApproveServiceImplTest extends BaseDbUnitTest {
  35 +
  36 + @Resource
  37 + private OAuth2ApproveServiceImpl oauth2ApproveService;
  38 +
  39 + @Resource
  40 + private OAuth2ApproveMapper oauth2ApproveMapper;
  41 +
  42 + @MockBean
  43 + private OAuth2ClientService oauth2ClientService;
  44 +
  45 + @Test
  46 + public void checkForPreApproval_clientAutoApprove() {
  47 + // 准备参数
  48 + Long userId = randomLongId();
  49 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  50 + String clientId = randomString();
  51 + List<String> requestedScopes = Lists.newArrayList("read");
  52 + // mock 方法
  53 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
  54 + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(requestedScopes));
  55 +
  56 + // 调用
  57 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
  58 + clientId, requestedScopes);
  59 + // 断言
  60 + assertTrue(success);
  61 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  62 + assertEquals(1, result.size());
  63 + assertEquals(userId, result.get(0).getUserId());
  64 + assertEquals(userType, result.get(0).getUserType());
  65 + assertEquals(clientId, result.get(0).getClientId());
  66 + assertEquals("read", result.get(0).getScope());
  67 + assertTrue(result.get(0).getApproved());
  68 + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
  69 + }
  70 +
  71 + @Test
  72 + public void checkForPreApproval_approve() {
  73 + // 准备参数
  74 + Long userId = randomLongId();
  75 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  76 + String clientId = randomString();
  77 + List<String> requestedScopes = Lists.newArrayList("read");
  78 + // mock 方法
  79 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
  80 + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null));
  81 + // mock 数据
  82 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
  83 + .setUserType(userType).setClientId(clientId).setScope("read")
  84 + .setExpiresTime(addTime(Duration.ofDays(1))).setApproved(true); // 同意
  85 + oauth2ApproveMapper.insert(approve);
  86 +
  87 + // 调用
  88 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
  89 + clientId, requestedScopes);
  90 + // 断言
  91 + assertTrue(success);
  92 + }
  93 +
  94 + @Test
  95 + public void checkForPreApproval_reject() {
  96 + // 准备参数
  97 + Long userId = randomLongId();
  98 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  99 + String clientId = randomString();
  100 + List<String> requestedScopes = Lists.newArrayList("read");
  101 + // mock 方法
  102 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
  103 + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null));
  104 + // mock 数据
  105 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
  106 + .setUserType(userType).setClientId(clientId).setScope("read")
  107 + .setExpiresTime(addTime(Duration.ofDays(1))).setApproved(false); // 拒绝
  108 + oauth2ApproveMapper.insert(approve);
  109 +
  110 + // 调用
  111 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
  112 + clientId, requestedScopes);
  113 + // 断言
  114 + assertFalse(success);
  115 + }
  116 +
  117 + @Test
  118 + public void testUpdateAfterApproval_none() {
  119 + // 准备参数
  120 + Long userId = randomLongId();
  121 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  122 + String clientId = randomString();
  123 +
  124 + // 调用
  125 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
  126 + null);
  127 + // 断言
  128 + assertTrue(success);
  129 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  130 + assertEquals(0, result.size());
  131 + }
  132 +
  133 + @Test
  134 + public void testUpdateAfterApproval_approved() {
  135 + // 准备参数
  136 + Long userId = randomLongId();
  137 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  138 + String clientId = randomString();
  139 + Map<String, Boolean> requestedScopes = new LinkedHashMap<>(); // 有序,方便判断
  140 + requestedScopes.put("read", true);
  141 + requestedScopes.put("write", false);
  142 + // mock 方法
  143 +
  144 + // 调用
  145 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
  146 + requestedScopes);
  147 + // 断言
  148 + assertTrue(success);
  149 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  150 + assertEquals(2, result.size());
  151 + // read
  152 + assertEquals(userId, result.get(0).getUserId());
  153 + assertEquals(userType, result.get(0).getUserType());
  154 + assertEquals(clientId, result.get(0).getClientId());
  155 + assertEquals("read", result.get(0).getScope());
  156 + assertTrue(result.get(0).getApproved());
  157 + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
  158 + // write
  159 + assertEquals(userId, result.get(1).getUserId());
  160 + assertEquals(userType, result.get(1).getUserType());
  161 + assertEquals(clientId, result.get(1).getClientId());
  162 + assertEquals("write", result.get(1).getScope());
  163 + assertFalse(result.get(1).getApproved());
  164 + assertFalse(DateUtils.isExpired(result.get(1).getExpiresTime()));
  165 + }
  166 +
  167 + @Test
  168 + public void testUpdateAfterApproval_reject() {
  169 + // 准备参数
  170 + Long userId = randomLongId();
  171 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  172 + String clientId = randomString();
  173 + Map<String, Boolean> requestedScopes = new LinkedHashMap<>();
  174 + requestedScopes.put("write", false);
  175 + // mock 方法
  176 +
  177 + // 调用
  178 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
  179 + requestedScopes);
  180 + // 断言
  181 + assertFalse(success);
  182 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  183 + assertEquals(1, result.size());
  184 + // write
  185 + assertEquals(userId, result.get(0).getUserId());
  186 + assertEquals(userType, result.get(0).getUserType());
  187 + assertEquals(clientId, result.get(0).getClientId());
  188 + assertEquals("write", result.get(0).getScope());
  189 + assertFalse(result.get(0).getApproved());
  190 + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
  191 + }
  192 +
  193 + @Test
  194 + public void testGetApproveList() {
  195 + // 准备参数
  196 + Long userId = 10L;
  197 + Integer userType = UserTypeEnum.ADMIN.getValue();
  198 + String clientId = randomString();
  199 + // mock 数据
  200 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
  201 + .setUserType(userType).setClientId(clientId).setExpiresTime(addTime(Duration.ofDays(1L)));
  202 + oauth2ApproveMapper.insert(approve); // 未过期
  203 + oauth2ApproveMapper.insert(ObjectUtil.clone(approve).setId(null)
  204 + .setExpiresTime(addTime(Duration.ofDays(-1L)))); // 已过期
  205 +
  206 + // 调用
  207 + List<OAuth2ApproveDO> result = oauth2ApproveService.getApproveList(userId, userType, clientId);
  208 + // 断言
  209 + assertEquals(1, result.size());
  210 + assertPojoEquals(approve, result.get(0));
  211 + }
  212 +
  213 + @Test
  214 + public void testSaveApprove_insert() {
  215 + // 准备参数
  216 + Long userId = randomLongId();
  217 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  218 + String clientId = randomString();
  219 + String scope = randomString();
  220 + Boolean approved = randomBoolean();
  221 + Date expireTime = randomDay(1, 30);
  222 + // mock 方法
  223 +
  224 + // 调用
  225 + oauth2ApproveService.saveApprove(userId, userType, clientId,
  226 + scope, approved, expireTime);
  227 + // 断言
  228 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  229 + assertEquals(1, result.size());
  230 + assertEquals(userId, result.get(0).getUserId());
  231 + assertEquals(userType, result.get(0).getUserType());
  232 + assertEquals(clientId, result.get(0).getClientId());
  233 + assertEquals(scope, result.get(0).getScope());
  234 + assertEquals(approved, result.get(0).getApproved());
  235 + assertEquals(expireTime, result.get(0).getExpiresTime());
  236 + }
  237 +
  238 + @Test
  239 + public void testSaveApprove_update() {
  240 + // mock 数据
  241 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class);
  242 + oauth2ApproveMapper.insert(approve);
  243 + // 准备参数
  244 + Long userId = approve.getUserId();
  245 + Integer userType = approve.getUserType();
  246 + String clientId = approve.getClientId();
  247 + String scope = approve.getScope();
  248 + Boolean approved = randomBoolean();
  249 + Date expireTime = randomDay(1, 30);
  250 + // mock 方法
  251 +
  252 + // 调用
  253 + oauth2ApproveService.saveApprove(userId, userType, clientId,
  254 + scope, approved, expireTime);
  255 + // 断言
  256 + List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
  257 + assertEquals(1, result.size());
  258 + assertEquals(approve.getId(), result.get(0).getId());
  259 + assertEquals(userId, result.get(0).getUserId());
  260 + assertEquals(userType, result.get(0).getUserType());
  261 + assertEquals(clientId, result.get(0).getClientId());
  262 + assertEquals(scope, result.get(0).getScope());
  263 + assertEquals(approved, result.get(0).getApproved());
  264 + assertEquals(expireTime, result.get(0).getExpiresTime());
  265 + }
  266 +
  267 +}
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
@@ -21,3 +21,4 @@ DELETE FROM &quot;system_tenant&quot;; @@ -21,3 +21,4 @@ DELETE FROM &quot;system_tenant&quot;;
21 DELETE FROM "system_tenant_package"; 21 DELETE FROM "system_tenant_package";
22 DELETE FROM "system_sensitive_word"; 22 DELETE FROM "system_sensitive_word";
23 DELETE FROM "system_oauth2_client"; 23 DELETE FROM "system_oauth2_client";
  24 +DELETE FROM "system_oauth2_approve";
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
@@ -495,3 +495,19 @@ CREATE TABLE IF NOT EXISTS &quot;system_oauth2_client&quot; ( @@ -495,3 +495,19 @@ CREATE TABLE IF NOT EXISTS &quot;system_oauth2_client&quot; (
495 "deleted" bit NOT NULL DEFAULT FALSE, 495 "deleted" bit NOT NULL DEFAULT FALSE,
496 PRIMARY KEY ("id") 496 PRIMARY KEY ("id")
497 ) COMMENT 'OAuth2 客户端表'; 497 ) COMMENT 'OAuth2 客户端表';
  498 +
  499 +CREATE TABLE IF NOT EXISTS "system_oauth2_approve" (
  500 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  501 + "user_id" bigint NOT NULL,
  502 + "user_type" tinyint NOT NULL,
  503 + "client_id" varchar NOT NULL,
  504 + "scope" varchar NOT NULL,
  505 + "approved" bit NOT NULL DEFAULT FALSE,
  506 + "expires_time" datetime NOT NULL,
  507 + "creator" varchar DEFAULT '',
  508 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  509 + "updater" varchar DEFAULT '',
  510 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  511 + "deleted" bit NOT NULL DEFAULT FALSE,
  512 + PRIMARY KEY ("id")
  513 +) COMMENT 'OAuth2 批准表';