feat(缺陷管理): 缺陷关联用例功能

This commit is contained in:
song-cc-rock 2023-11-15 15:07:59 +08:00 committed by 刘瑞斌
parent 04b8228808
commit 4eac5632f7
25 changed files with 570 additions and 39 deletions

View File

@ -0,0 +1,52 @@
package io.metersphere.sdk.constants;
import io.metersphere.sdk.util.Translator;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
@Getter
public enum CaseType {
/**
* 功能用例
*/
FUNCTIONAL_CASE("FUNCTIONAL", "test_case"),
/**
* 接口用例
*/
API_CASE("API", "api_case"),
/**
* 性能用例
*/
SCENARIO_CASE("SCENARIO", "scenario_case"),
/**
* UI用例
*/
UI_CASE("UI", "ui_case"),
/**
* 性能用例
*/
PERFORMANCE_CASE("PERFORMANCE", "performance_case");
private final String key;
private final String value;
CaseType(String key, String value) {
this.key = key;
this.value = value;
}
public String getValue() {
return Translator.get(value);
}
public static String getValue(String key) {
for (CaseType caseType : CaseType.values()) {
if (StringUtils.equals(caseType.getKey(), key)) {
return caseType.getValue();
}
}
return null;
}
}

View File

@ -95,3 +95,6 @@ bug_comment.parent_id.not_blank=缺陷评论父级ID不能为空
bug_comment.parent.not_exist=父级评论不存在
bug_comment.reply_user.not_blank=缺陷回复人不能为空
bug_comment_not_exist=缺陷评论不存在
bug_relate_case_not_found=未查询到关联的用例
bug_relate_case_type_unknown=关联的用例类型未知, 无法查看
bug_relate_case_permission_error=无权限查看, 请联系管理员

View File

@ -95,3 +95,6 @@ bug_comment.parent_id.not_blank=Bug comment parent-id cannot be empty
bug_comment.parent.not_exist=Bug comment parent does not exist
bug_comment.reply_user.not_blank=Bug comment reply-user cannot be empty
bug_comment_not_exist=Bug comment does not exist
bug_relate_case_not_found=Bug related case not found
bug_relate_case_type_unknown=Bug related case type unknown
bug_relate_case_permission_error=No permission to show the case

View File

@ -95,3 +95,6 @@ bug_comment.parent_id.not_blank=缺陷评论父级ID不能为空
bug_comment.parent.not_exist=父级评论不存在
bug_comment.reply_user.not_blank=缺陷回复人不能为空
bug_comment_not_exist=缺陷评论不存在
bug_relate_case_not_found=未查询到关联的用例
bug_relate_case_type_unknown=关联的用例类型未知, 无法查看
bug_relate_case_permission_error=无用例查看权限, 请联系管理员

View File

@ -95,4 +95,6 @@ bug_comment.parent_id.not_blank=缺陷評論父級ID不能為空
bug_comment.parent.not_exist=父級評論不存在
bug_comment.reply_user.not_blank=缺陷回復人不能為空
bug_comment_not_exist=缺陷評論不存在
bug_relate_case_not_found=未查詢到關聯的用例
bug_relate_case_type_unknown=關聯的用例類型未知, 無法查看
bug_relate_case_permission_error=無權限查看, 請聯繫管理員

View File

@ -314,6 +314,7 @@ connection_expired=The connection has expired, please get it again
api_case=Api
performance_case=Performance
scenario_case=Scenario
ui_case=UI
scenario_name_is_null=Scenario name cannot be empty
create_user=Create user
test_case_status=Case status

View File

@ -310,6 +310,7 @@ connection_expired=连接已失效,请重新获取
api_case=接口用例
performance_case=性能用例
scenario_case=场景用例
ui_case=UI用例
scenario_name_is_null=场景名称不能为空
test_case_status_error=失败
test_case_status_success=成功

View File

@ -309,6 +309,7 @@ connection_expired=連接已失效,請重新獲取
api_case=接口用例
performance_case=性能用例
scenario_case=場景用例
ui_case=UI用例
scenario_name_is_null=場景名稱不能為空
test_case_status_error=失敗
test_case_status_success=成功

View File

