From 20190426494e47ebe4f792949d8cc41955245f9a Mon Sep 17 00:00:00 2001 From: song-cc-rock Date: Tue, 4 Jun 2024 10:37:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92):=20?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=B5=8B=E8=AF=95=E8=A7=84=E5=88=92=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan/domain/TestPlanAllocation.java | 262 +++++++++--------- .../migration/3.0.0/ddl/V3.0.0_13__ga_ddl.sql | 3 + .../src/main/resources/i18n/plan.properties | 4 +- .../main/resources/i18n/plan_en_US.properties | 3 +- .../main/resources/i18n/plan_zh_CN.properties | 3 +- .../main/resources/i18n/plan_zh_TW.properties | 3 +- .../provider/AssociateFunctionalProvider.java | 124 +++++---- .../plan/dto/TestPlanAllocationTypeDTO.java | 24 ++ .../plan/dto/TestPlanCollectionInitDTO.java | 29 ++ .../TestPlanAllocationCreateRequest.java | 27 ++ .../dto/request/TestPlanCreateRequest.java | 159 +++++------ .../dto/request/TestPlanUpdateRequest.java | 114 ++++---- .../metersphere/plan/enums/ExecuteMethod.java | 14 + .../plan/service/TestPlanService.java | 87 ++++++ .../plan/controller/TestPlanTests.java | 115 +++++++- 15 files changed, 636 insertions(+), 335 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanAllocationTypeDTO.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanCollectionInitDTO.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanAllocationCreateRequest.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/enums/ExecuteMethod.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanAllocation.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanAllocation.java index 498ade0422..04ce25272a 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanAllocation.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanAllocation.java @@ -1,132 +1,132 @@ -package io.metersphere.plan.domain; - -import io.metersphere.validation.groups.Created; -import io.metersphere.validation.groups.Updated; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; - -@Data -public class TestPlanAllocation implements Serializable { - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_allocation.id.not_blank}", groups = {Updated.class}) - @Size(min = 1, max = 50, message = "{test_plan_allocation.id.length_range}", groups = {Created.class, Updated.class}) - private String id; - - @Schema(description = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_allocation.test_plan_id.not_blank}", groups = {Created.class}) - @Size(min = 1, max = 50, message = "{test_plan_allocation.test_plan_id.length_range}", groups = {Created.class, Updated.class}) - private String testPlanId; - - @Schema(description = "资源池ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_allocation.test_resource_pool_id.not_blank}", groups = {Created.class}) - @Size(min = 1, max = 50, message = "{test_plan_allocation.test_resource_pool_id.length_range}", groups = {Created.class, Updated.class}) - private String testResourcePoolId; - - @Schema(description = "是否失败重试", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "{test_plan_allocation.retry_on_fail.not_blank}", groups = {Created.class}) - private Boolean retryOnFail; - - @Schema(description = "失败重试类型(步骤/场景)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_allocation.retry_type.not_blank}", groups = {Created.class}) - @Size(min = 1, max = 50, message = "{test_plan_allocation.retry_type.length_range}", groups = {Created.class, Updated.class}) - private String retryType; - - @Schema(description = "失败重试次数", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "{test_plan_allocation.retry_times.not_blank}", groups = {Created.class}) - private Integer retryTimes; - - @Schema(description = "失败重试间隔(单位: ms)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "{test_plan_allocation.retry_interval.not_blank}", groups = {Created.class}) - private Integer retryInterval; - - @Schema(description = "是否失败停止", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "{test_plan_allocation.stop_on_fail.not_blank}", groups = {Created.class}) - private Boolean stopOnFail; - - private static final long serialVersionUID = 1L; - - public enum Column { - id("id", "id", "VARCHAR", false), - testPlanId("test_plan_id", "testPlanId", "VARCHAR", false), - testResourcePoolId("test_resource_pool_id", "testResourcePoolId", "VARCHAR", false), - retryOnFail("retry_on_fail", "retryOnFail", "BIT", false), - retryType("retry_type", "retryType", "VARCHAR", false), - retryTimes("retry_times", "retryTimes", "INTEGER", false), - retryInterval("retry_interval", "retryInterval", "INTEGER", false), - stopOnFail("stop_on_fail", "stopOnFail", "BIT", false); - - private static final String BEGINNING_DELIMITER = "`"; - - private static final String ENDING_DELIMITER = "`"; - - private final String column; - - private final boolean isColumnNameDelimited; - - private final String javaProperty; - - private final String jdbcType; - - public String value() { - return this.column; - } - - public String getValue() { - return this.column; - } - - public String getJavaProperty() { - return this.javaProperty; - } - - public String getJdbcType() { - return this.jdbcType; - } - - Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) { - this.column = column; - this.javaProperty = javaProperty; - this.jdbcType = jdbcType; - this.isColumnNameDelimited = isColumnNameDelimited; - } - - public String desc() { - return this.getEscapedColumnName() + " DESC"; - } - - public String asc() { - return this.getEscapedColumnName() + " ASC"; - } - - public static Column[] excludes(Column ... excludes) { - ArrayList columns = new ArrayList<>(Arrays.asList(Column.values())); - if (excludes != null && excludes.length > 0) { - columns.removeAll(new ArrayList<>(Arrays.asList(excludes))); - } - return columns.toArray(new Column[]{}); - } - - public static Column[] all() { - return Column.values(); - } - - public String getEscapedColumnName() { - if (this.isColumnNameDelimited) { - return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString(); - } else { - return this.column; - } - } - - public String getAliasedEscapedColumnName() { - return this.getEscapedColumnName(); - } - } +package io.metersphere.plan.domain; + +import io.metersphere.validation.groups.Created; +import io.metersphere.validation.groups.Updated; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; + +@Data +public class TestPlanAllocation implements Serializable { + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan_allocation.id.not_blank}", groups = {Updated.class}) + @Size(min = 1, max = 50, message = "{test_plan_allocation.id.length_range}", groups = {Created.class, Updated.class}) + private String id; + + @Schema(description = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan_allocation.id.not_blank}", groups = {Updated.class}) + @Size(min = 1, max = 50, message = "{test_plan_allocation.test_plan_id.length_range}", groups = {Created.class, Updated.class}) + private String testPlanId; + + @Schema(description = "资源池ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan_allocation.test_resource_pool_id.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 50, message = "{test_plan_allocation.test_resource_pool_id.length_range}", groups = {Created.class, Updated.class}) + private String testResourcePoolId; + + @Schema(description = "是否失败重试", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{test_plan_allocation.retry_on_fail.not_blank}", groups = {Created.class}) + private Boolean retryOnFail; + + @Schema(description = "失败重试类型(步骤/场景)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan_allocation.retry_type.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 50, message = "{test_plan_allocation.retry_type.length_range}", groups = {Created.class, Updated.class}) + private String retryType; + + @Schema(description = "失败重试次数", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{test_plan_allocation.retry_times.not_blank}", groups = {Created.class}) + private Integer retryTimes; + + @Schema(description = "失败重试间隔(单位: ms)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{test_plan_allocation.retry_interval.not_blank}", groups = {Created.class}) + private Integer retryInterval; + + @Schema(description = "是否失败停止", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{test_plan_allocation.stop_on_fail.not_blank}", groups = {Created.class}) + private Boolean stopOnFail; + + private static final long serialVersionUID = 1L; + + public enum Column { + id("id", "id", "VARCHAR", false), + testPlanId("test_plan_id", "testPlanId", "VARCHAR", false), + testResourcePoolId("test_resource_pool_id", "testResourcePoolId", "VARCHAR", false), + retryOnFail("retry_on_fail", "retryOnFail", "BIT", false), + retryType("retry_type", "retryType", "VARCHAR", false), + retryTimes("retry_times", "retryTimes", "INTEGER", false), + retryInterval("retry_interval", "retryInterval", "INTEGER", false), + stopOnFail("stop_on_fail", "stopOnFail", "BIT", false); + + private static final String BEGINNING_DELIMITER = "`"; + + private static final String ENDING_DELIMITER = "`"; + + private final String column; + + private final boolean isColumnNameDelimited; + + private final String javaProperty; + + private final String jdbcType; + + public String value() { + return this.column; + } + + public String getValue() { + return this.column; + } + + public String getJavaProperty() { + return this.javaProperty; + } + + public String getJdbcType() { + return this.jdbcType; + } + + Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) { + this.column = column; + this.javaProperty = javaProperty; + this.jdbcType = jdbcType; + this.isColumnNameDelimited = isColumnNameDelimited; + } + + public String desc() { + return this.getEscapedColumnName() + " DESC"; + } + + public String asc() { + return this.getEscapedColumnName() + " ASC"; + } + + public static Column[] excludes(Column ... excludes) { + ArrayList columns = new ArrayList<>(Arrays.asList(Column.values())); + if (excludes != null && excludes.length > 0) { + columns.removeAll(new ArrayList<>(Arrays.asList(excludes))); + } + return columns.toArray(new Column[]{}); + } + + public static Column[] all() { + return Column.values(); + } + + public String getEscapedColumnName() { + if (this.isColumnNameDelimited) { + return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString(); + } else { + return this.column; + } + } + + public String getAliasedEscapedColumnName() { + return this.getEscapedColumnName(); + } + } } \ No newline at end of file diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_13__ga_ddl.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_13__ga_ddl.sql index cff9921b98..86a2ca332a 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_13__ga_ddl.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_13__ga_ddl.sql @@ -19,13 +19,16 @@ alter table test_plan ALTER TABLE test_plan_config DROP `test_planning`; -- 修改计划报告详情表字段 +UPDATE test_plan_report_summary SET bug_count = 0 WHERE bug_count IS NULL; ALTER TABLE test_plan_report_summary MODIFY `bug_count` BIGINT NOT NULL DEFAULT 0 COMMENT '缺陷数量'; ALTER TABLE test_plan_report_summary DROP `report_count`; -- 修改计划报告功能用例表字段 +UPDATE test_plan_report_function_case SET function_case_bug_count = 0 WHERE function_case_bug_count IS NULL; ALTER TABLE test_plan_report_function_case MODIFY `function_case_bug_count` BIGINT NOT NULL DEFAULT 0 COMMENT '功能用例关联缺陷数'; -- 修改计划报告缺陷表字段 +UPDATE test_plan_report_bug SET bug_case_count = 0 WHERE bug_case_count IS NULL; ALTER TABLE test_plan_report_bug MODIFY `bug_case_count` BIGINT NOT NULL DEFAULT 0 COMMENT '缺陷用例数'; -- 修改测试计划关联接口表字段 diff --git a/backend/framework/sdk/src/main/resources/i18n/plan.properties b/backend/framework/sdk/src/main/resources/i18n/plan.properties index 07a3050149..aceca1de2e 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan.properties @@ -104,11 +104,11 @@ test_plan.batch.log={0}测试计划 test_plan_report_not_exist=测试计划报告不存在 test_plan_report_id.not_blank=测试计划报告id不能为空 test_plan_report_name.not_blank=测试计划报告名称不能为空 -run_functional_case=执行功能用例 test_plan_not_exist=测试计划不存在 test_plan.report_id.not_blank=测试计划报告ID不能为空 test_plan.report.share_id.not_blank=测试计划报告分享ID不能为空 no_plan_to_archive=没有可归档的计划/计划组 test_plan.is.archived=测试计划已归档 test_plan_module_already_exists=同名模块已存在 -test_plan_report_name_length_range=报告名称长度过长 \ No newline at end of file +test_plan_report_name_length_range=报告名称长度过长 +test_plan_allocation_type_param_error=测试集所属分类参数错误 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties index 0aa5888bd7..032b3457b7 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties @@ -111,4 +111,5 @@ test_plan.report.share_id.not_blank=The test plan report share ID cannot be empt no_plan_to_archive=No plans/plan groups to archive test_plan.is.archived=Test plan has been archived test_plan_module_already_exists=The module with the same name already exists -test_plan_report_name_length_range=The report name is too long \ No newline at end of file +test_plan_report_name_length_range=The report name is too long +test_plan_allocation_type_param_error=The parameter of the allocation type is not correct \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties index 07a3050149..71a701ac97 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties @@ -111,4 +111,5 @@ test_plan.report.share_id.not_blank=测试计划报告分享ID不能为空 no_plan_to_archive=没有可归档的计划/计划组 test_plan.is.archived=测试计划已归档 test_plan_module_already_exists=同名模块已存在 -test_plan_report_name_length_range=报告名称长度过长 \ No newline at end of file +test_plan_report_name_length_range=报告名称长度过长 +test_plan_allocation_type_param_error=测试集所属分类参数错误 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties index 7ed5178362..d05f848b0c 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties @@ -111,4 +111,5 @@ test_plan.report.share_id.not_blank=測試計劃報告分享ID不能爲空 no_plan_to_archive=沒有可歸檔的計劃/計劃組 test_plan.is.archived=測試計劃已歸檔 test_plan_module_already_exists=同名模塊已存在 -test_plan_report_name_length_range=报告名称长度过长 \ No newline at end of file +test_plan_report_name_length_range=报告名称长度过长 +test_plan_allocation_type_param_error=測試集所屬分類參數錯誤 \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/provider/AssociateFunctionalProvider.java b/backend/services/case-management/src/main/java/io/metersphere/functional/provider/AssociateFunctionalProvider.java index 872fa4687a..65df7209ce 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/provider/AssociateFunctionalProvider.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/provider/AssociateFunctionalProvider.java @@ -1,61 +1,63 @@ -package io.metersphere.functional.provider; - -import io.metersphere.dto.BaseCaseCustomFieldDTO; -import io.metersphere.dto.TestCaseProviderDTO; -import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO; -import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; -import io.metersphere.functional.service.FunctionalCaseService; -import io.metersphere.provider.BaseAssociateCaseProvider; -import io.metersphere.request.AssociateOtherCaseRequest; -import io.metersphere.request.TestCasePageProviderRequest; -import io.metersphere.sdk.util.BeanUtils; -import jakarta.annotation.Resource; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@Service("FUNCTIONAL") -public class AssociateFunctionalProvider implements BaseAssociateCaseProvider { - - @Resource - private FunctionalCaseService functionalCaseService; - @Resource - private ExtFunctionalCaseMapper extFunctionalCaseMapper; - - @Override - public List listUnRelatedTestCaseList(TestCasePageProviderRequest testCasePageProviderRequest) { - List functionalCases = extFunctionalCaseMapper.listUnRelatedCaseWithBug(testCasePageProviderRequest, false, testCasePageProviderRequest.getSortString()); - if (CollectionUtils.isEmpty(functionalCases)) { - return new ArrayList<>(); - } - List ids = functionalCases.stream().map(TestCaseProviderDTO::getId).toList(); - Map> caseCustomFiledMap = functionalCaseService.getCaseCustomFiledMap(ids, testCasePageProviderRequest.getProjectId()); - functionalCases.forEach(functionalCase -> { - List customFields = caseCustomFiledMap.get(functionalCase.getId()); - List customs = new ArrayList<>(); - for (FunctionalCaseCustomFieldDTO customField : customFields) { - BaseCaseCustomFieldDTO baseCaseCustomFieldDTO = new BaseCaseCustomFieldDTO(); - BeanUtils.copyBean(baseCaseCustomFieldDTO, customField); - customs.add(baseCaseCustomFieldDTO); - } - functionalCase.setCustomFields(customs); - }); - return functionalCases; - } - - @Override - public List getRelatedIdsByParam(AssociateOtherCaseRequest request, boolean deleted) { - if (request.isSelectAll()) { - List relatedIds = extFunctionalCaseMapper.getSelectIdsByAssociateParam(request, deleted); - if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { - relatedIds = relatedIds.stream().filter(id -> !request.getExcludeIds().contains(id)).toList(); - } - return relatedIds; - } else { - return request.getSelectIds(); - } - } -} +package io.metersphere.functional.provider; + +import io.metersphere.dto.BaseCaseCustomFieldDTO; +import io.metersphere.dto.TestCaseProviderDTO; +import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO; +import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; +import io.metersphere.functional.service.FunctionalCaseService; +import io.metersphere.provider.BaseAssociateCaseProvider; +import io.metersphere.request.AssociateOtherCaseRequest; +import io.metersphere.request.TestCasePageProviderRequest; +import io.metersphere.sdk.util.BeanUtils; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Service("FUNCTIONAL") +public class AssociateFunctionalProvider implements BaseAssociateCaseProvider { + + @Resource + private FunctionalCaseService functionalCaseService; + @Resource + private ExtFunctionalCaseMapper extFunctionalCaseMapper; + + @Override + public List listUnRelatedTestCaseList(TestCasePageProviderRequest testCasePageProviderRequest) { + List functionalCases = extFunctionalCaseMapper.listUnRelatedCaseWithBug(testCasePageProviderRequest, false, testCasePageProviderRequest.getSortString()); + if (CollectionUtils.isEmpty(functionalCases)) { + return new ArrayList<>(); + } + List ids = functionalCases.stream().map(TestCaseProviderDTO::getId).toList(); + Map> caseCustomFiledMap = functionalCaseService.getCaseCustomFiledMap(ids, testCasePageProviderRequest.getProjectId()); + functionalCases.forEach(functionalCase -> { + List customFields = caseCustomFiledMap.get(functionalCase.getId()); + if (CollectionUtils.isNotEmpty(customFields)) { + List customs = new ArrayList<>(); + for (FunctionalCaseCustomFieldDTO customField : customFields) { + BaseCaseCustomFieldDTO baseCaseCustomFieldDTO = new BaseCaseCustomFieldDTO(); + BeanUtils.copyBean(baseCaseCustomFieldDTO, customField); + customs.add(baseCaseCustomFieldDTO); + } + functionalCase.setCustomFields(customs); + } + }); + return functionalCases; + } + + @Override + public List getRelatedIdsByParam(AssociateOtherCaseRequest request, boolean deleted) { + if (request.isSelectAll()) { + List relatedIds = extFunctionalCaseMapper.getSelectIdsByAssociateParam(request, deleted); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + relatedIds = relatedIds.stream().filter(id -> !request.getExcludeIds().contains(id)).toList(); + } + return relatedIds; + } else { + return request.getSelectIds(); + } + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanAllocationTypeDTO.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanAllocationTypeDTO.java new file mode 100644 index 0000000000..5dc64583ef --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanAllocationTypeDTO.java @@ -0,0 +1,24 @@ +package io.metersphere.plan.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class TestPlanAllocationTypeDTO { + + @Schema(description = "测试集类型ID") + private String id; + + @Schema(description = "测试集名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + + @Schema(description = "测试集类型", allowableValues = {"FUNCTIONAL", "API", "SCENARIO"}, requiredMode = Schema.RequiredMode.REQUIRED) + private String type; + + @Schema(description = "执行方式", allowableValues = {"SERIAL-串行", "PARALLEL-并行"}, requiredMode = Schema.RequiredMode.REQUIRED) + private String executeMethod; + + @Schema(description = "位置, 从1开始递增的整数即可", requiredMode = Schema.RequiredMode.REQUIRED) + private Long pos; + +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanCollectionInitDTO.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanCollectionInitDTO.java new file mode 100644 index 0000000000..4a2668110f --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanCollectionInitDTO.java @@ -0,0 +1,29 @@ +package io.metersphere.plan.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class TestPlanCollectionInitDTO { + + @Schema(description = "测试集节点ID") + private String id; + + @Schema(description = "所属测试集类型ID, 类型ID空时传递具体测试集类型名称即可", requiredMode = Schema.RequiredMode.REQUIRED) + private String testCollectionTypeId; + + @Schema(description = "测试集名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + + @Schema(description = "执行方式", allowableValues = {"SERIAL-串行", "PARALLEL-并行"}, requiredMode = Schema.RequiredMode.REQUIRED) + private String executeMethod; + + @Schema(description = "是否使用环境组", requiredMode = Schema.RequiredMode.REQUIRED) + private Boolean grouped; + + @Schema(description = "环境ID/环境组ID, 根据是否环境组来传递相应的值", requiredMode = Schema.RequiredMode.REQUIRED) + private String environmentId; + + @Schema(description = "位置, 从1开始递增的整数即可", requiredMode = Schema.RequiredMode.REQUIRED) + private Long pos; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanAllocationCreateRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanAllocationCreateRequest.java new file mode 100644 index 0000000000..c4186de404 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanAllocationCreateRequest.java @@ -0,0 +1,27 @@ +package io.metersphere.plan.dto.request; + +import io.metersphere.plan.domain.TestPlanAllocation; +import io.metersphere.plan.dto.TestPlanAllocationTypeDTO; +import io.metersphere.plan.dto.TestPlanCollectionInitDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 规划创建请求参数-脑图使用 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class TestPlanAllocationCreateRequest { + + @Schema(description = "规划节点(分类/类型-第二级)") + private List typeNodes; + + @Schema(description = "规划节点(功能集-第三级)") + private List collectionNodes; + + @Schema(description = "规划配置") + private TestPlanAllocation config; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCreateRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCreateRequest.java index afa723c948..0d38bc4f08 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCreateRequest.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCreateRequest.java @@ -1,78 +1,81 @@ -package io.metersphere.plan.dto.request; - -import io.metersphere.sdk.constants.ModuleConstants; -import io.metersphere.sdk.constants.TestPlanConstants; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import lombok.Data; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; - -@Data -public class TestPlanCreateRequest { - @Schema(description = "测试计划所属项目", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan.project_id.not_blank}") - @Size(min = 1, max = 50, message = "{test_plan.project_id.length_range}") - private String projectId; - - @Schema(description = "测试计划组ID;测试计划要改为树结构。最上层的为NONE,其余则是父节点ID") - @Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}") - private String groupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID; - - @Schema(description = "测试计划名称", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan.name.not_blank}") - @Size(min = 1, max = 255, message = "{test_plan.name.length_range}") - private String name; - - - @Schema(description = "测试计划模块ID") - @Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}") - private String moduleId = ModuleConstants.DEFAULT_NODE_ID; - - @Schema(description = "计划开始时间") - private Long plannedStartTime; - - @Schema(description = "计划结束时间") - private Long plannedEndTime; - - @Schema(description = "标签") - private LinkedHashSet< - @NotBlank - String> tags; - - @Schema(description = "描述") - private String description; - - @Schema(description = "是否开启测试规划", requiredMode = Schema.RequiredMode.REQUIRED) - private boolean testPlanning; - - @Schema(description = "是否自定更新功能用例状态", requiredMode = Schema.RequiredMode.REQUIRED) - private boolean automaticStatusUpdate; - - @Schema(description = "是否允许重复添加用例", requiredMode = Schema.RequiredMode.REQUIRED) - private boolean repeatCase; - - @Schema(description = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED) - @Max(value = 100, message = "{test_plan.pass_threshold.max}") - @Min(value = 0) - private double passThreshold = 100; - @Schema(description = "测试计划类型",allowableValues ={"TEST_PLAN", "GROUP"}, requiredMode = Schema.RequiredMode.REQUIRED ) - private String type = TestPlanConstants.TEST_PLAN_TYPE_PLAN; - - public List getTags() { - return tags == null ? null : new ArrayList<>(tags); - } - - public boolean isGroupOption() { - return StringUtils.equals(this.type, TestPlanConstants.TEST_PLAN_TYPE_GROUP) || !StringUtils.equals(this.groupId, TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID); - } - - @Schema(description = "查询用例的条件") - private BaseAssociateCaseRequest baseAssociateCaseRequest; -} +package io.metersphere.plan.dto.request; + +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.constants.TestPlanConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; + +@Data +public class TestPlanCreateRequest { + @Schema(description = "测试计划所属项目", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan.project_id.not_blank}") + @Size(min = 1, max = 50, message = "{test_plan.project_id.length_range}") + private String projectId; + + @Schema(description = "测试计划组ID;测试计划要改为树结构。最上层的为NONE,其余则是父节点ID") + @Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}") + private String groupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID; + + @Schema(description = "测试计划名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan.name.not_blank}") + @Size(min = 1, max = 255, message = "{test_plan.name.length_range}") + private String name; + + + @Schema(description = "测试计划模块ID") + @Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}") + private String moduleId = ModuleConstants.DEFAULT_NODE_ID; + + @Schema(description = "计划开始时间") + private Long plannedStartTime; + + @Schema(description = "计划结束时间") + private Long plannedEndTime; + + @Schema(description = "标签") + private LinkedHashSet< + @NotBlank + String> tags; + + @Schema(description = "描述") + private String description; + + @Schema(description = "是否开启测试规划", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean testPlanning; + + @Schema(description = "是否自定更新功能用例状态", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean automaticStatusUpdate; + + @Schema(description = "是否允许重复添加用例", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean repeatCase; + + @Schema(description = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED) + @Max(value = 100, message = "{test_plan.pass_threshold.max}") + @Min(value = 0) + private double passThreshold = 100; + @Schema(description = "测试计划类型",allowableValues ={"TEST_PLAN", "GROUP"}, requiredMode = Schema.RequiredMode.REQUIRED ) + private String type = TestPlanConstants.TEST_PLAN_TYPE_PLAN; + + public List getTags() { + return tags == null ? null : new ArrayList<>(tags); + } + + public boolean isGroupOption() { + return StringUtils.equals(this.type, TestPlanConstants.TEST_PLAN_TYPE_GROUP) || !StringUtils.equals(this.groupId, TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID); + } + + @Schema(description = "查询用例的条件") + private BaseAssociateCaseRequest baseAssociateCaseRequest; + + @Schema(description = "测试规划请求参数") + private TestPlanAllocationCreateRequest allocationRequest; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanUpdateRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanUpdateRequest.java index 680ada281c..9bf7cd8b9e 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanUpdateRequest.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanUpdateRequest.java @@ -1,56 +1,58 @@ -package io.metersphere.plan.dto.request; - -import io.metersphere.sdk.constants.ModuleConstants; -import io.metersphere.sdk.constants.TestPlanConstants; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import lombok.Data; - -import java.util.LinkedHashSet; - -@Data -public class TestPlanUpdateRequest { - @Schema(description = "测试计划ID") - @NotBlank(message = "{test_plan.id.not_blank}") - private String id; - - @Schema(description = "测试计划名称") - @Size(min = 1, max = 255, message = "{test_plan.name.length_range}") - private String name; - - @Schema(description = "模块ID") - private String moduleId = ModuleConstants.DEFAULT_NODE_ID; - - @Schema(description = "标签") - private LinkedHashSet tags; - - @Schema(description = "计划开始时间") - private Long plannedStartTime; - - @Schema(description = "计划结束时间") - private Long plannedEndTime; - - @Schema(description = "描述") - private String description; - - @Schema(description = "是否开启测试规划") - private Boolean testPlanning; - - @Schema(description = "是否自定更新功能用例状态") - private Boolean automaticStatusUpdate; - - @Schema(description = "是否允许重复添加用例") - private Boolean repeatCase; - - @Schema(description = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED) - @Max(value = 100) - @Min(value = 0) - private Double passThreshold; - - @Schema(description = "测试计划组Id") - private String testPlanGroupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID; - -} +package io.metersphere.plan.dto.request; + +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.constants.TestPlanConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.LinkedHashSet; + +@Data +public class TestPlanUpdateRequest { + @Schema(description = "测试计划ID") + @NotBlank(message = "{test_plan.id.not_blank}") + private String id; + + @Schema(description = "测试计划名称") + @Size(min = 1, max = 255, message = "{test_plan.name.length_range}") + private String name; + + @Schema(description = "模块ID") + private String moduleId = ModuleConstants.DEFAULT_NODE_ID; + + @Schema(description = "标签") + private LinkedHashSet tags; + + @Schema(description = "计划开始时间") + private Long plannedStartTime; + + @Schema(description = "计划结束时间") + private Long plannedEndTime; + + @Schema(description = "描述") + private String description; + + @Schema(description = "是否开启测试规划") + private Boolean testPlanning; + + @Schema(description = "是否自定更新功能用例状态") + private Boolean automaticStatusUpdate; + + @Schema(description = "是否允许重复添加用例") + private Boolean repeatCase; + + @Schema(description = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED) + @Max(value = 100) + @Min(value = 0) + private Double passThreshold; + + @Schema(description = "测试计划组Id") + private String testPlanGroupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID; + + @Schema(description = "测试规划请求参数") + private TestPlanAllocationCreateRequest allocationRequest; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/enums/ExecuteMethod.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/enums/ExecuteMethod.java new file mode 100644 index 0000000000..f2f0aa2182 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/enums/ExecuteMethod.java @@ -0,0 +1,14 @@ +package io.metersphere.plan.enums; + +public enum ExecuteMethod { + + /** + * 串行 + */ + SERIAL, + + /** + * 并行 + */ + PARALLEL +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java index 47db53add6..ee7265bf43 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java @@ -83,6 +83,7 @@ public class TestPlanService extends TestPlanBaseUtilsService { private TestPlanSendNoticeService testPlanSendNoticeService; @Resource private TestPlanCaseExecuteHistoryMapper testPlanCaseExecuteHistoryMapper; + private static final int MAX_TAG_SIZE = 10; /** @@ -90,6 +91,8 @@ public class TestPlanService extends TestPlanBaseUtilsService { */ public TestPlan add(TestPlanCreateRequest testPlanCreateRequest, String operator, String requestUrl, String requestMethod) { TestPlan testPlan = savePlanDTO(testPlanCreateRequest, operator, null); + // 保存规划节点及配置 + saveAllocation(testPlanCreateRequest.getAllocationRequest(), operator, testPlan.getId()); testPlanLogService.saveAddLog(testPlan, operator, requestUrl, requestMethod); return testPlan; } @@ -152,6 +155,82 @@ public class TestPlanService extends TestPlanBaseUtilsService { } } + /** + * 保存规划 + * + * @param allocationRequest 规划的请求参数 + * @param currentUser 当前用户 + */ + private void saveAllocation(TestPlanAllocationCreateRequest allocationRequest, String currentUser, String planId) { + // 前置参数校验 + checkAllocationParam(allocationRequest); + + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanAllocationTypeMapper allocationTypeBatchMapper = sqlSession.getMapper(TestPlanAllocationTypeMapper.class); + TestPlanCollectionMapper collectionBatchMapper = sqlSession.getMapper(TestPlanCollectionMapper.class); + // 处理测试集分类 + Map allocationTypeMap = new HashMap<>(); + List addAllocationTypes = new ArrayList<>(); + List updateAllocationTypes = new ArrayList<>(); + allocationRequest.getTypeNodes().forEach(allocationTypeDTO -> { + TestPlanAllocationType allocationType = new TestPlanAllocationType(); + BeanUtils.copyBean(allocationType, allocationTypeDTO); + allocationType.setTestPlanId(planId); + allocationType.setPos(allocationType.getPos() << 6); + if (allocationTypeDTO.getId() == null) { + allocationType.setId(IDGenerator.nextStr()); + allocationTypeMap.put(allocationType.getType(), allocationType.getId()); + addAllocationTypes.add(allocationType); + } else { + updateAllocationTypes.add(allocationType); + } + }); + // 处理测试集 + List addCollections = new ArrayList<>(); + List updateCollections = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(allocationRequest.getCollectionNodes())) { + allocationRequest.getCollectionNodes().forEach(collectionNode -> { + TestPlanCollection collection = new TestPlanCollection(); + BeanUtils.copyBean(collection, collectionNode); + collection.setTestPlanId(planId); + collection.setPos(collection.getPos() << 6); + if (collection.getId() == null) { + collection.setId(IDGenerator.nextStr()); + collection.setTestCollectionTypeId(allocationTypeMap.get(collection.getTestCollectionTypeId())); + collection.setCreateUser(currentUser); + collection.setCreateTime(System.currentTimeMillis()); + addCollections.add(collection); + } else { + updateCollections.add(collection); + } + }); + } + + // 入库 { 测试集分类, 测试集, 规划配置项 } + if (CollectionUtils.isNotEmpty(addAllocationTypes)) { + allocationTypeBatchMapper.batchInsert(addAllocationTypes); + } + if (CollectionUtils.isNotEmpty(updateAllocationTypes)) { + // 仅有三条记录 + updateAllocationTypes.forEach(allocationType -> allocationTypeBatchMapper.updateByPrimaryKey(allocationType)); + } + if (CollectionUtils.isNotEmpty(addCollections)) { + collectionBatchMapper.batchInsert(addCollections); + } + if (CollectionUtils.isNotEmpty(updateCollections)) { + updateCollections.forEach(collection -> collectionBatchMapper.updateByPrimaryKeySelective(collection)); + } + if (allocationRequest.getConfig().getId() == null) { + TestPlanAllocation allocationConfig = allocationRequest.getConfig(); + allocationConfig.setId(IDGenerator.nextStr()); + allocationConfig.setTestPlanId(planId); + testPlanAllocationMapper.insert(allocationConfig); + } else { + testPlanAllocationMapper.updateByPrimaryKeySelective(allocationRequest.getConfig()); + } + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } /** * 删除测试计划 @@ -326,6 +405,8 @@ public class TestPlanService extends TestPlanBaseUtilsService { testPlanConfig.setPassThreshold(request.getPassThreshold()); testPlanConfigMapper.updateByPrimaryKeySelective(testPlanConfig); } + // 保存规划节点及配置 + saveAllocation(request.getAllocationRequest(), userId, testPlan.getId()); testPlanLogService.saveUpdateLog(testPlan, testPlanMapper.selectByPrimaryKey(request.getId()), testPlan.getProjectId(), userId, requestUrl, requestMethod); return testPlan; } @@ -721,4 +802,10 @@ public class TestPlanService extends TestPlanBaseUtilsService { return new TestPlanResourceSortResponse(1); } + + private void checkAllocationParam(TestPlanAllocationCreateRequest request) { + if (CollectionUtils.size(request.getTypeNodes()) != 3) { + throw new MSException(Translator.get("test_plan_allocation_type_param_error")); + } + } } diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java index 1f8c4baca2..7894b53c06 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java @@ -5,12 +5,13 @@ import io.metersphere.api.domain.ApiTestCase; import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.plan.constants.TestPlanResourceConfig; import io.metersphere.plan.domain.*; +import io.metersphere.plan.dto.TestPlanAllocationTypeDTO; +import io.metersphere.plan.dto.TestPlanCollectionInitDTO; import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.response.TestPlanResourceSortResponse; import io.metersphere.plan.dto.response.TestPlanResponse; -import io.metersphere.plan.mapper.ExtTestPlanMapper; -import io.metersphere.plan.mapper.TestPlanMapper; -import io.metersphere.plan.mapper.TestPlanReportMapper; +import io.metersphere.plan.enums.ExecuteMethod; +import io.metersphere.plan.mapper.*; import io.metersphere.plan.service.*; import io.metersphere.plan.utils.TestPlanTestUtils; import io.metersphere.project.domain.Project; @@ -89,6 +90,12 @@ public class TestPlanTests extends BaseTest { private CommonProjectService commonProjectService; @Resource private TestPlanTestService testPlanTestService; + @Resource + private TestPlanAllocationTypeMapper testPlanAllocationTypeMapper; + @Resource + private TestPlanCollectionMapper testPlanCollectionMapper; + @Resource + private TestPlanAllocationMapper testPlanAllocationMapper; private static final List LOG_CHECK_LIST = new ArrayList<>(); @@ -526,6 +533,7 @@ public class TestPlanTests extends BaseTest { BaseAssociateCaseRequest associateCaseRequest = new BaseAssociateCaseRequest(); request.setBaseAssociateCaseRequest(associateCaseRequest); + request.setAllocationRequest(buildAllocationParam()); for (int i = 0; i < 999; i++) { String moduleId; if (i < 50) { @@ -637,6 +645,7 @@ public class TestPlanTests extends BaseTest { itemRequest.setGroupId(groupTestPlanId7); itemRequest.setName("testPlan_group7_" + i); itemRequest.setBaseAssociateCaseRequest(associateCaseRequest); + itemRequest.setAllocationRequest(buildAllocationParam()); if (i == 0) { //测试项目没有开启测试计划模块时能否使用 testPlanTestService.removeProjectModule(project, PROJECT_MODULE, "testPlan"); @@ -947,6 +956,7 @@ public class TestPlanTests extends BaseTest { //修改名称 TestPlanUpdateRequest updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); updateRequest.setName(IDGenerator.nextStr()); + updateRequest.setAllocationRequest(buildAllocationParam()); //测试项目没有开启测试计划模块时能否使用 testPlanTestService.removeProjectModule(project, PROJECT_MODULE, "testPlan"); @@ -965,6 +975,7 @@ public class TestPlanTests extends BaseTest { //修改回来 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); updateRequest.setName(testPlan.getName()); + updateRequest.setAllocationRequest(buildAllocationParam()); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); returnStr = mvcResult.getResponse().getContentAsString(); holder = JSON.parseObject(returnStr, ResultHolder.class); @@ -983,6 +994,7 @@ public class TestPlanTests extends BaseTest { BaseTreeNode a2Node = TestPlanTestUtils.getNodeByName(preliminaryTreeNodes, "a2"); updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); updateRequest.setModuleId(a2Node.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); returnStr = mvcResult.getResponse().getContentAsString(); holder = JSON.parseObject(returnStr, ResultHolder.class); @@ -991,6 +1003,7 @@ public class TestPlanTests extends BaseTest { testPlanTestService.checkTestPlanUpdateResult(testPlan, testPlanConfig, updateRequest); //修改回来 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setModuleId(testPlan.getModuleId()); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); returnStr = mvcResult.getResponse().getContentAsString(); @@ -1001,6 +1014,7 @@ public class TestPlanTests extends BaseTest { //修改标签 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setTags(new LinkedHashSet<>(Arrays.asList("tag1", "tag2", "tag3", "tag3"))); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); returnStr = mvcResult.getResponse().getContentAsString(); @@ -1012,6 +1026,7 @@ public class TestPlanTests extends BaseTest { //修改计划开始结束时间 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setPlannedStartTime(System.currentTimeMillis()); updateRequest.setPlannedEndTime(updateRequest.getPlannedStartTime() - 10000); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); @@ -1024,6 +1039,7 @@ public class TestPlanTests extends BaseTest { //修改描述 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setDescription("This is desc"); mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_UPDATE, updateRequest); returnStr = mvcResult.getResponse().getContentAsString(); @@ -1035,6 +1051,7 @@ public class TestPlanTests extends BaseTest { //修改配置项 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setAutomaticStatusUpdate(true); updateRequest.setRepeatCase(true); updateRequest.setPassThreshold(43.12); @@ -1046,6 +1063,7 @@ public class TestPlanTests extends BaseTest { testPlanTestService.checkTestPlanUpdateResult(testPlan, testPlanConfig, updateRequest); updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setAutomaticStatusUpdate(false); updateRequest.setRepeatCase(false); updateRequest.setPassThreshold(56.47); @@ -1060,16 +1078,19 @@ public class TestPlanTests extends BaseTest { //修改a2节点下的数据(91,92)的所属测试计划组 updateRequest = testPlanTestService.generateUpdateRequest(testPlanTestService.selectTestPlanByName("testPlan_91").getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setTestPlanGroupId(groupTestPlanId7); this.requestPostWithOk(URL_POST_TEST_PLAN_UPDATE, updateRequest); a2NodeCount--; updateRequest = testPlanTestService.generateUpdateRequest(testPlanTestService.selectTestPlanByName("testPlan_92").getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setTestPlanGroupId(groupTestPlanId7); this.requestPostWithOk(URL_POST_TEST_PLAN_UPDATE, updateRequest); a2NodeCount--; //修改测试计划组信息 updateRequest = testPlanTestService.generateUpdateRequest(groupTestPlanId7); + updateRequest.setAllocationRequest(buildAllocationParam()); updateRequest.setName(IDGenerator.nextStr()); updateRequest.setDescription("This is desc"); updateRequest.setTags(new LinkedHashSet<>(Arrays.asList("tag1", "tag2", "tag3", "tag3"))); @@ -1079,6 +1100,7 @@ public class TestPlanTests extends BaseTest { //什么都不修改 updateRequest = testPlanTestService.generateUpdateRequest(testPlan.getId()); + updateRequest.setAllocationRequest(buildAllocationParam()); this.requestPostWithOk(URL_POST_TEST_PLAN_UPDATE, updateRequest); //因为有条数据被移动了测试计划组里,所以检查一下moduleCount. @@ -1838,7 +1860,7 @@ public class TestPlanTests extends BaseTest { TestPlanCreateRequest request = new TestPlanCreateRequest(); request.setProjectId(project.getId()); request.setTestPlanning(false); - + request.setAllocationRequest(buildAllocationParam()); BaseAssociateCaseRequest associateCaseRequest = new BaseAssociateCaseRequest(); associateCaseRequest.setFunctionalSelectIds(Arrays.asList("wx_fc_1", "wx_fc_2")); request.setBaseAssociateCaseRequest(associateCaseRequest); @@ -2045,4 +2067,89 @@ public class TestPlanTests extends BaseTest { this.requestPostWithOk(URL_TEST_PLAN_BATCH_EDIT, request); } + @Test + @Order(308) + void testSaveAllocation() throws Exception { + TestPlanCreateRequest createRequest = new TestPlanCreateRequest(); + BaseAssociateCaseRequest associateCaseRequest = new BaseAssociateCaseRequest(); + createRequest.setBaseAssociateCaseRequest(associateCaseRequest); + createRequest.setTestPlanning(true); + createRequest.setProjectId(project.getId()); + createRequest.setName("测试一下关联"); + createRequest.setPlannedEndTime(null); + createRequest.setPlannedStartTime(null); + createRequest.setRepeatCase(false); + createRequest.setAutomaticStatusUpdate(false); + createRequest.setPassThreshold(100); + createRequest.setDescription(null); + createRequest.setType(TestPlanConstants.TEST_PLAN_TYPE_PLAN); + createRequest.setAllocationRequest(buildAllocationParam()); + MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_ADD, createRequest); + String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder sortHolder = JSON.parseObject(sortData, ResultHolder.class); + TestPlan testPlan = JSON.parseObject(JSON.toJSONString(sortHolder.getData()), TestPlan.class); + TestPlanAllocationCreateRequest updateRequest = buildAllocationUpdateParam(testPlan.getId()); + createRequest.setAllocationRequest(updateRequest); + this.requestPostWithOk(URL_POST_TEST_PLAN_ADD, createRequest); + TestPlanAllocationCreateRequest createRequest1 = buildAllocationParam(); + createRequest1.setCollectionNodes(null); + createRequest.setAllocationRequest(createRequest1); + this.requestPostWithOk(URL_POST_TEST_PLAN_ADD, createRequest); + } + + private TestPlanAllocationCreateRequest buildAllocationParam() { + TestPlanAllocationCreateRequest request = new TestPlanAllocationCreateRequest(); + TestPlanAllocationTypeDTO functionalType = new TestPlanAllocationTypeDTO(); + functionalType.setType(CaseType.FUNCTIONAL_CASE.getKey()); + functionalType.setName("功能用例"); + functionalType.setPos(1L); + functionalType.setExecuteMethod(ExecuteMethod.SERIAL.name()); + request.setTypeNodes(List.of(functionalType, functionalType, functionalType)); + TestPlanCollectionInitDTO collection = new TestPlanCollectionInitDTO(); + collection.setName("默认测试集"); + collection.setTestCollectionTypeId(CaseType.FUNCTIONAL_CASE.getKey()); + collection.setExecuteMethod(ExecuteMethod.PARALLEL.name()); + collection.setGrouped(false); + collection.setEnvironmentId(IDGenerator.nextStr()); + collection.setPos(1L); + request.setCollectionNodes(List.of(collection)); + TestPlanAllocation allocation = new TestPlanAllocation(); + allocation.setTestResourcePoolId(IDGenerator.nextStr()); + allocation.setRetryOnFail(true); + allocation.setRetryTimes(10); + allocation.setRetryInterval(1000); + allocation.setRetryType(CaseType.SCENARIO_CASE.getKey()); + allocation.setStopOnFail(false); + request.setConfig(allocation); + return request; + } + + private TestPlanAllocationCreateRequest buildAllocationUpdateParam(String planId) { + TestPlanAllocationCreateRequest updateParam = new TestPlanAllocationCreateRequest(); + TestPlanAllocationTypeExample allocationTypeExample = new TestPlanAllocationTypeExample(); + allocationTypeExample.createCriteria().andTestPlanIdEqualTo(planId); + List testPlanAllocationTypes = testPlanAllocationTypeMapper.selectByExample(allocationTypeExample); + List allocationTypeDTOS = new ArrayList<>(); + testPlanAllocationTypes.forEach(allocationType -> { + TestPlanAllocationTypeDTO allocationTypeDTO = new TestPlanAllocationTypeDTO(); + BeanUtils.copyBean(allocationTypeDTO, allocationType); + allocationTypeDTOS.add(allocationTypeDTO); + }); + TestPlanCollectionExample collectionExample = new TestPlanCollectionExample(); + collectionExample.createCriteria().andTestPlanIdEqualTo(planId); + List collections = testPlanCollectionMapper.selectByExample(collectionExample); + List collectionInitDTOS = new ArrayList<>(); + collections.forEach(collection -> { + TestPlanCollectionInitDTO collectionInitDTO = new TestPlanCollectionInitDTO(); + BeanUtils.copyBean(collectionInitDTO, collection); + collectionInitDTOS.add(collectionInitDTO); + }); + TestPlanAllocationExample example = new TestPlanAllocationExample(); + example.createCriteria().andTestPlanIdEqualTo(planId); + TestPlanAllocation allocation = testPlanAllocationMapper.selectByExample(example).get(0); + updateParam.setTypeNodes(allocationTypeDTOS); + updateParam.setCollectionNodes(collectionInitDTOS); + updateParam.setConfig(allocation); + return updateParam; + } }