@ -0,0 +1,49 @@
package io.metersphere.bug.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.bug.dto.BugRelateCaseDTO;
import io.metersphere.bug.dto.request.BugRelatedCasePageRequest;
import io.metersphere.bug.service.BugRelateCaseService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "缺陷管理-关联用例")
@RestController
@RequestMapping("/bug/relate/case")
public class BugRelateCaseController {
@Resource
private BugRelateCaseService bugRelateCaseService;
@PostMapping("/page")
@Operation(description = "缺陷管理-关联用例-列表分页查询")
@RequiresPermissions(PermissionConstants.BUG_UPDATE)
public Pager<List<BugRelateCaseDTO>> page(@Validated @RequestBody BugRelatedCasePageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize());
return PageUtils.setPageInfo(page, bugRelateCaseService.page(request));
}
@GetMapping("/un-relate/{id}")
@Operation(description = "缺陷管理-关联用例-取消关联用例")
@RequiresPermissions(PermissionConstants.BUG_UPDATE)
public void unRelate(@PathVariable String id) {
bugRelateCaseService.unRelate(id);
}
@GetMapping("/check-permission/{projectId}/{caseType}")
@Operation(description = "缺陷管理-关联用例-查看用例权限校验")
public void checkPermission(@PathVariable String projectId, @PathVariable String caseType) {
bugRelateCaseService.checkPermission(projectId, SessionUtils.getUserId(), caseType);
}
}

View File

@ -7,7 +7,7 @@ import lombok.Data;
* @author song-cc-rock
*/
@Data
public class BugRelationCaseCountDTO {
public class BugRelateCaseCountDTO {
@Schema(description = "缺陷ID")
private String bugId;

View File

@ -0,0 +1,32 @@
package io.metersphere.bug.dto;
import io.metersphere.functional.domain.FunctionalCase;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugRelateCaseDTO extends FunctionalCase {
@Schema(description = "关联ID")
private String relateId;
@Schema(description = "关联类型")
private String relateCaseType;
@Schema(description = "是否关联计划用例")
private Boolean relatePlanCase;
@Schema(description = "是否关联用例")
private Boolean relateCase;
@Schema(description = "关联用例类型名称")
private String relateCaseTypeName;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "版本名称")
private String versionName;
}

View File

@ -0,0 +1,20 @@
package io.metersphere.bug.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugRelateCasePageRequest extends BasePageRequest {
@Schema(description = "所属项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String projectId;
@Schema(description = "版本ID")
private String versionId;
@Schema(description = "选中模块ID")
private String selectModuleId;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.bug.dto.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugRelateCaseRequest extends BugRelateCasePageRequest{
@Schema(description = "是否全选", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean selectAll;
@Schema(description = "用例ID勾选列表")
private List<String> includeCaseIds;
}

View File

@ -0,0 +1,14 @@
package io.metersphere.bug.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugRelatedCasePageRequest extends BasePageRequest {
@Schema(description = "缺陷ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String bugId;
}

View File

@ -0,0 +1,28 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.dto.BugRelateCaseCountDTO;
import io.metersphere.bug.dto.BugRelateCaseDTO;
import io.metersphere.bug.dto.request.BugRelatedCasePageRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author song-cc-rock
*/
public interface ExtBugRelateCaseMapper {
/**
* 统计缺陷关联的用例数量
* @param bugIds 缺陷ID集合
* @return 缺陷关联DTO
*/
List<BugRelateCaseCountDTO> countRelationCases(@Param("ids") List<String> bugIds);
/**
* 缺陷关联用例列表查询
* @param request 请求参数
* @return 缺陷关联用例列表
*/
List<BugRelateCaseDTO> list(@Param("request") BugRelatedCasePageRequest request);
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.bug.mapper.ExtBugRelateCaseMapper">
<select id="countRelationCases" resultType="io.metersphere.bug.dto.BugRelateCaseCountDTO">
select count(id) as relationCaseCount, bug_id as bugId from bug_relation_case brc
where brc.bug_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
group by brc.bug_id
</select>
<select id="list" resultType="io.metersphere.bug.dto.BugRelateCaseDTO">
select fc.num, fc.name, fc.project_id, fc.version_id, brc.case_type relateCaseType, brc.id relateId,
brc.test_plan_id is not null relatePlanCase, brc.case_id is not null relateCase
from bug_relation_case brc join functional_case fc on (brc.case_id = fc.id or brc.test_plan_case_id = fc.id)
where brc.bug_id = #{request.bugId}
<if test="request.keyword != null and request.keyword != ''">
and fc.name like concat('%', #{request.keyword}, '%')
</if>
</select>
</mapper>

View File

@ -1,19 +0,0 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.dto.BugRelationCaseCountDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author song-cc-rock
*/
public interface ExtBugRelationCaseMapper {
/**
* 统计缺陷关联的用例数量
* @param bugIds 缺陷ID集合
* @return 缺陷关联DTO
*/
List<BugRelationCaseCountDTO> countRelationCases(@Param("ids") List<String> bugIds);
}

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.bug.mapper.ExtBugRelationCaseMapper">
<select id="countRelationCases" resultType="io.metersphere.bug.dto.BugRelationCaseCountDTO">
select count(id) as relationCaseCount, bug_id as bugId from bug_relation_case brc
where brc.bug_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
group by brc.bug_id
</select>
</mapper>

View File

@ -0,0 +1,136 @@
package io.metersphere.bug.service;
import io.metersphere.bug.domain.BugRelationCase;
import io.metersphere.bug.dto.BugRelateCaseDTO;
import io.metersphere.bug.dto.request.BugRelatedCasePageRequest;
import io.metersphere.bug.mapper.BugRelationCaseMapper;
import io.metersphere.bug.mapper.ExtBugRelateCaseMapper;
import io.metersphere.project.domain.Project;
import io.metersphere.project.domain.ProjectExample;
import io.metersphere.project.domain.ProjectVersion;
import io.metersphere.project.domain.ProjectVersionExample;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.mapper.ProjectVersionMapper;
import io.metersphere.project.service.PermissionCheckService;
import io.metersphere.sdk.constants.CaseType;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class BugRelateCaseService {
@Resource
private ProjectMapper projectMapper;
@Resource
private ProjectVersionMapper projectVersionMapper;
@Resource
private BugRelationCaseMapper bugRelationCaseMapper;
@Resource
private ExtBugRelateCaseMapper extBugRelateCaseMapper;
@Resource
private PermissionCheckService permissionCheckService;
/**
* 分页查询关联用例列表
* @param request 请求参数
*/
public List<BugRelateCaseDTO> page(BugRelatedCasePageRequest request) {
List<BugRelateCaseDTO> relateCases = extBugRelateCaseMapper.list(request);
if (CollectionUtils.isEmpty(relateCases)) {
return new ArrayList<>();
}
Map<String, String> projectMap = getProjectMap(relateCases.stream().map(BugRelateCaseDTO::getProjectId).toList());
Map<String, String> versionMap = getVersionMap(relateCases.stream().map(BugRelateCaseDTO::getVersionId).toList());
relateCases.forEach(relateCase -> {
relateCase.setProjectName(projectMap.get(relateCase.getProjectId()));
relateCase.setVersionName(versionMap.get(relateCase.getVersionId()));
relateCase.setRelateCaseTypeName(CaseType.getValue(relateCase.getRelateCaseType()));
});
return relateCases;
}
/**
* 取消关联用例
* @param id 关联ID
*/
public void unRelate(String id) {
// 只用取消关联直接关联的用例, 保留测试计划关联的用例
BugRelationCase bugRelationCase = checkRelate(id);
if (StringUtils.isEmpty(bugRelationCase.getTestPlanId())) {
// 不包含测试计划关联的用例, 直接删除
bugRelationCaseMapper.deleteByPrimaryKey(id);
} else {
// 包含测试计划关联的用例, 只需将直接关联CaseId置空即可
bugRelationCase.setCaseId(null);
bugRelationCaseMapper.updateByPrimaryKey(bugRelationCase);
}
}
/**
* 校验当前用户是否有关联用例的权限
* @param projectId 项目ID
* @param currentUser 当前用户
* @param caseType 用例类型
*/
public void checkPermission(String projectId, String currentUser, String caseType) {
// 校验关联用例的查看权限, 目前只支持功能用例的查看权限, 后续支持除功能用例外的其他类型用例
if (!CaseType.FUNCTIONAL_CASE.getKey().equals(caseType)) {
// 关联的用例类型未知
throw new MSException(Translator.get("bug_relate_case_type_unknown"));
}
boolean hasPermission = permissionCheckService.userHasProjectPermission(currentUser, projectId, PermissionConstants.FUNCTIONAL_CASE_READ);
if (!hasPermission) {
// 没有该用例的访问权限
throw new MSException(Translator.get("bug_relate_case_permission_error"));
}
}
/**
* 校验关联用例
* @param relateId 关联ID
* @return 关联用例
*/
private BugRelationCase checkRelate(String relateId) {
BugRelationCase bugRelationCase = bugRelationCaseMapper.selectByPrimaryKey(relateId);
if (bugRelationCase == null) {
throw new MSException(Translator.get("bug_relate_case_not_found"));
}
return bugRelationCase;
}
/**
* 获取项目Map
* @param projectIds 项目ID集合
* @return 获取项目Map
*/
private Map<String, String> getProjectMap(List<String> projectIds) {
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andIdIn(projectIds);
List<Project> projects = projectMapper.selectByExample(projectExample);
return projects.stream().collect(Collectors.toMap(Project::getId, Project::getName));
}
/**
* 获取版本Map
* @param versionIds 版本ID集合
* @return 获取版本Map
*/
private Map<String, String> getVersionMap(List<String> versionIds) {
ProjectVersionExample projectVersionExample = new ProjectVersionExample();
projectVersionExample.createCriteria().andIdIn(versionIds);
List<ProjectVersion> projectVersions = projectVersionMapper.selectByExample(projectVersionExample);
return projectVersions.stream().collect(Collectors.toMap(ProjectVersion::getId, ProjectVersion::getName));
}
}

View File

@ -3,7 +3,7 @@ package io.metersphere.bug.service;
import io.metersphere.bug.domain.*;
import io.metersphere.bug.dto.BugCustomFieldDTO;
import io.metersphere.bug.dto.BugDTO;
import io.metersphere.bug.dto.BugRelationCaseCountDTO;
import io.metersphere.bug.dto.BugRelateCaseCountDTO;
import io.metersphere.bug.dto.BugTagEditDTO;
import io.metersphere.bug.dto.request.BugBatchRequest;
import io.metersphere.bug.dto.request.BugBatchUpdateRequest;
@ -76,7 +76,7 @@ public class BugService {
@Resource
private ExtBugCustomFieldMapper extBugCustomFieldMapper;
@Resource
private ExtBugRelationCaseMapper extBugRelationCaseMapper;
private ExtBugRelateCaseMapper extBugRelateCaseMapper;
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
@ -561,8 +561,8 @@ public class BugService {
Map<String, String> userMap = userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
// 根据缺陷ID获取关联用例数
List<String> ids = bugs.stream().map(BugDTO::getId).toList();
List<BugRelationCaseCountDTO> relationCaseCount = extBugRelationCaseMapper.countRelationCases(ids);
Map<String, Integer> countMap = relationCaseCount.stream().collect(Collectors.toMap(BugRelationCaseCountDTO::getBugId, BugRelationCaseCountDTO::getRelationCaseCount));
List<BugRelateCaseCountDTO> relationCaseCount = extBugRelateCaseMapper.countRelationCases(ids);
Map<String, Integer> countMap = relationCaseCount.stream().collect(Collectors.toMap(BugRelateCaseCountDTO::getBugId, BugRelateCaseCountDTO::getRelationCaseCount));
bugs.forEach(bug -> {
bug.setRelationCaseCount(countMap.get(bug.getId()));
bug.setCreateUserName(userMap.get(bug.getCreateUser()));

View File

@ -0,0 +1,124 @@
package io.metersphere.bug.controller;
import io.metersphere.bug.dto.BugRelateCaseDTO;
import io.metersphere.bug.dto.request.BugRelatedCasePageRequest;
import io.metersphere.sdk.constants.UserRoleType;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.utils.Pager;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BugRelateCaseControllerTests extends BaseTest {
public static final String BUG_CASE_RELATE_PAGE = "/bug/relate/case/page";
public static final String BUG_CASE_UN_RELATE = "/bug/relate/case/un-relate";
public static final String BUG_CASE_RELATE_CHECK = "/bug/relate/case/check-permission";
@Test
@Order(0)
@Sql(scripts = {"/dml/init_bug_case.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
void testBugRelatePageSuccess() throws Exception {
BugRelatedCasePageRequest request = new BugRelatedCasePageRequest();
request.setCurrent(1);
request.setPageSize(10);
request.setBugId("default-relate-bug-id");
request.setKeyword("first");
MvcResult mvcResult = this.requestPostWithOkAndReturn(BUG_CASE_RELATE_PAGE, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
// 返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize());
// 返回值中取出第一条数据, 并判断是否包含关键字default
BugRelateCaseDTO bugRelateCaseDTO = JSON.parseArray(JSON.toJSONString(pageData.getList()), BugRelateCaseDTO.class).get(0);
Assertions.assertTrue(StringUtils.contains(bugRelateCaseDTO.getName(), request.getKeyword()));
}
@Test
@Order(1)
void testBugRelatePageEmpty() throws Exception {
BugRelatedCasePageRequest request = new BugRelatedCasePageRequest();
request.setCurrent(1);
request.setPageSize(10);
request.setBugId("default-relate-bug-id");
request.setKeyword("keyword");
MvcResult mvcResult = this.requestPostWithOkAndReturn(BUG_CASE_RELATE_PAGE, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
// 返回的数据量为0条
Assertions.assertEquals(0, pageData.getTotal());
}
@Test
@Order(2)
void testBugRelatePageError() throws Exception {
// 页码有误
BugRelatedCasePageRequest request = new BugRelatedCasePageRequest();
request.setCurrent(0);
request.setPageSize(10);
this.requestPost(BUG_CASE_RELATE_PAGE, request, status().isBadRequest());
// 页数有误
request = new BugRelatedCasePageRequest();
request.setCurrent(1);
request.setPageSize(1);
this.requestPost(BUG_CASE_RELATE_PAGE, request, status().isBadRequest());
}
@Test
@Order(3)
void testBugUnRelateSuccess() throws Exception {
this.requestGetWithOk(BUG_CASE_UN_RELATE + "/bug-relate-case-default-id");
this.requestGetWithOk(BUG_CASE_UN_RELATE + "/bug-relate-case-default-id-1");
}
@Test
@Order(4)
void testBugUnRelateError() throws Exception {
this.requestGet(BUG_CASE_UN_RELATE + "/bug-relate-case-default-id-x", status().is5xxServerError());
}
@Test
@Order(5)
void testBugRelateCheckPermissionError() throws Exception {
// 非功能用例类型参数
this.requestGet(BUG_CASE_RELATE_CHECK + "/100001100001/API", status().is5xxServerError());
}
@Test
@Order(6)
void testBugRelateCheckPermissionSuccess() throws Exception {
// 默认项目ID且登录用户为admin
this.requestGet(BUG_CASE_RELATE_CHECK + "/100001100001/FUNCTIONAL", status().isOk()).andReturn();
// 切换登录用户为PROJECT, 权限校验失败
this.requestGetWithNoAdmin(BUG_CASE_RELATE_CHECK + "/default-project-for-bug/FUNCTIONAL", UserRoleType.PROJECT.name(), status().is5xxServerError()).andReturn();
}
}

View File

@ -26,7 +26,10 @@ spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
#
# 单元测试初始化权限 sql
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath*:dml/init_permission_test.sql
# spring.kafka
spring.kafka.bootstrap-servers=${embedded.kafka.brokerList}
spring.kafka.consumer.group-id=metersphere_group_id

View File

@ -0,0 +1,24 @@
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos,version_id, ref_id, last_execute_result,
deleted, public_case, latest, create_user,update_user, delete_user, create_time, update_time, delete_time)
VALUES ('bug_relate_case-tmp', 100100, 'init_module', 'default-project-for-bug', 'default_template', 'first_case', 'UN_REVIEWED', null, 'STEP',
10001, '111', '111', 'UN_EXECUTED', 0, 0, true, 'admin', 'admin', null, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null);
INSERT INTO project_version(id, project_id, name, description, status, latest, publish_time, start_time, end_time, create_time, create_user) VALUE
('default_bug_version', 'default-project-for-bug', 'v1.2.8', 'This is a test version!', 'open', true, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos,version_id, ref_id, last_execute_result,
deleted, public_case, latest, create_user,update_user, delete_user, create_time, update_time, delete_time) VALUES
('bug_relate_case', 100099, 'init_module', 'default-project-for-bug', 'default_template', 'first_case1', 'UN_REVIEWED', null, 'STEP',
10001, '111', '111', 'UN_EXECUTED', 0, 0, true, 'admin', 'admin', null, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null),
('bug_relate_case-1', 100099, 'init_module', '100001100001', 'default_template', 'first_case2', 'UN_REVIEWED', null, 'STEP',
10001, '111', '111', 'UN_EXECUTED', 0, 0, true, 'admin', 'admin', null, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tag, platform_bug_id, deleted) VALUES
('default-relate-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', 'default-tag', null, 0),
('default-relate-bug-id-1', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', 'default-tag', null, 0);
INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time)
VALUES ('bug-relate-case-default-id', 'bug_relate_case', 'default-relate-bug-id', 'FUNCTIONAL', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('bug-relate-case-default-id-1', 'bug_relate_case', 'default-relate-bug-id-1', 'FUNCTIONAL', 'test-plan-id', 'bug_relate_case', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('bug-relate-case-default-id-2', 'bug_relate_case-1', 'default-relate-bug-id', 'FUNCTIONAL', 'test-plan-id', 'bug_relate_case-1', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);

View File

@ -0,0 +1,14 @@
-- 初始化用于权限测试的组织用户
INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source,
last_project_id, create_user, update_user, deleted)
VALUES ('PROJECT', 'PROJECT', 'PROJECT@fit2cloud.com', MD5('metersphere'),
UNIX_TIMESTAMP() * 1000,
UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false);
-- 初始化一个用于权限测试的用户组,这里默认使用 PROJECT 作为ID如果是组织和项目级别类似便于根据权限的前缀找到对应测试的用户组
INSERT INTO user_role (id, name, description, internal, type, create_time, update_time, create_user, scope_id)
VALUES ('PROJECT', '项目级别权限校验', '', 1, 'PROJECT', 1620674220005, 1620674220000, 'admin', 'global');
-- 初始化用户和组的关系
INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user)
SELECT 'PROJECT', 'PROJECT', 'PROJECT', id, organization_id, 1684747668375, 'admin' FROM project WHERE num = 100001;

View File

@ -8,7 +8,6 @@ import io.metersphere.sdk.exception.IResultCode;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.OperationLogMapper;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.base.param.InvalidateParamInfo;
import io.metersphere.system.base.param.ParamGeneratorFactory;
import io.metersphere.system.domain.User;
@ -18,6 +17,7 @@ import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.mapper.UserRolePermissionMapper;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.Pager;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import jakarta.annotation.Resource;
@ -133,6 +133,13 @@ public abstract class BaseTest {
return setRequestBuilderHeader(requestBuilder, adminAuthInfo);
}
protected MockHttpServletRequestBuilder getRequestBuilderByRole(String url, String userRoleType, Object... uriVariables) {
// 使用对应的用户认证信息来请求, 非Admin
AuthInfo authInfo = permissionAuthInfoMap.get(userRoleType);
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(getBasePath() + url, uriVariables);
return setRequestBuilderHeader(requestBuilder, authInfo == null ? adminAuthInfo : authInfo);
}
protected ResultActions requestPost(String url, Object param, Object... uriVariables) throws Exception {
return mockMvc.perform(getPostRequestBuilder(url, param, uriVariables))
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
@ -505,6 +512,11 @@ public abstract class BaseTest {
requestPermissionsTest(permissionIds, url, () -> getPermissionMultipartRequestBuilder(permissionIds.get(0).split("_")[0], url, paramMap, uriVariables));
}
protected ResultActions requestGetWithNoAdmin(String url, String userRoleType, Object... uriVariables) throws Exception {
return mockMvc.perform(getRequestBuilderByRole(url, userRoleType, uriVariables))
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
/**
* 给用户组绑定对应权限
*