feat(测试计划): 测试计划增加删除和批量删除的方法

测试计划增加删除和批量删除的方法
This commit is contained in:
song-tianyang 2023-06-06 14:18:06 +08:00 committed by f2c-ci-robot[bot]
parent ca06577113
commit 1c282ab379
32 changed files with 675 additions and 106 deletions

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlan implements Serializable { public class TestPlan implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanApiCase implements Serializable { public class TestPlanApiCase implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_api_case.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_api_case.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_api_case.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_api_case.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanApiScenario implements Serializable { public class TestPlanApiScenario implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_api_scenario.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_api_scenario.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_api_scenario.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_api_scenario.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanConfig implements Serializable { public class TestPlanConfig implements Serializable {
@Schema(title = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_config.test_plan_id.not_blank}", groups = {Created.class}) @NotBlank(message = "{test_plan_config.test_plan_id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_config.test_plan_id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_config.test_plan_id.length_range}", groups = {Created.class, Updated.class})
private String testPlanId; private String testPlanId;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanExecuteRecord implements Serializable { public class TestPlanExecuteRecord implements Serializable {
@Schema(title = "测试计划执行记录ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "测试计划执行记录ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_execute_record.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_execute_record.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_execute_record.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_execute_record.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanExecutionQueue implements Serializable { public class TestPlanExecutionQueue implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_execution_queue.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_execution_queue.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_execution_queue.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_execution_queue.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanFunctionCase implements Serializable { public class TestPlanFunctionCase implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_function_case.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_function_case.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_function_case.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_function_case.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanFunctionCaseResult implements Serializable { public class TestPlanFunctionCaseResult implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_function_case_result.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_function_case_result.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_function_case_result.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_function_case_result.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanLoadCase implements Serializable { public class TestPlanLoadCase implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_load_case.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_load_case.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_load_case.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_load_case.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanRecordApiCaseInfo implements Serializable { public class TestPlanRecordApiCaseInfo implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_record_api_case_info.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_record_api_case_info.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_record_api_case_info.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_api_case_info.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanRecordApiScenarioInfo implements Serializable { public class TestPlanRecordApiScenarioInfo implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_record_api_scenario_info.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_record_api_scenario_info.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_record_api_scenario_info.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_api_scenario_info.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanRecordLoadCaseInfo implements Serializable { public class TestPlanRecordLoadCaseInfo implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_record_load_case_info.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_record_load_case_info.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_record_load_case_info.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_load_case_info.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanRecordUiScenarioInfo implements Serializable { public class TestPlanRecordUiScenarioInfo implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_record_ui_scenario_info.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_record_ui_scenario_info.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_record_ui_scenario_info.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_ui_scenario_info.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanReport implements Serializable { public class TestPlanReport implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_report.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_report.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_report.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -5,13 +5,14 @@ import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanReportContent implements Serializable { public class TestPlanReportContent implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_content.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_report_content.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_report_content.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_report_content.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
@Data @Data
public class TestPlanUiScenario implements Serializable { public class TestPlanUiScenario implements Serializable {
@Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_ui_scenario.id.not_blank}", groups = {Created.class, Updated.class}) @NotBlank(message = "{test_plan_ui_scenario.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_ui_scenario.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_ui_scenario.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;

View File

@ -41,12 +41,105 @@ CREATE TABLE IF NOT EXISTS test_plan_principal(
CREATE TABLE IF NOT EXISTS test_plan_config( CREATE TABLE IF NOT EXISTS test_plan_config(
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`run_mode_config` TEXT NOT NULL COMMENT '运行模式' , `run_mode_config` TEXT COMMENT '运行模式' ,
`automatic_status_update` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否自定更新功能用例状态' , `automatic_status_update` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否自定更新功能用例状态' ,
`repeat_case` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否允许重复添加用例' , `repeat_case` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否允许重复添加用例' ,
`pass_threshold` INT(3) NOT NULL DEFAULT 100 COMMENT '测试计划通过阈值;0-100' , `pass_threshold` INT(3) NOT NULL DEFAULT 100 COMMENT '测试计划通过阈值;0-100' ,
PRIMARY KEY (test_plan_id) PRIMARY KEY (test_plan_id)
) COMMENT = '测试计划配置'; ) COMMENT = '测试计划配置';
DROP TABLE IF EXISTS test_plan_api_case;
CREATE TABLE test_plan_api_case(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`api_case_id` VARCHAR(50) NOT NULL COMMENT '接口用例ID' ,
`environment_type` VARCHAR(20) COMMENT '环境类型' ,
`environment` LONGTEXT COMMENT '所属环境' ,
`environment_group_id` VARCHAR(50) COMMENT '环境组ID' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`create_user` VARCHAR(40) NOT NULL COMMENT '创建人' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
PRIMARY KEY (id)
) COMMENT = '测试计划关联接口用例';
CREATE INDEX idx_api_case_id ON test_plan_api_case(api_case_id);
CREATE INDEX idx_test_plan_id ON test_plan_api_case(test_plan_id);
CREATE INDEX idx_create_user ON test_plan_api_case(create_user);
DROP TABLE IF EXISTS test_plan_api_scenario;
CREATE TABLE test_plan_api_scenario(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`api_scenario_id` VARCHAR(255) COMMENT '场景ID' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`create_user` VARCHAR(100) NOT NULL COMMENT '创建人' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
`environment_type` VARCHAR(20) COMMENT '环境类型' ,
`environment` LONGTEXT COMMENT '所属环境' ,
`environment_group_id` VARCHAR(50) COMMENT '环境组ID' ,
PRIMARY KEY (id)
) COMMENT = '测试计划关联场景用例';
CREATE INDEX idx_api_scenario_id ON test_plan_api_scenario(api_scenario_id);
CREATE INDEX idx_test_plan_id ON test_plan_api_scenario(test_plan_id);
CREATE INDEX idx_create_user ON test_plan_api_scenario(create_user);
DROP TABLE IF EXISTS test_plan_load_case;
CREATE TABLE test_plan_load_case(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`load_case_id` VARCHAR(50) NOT NULL COMMENT '性能用例ID' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`test_resource_pool_id` VARCHAR(50) COMMENT '所用测试资源池ID' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
`load_configuration` LONGTEXT COMMENT '压力配置' ,
`advanced_configuration` TEXT COMMENT '高级配置' ,
PRIMARY KEY (id)
) COMMENT = '测试计划关联性能测试用例';
CREATE INDEX idx_load_case_id ON test_plan_load_case(load_case_id);
CREATE INDEX idx_test_plan_id ON test_plan_load_case(test_plan_id);
CREATE INDEX idx_create_user ON test_plan_load_case(create_user);
DROP TABLE IF EXISTS test_plan_function_case;
CREATE TABLE test_plan_function_case(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`function_case_id` VARCHAR(50) NOT NULL COMMENT '功能用例ID' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
PRIMARY KEY (id)
) COMMENT = '测试计划关联功能用例';
CREATE INDEX idx_function_case_id ON test_plan_function_case(function_case_id);
CREATE INDEX idx_test_plan_id ON test_plan_function_case(test_plan_id);
CREATE INDEX idx_create_user ON test_plan_function_case(create_user);
DROP TABLE IF EXISTS test_plan_ui_scenario;
CREATE TABLE test_plan_ui_scenario(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' ,
`ui_scenario_id` VARCHAR(50) NOT NULL COMMENT 'UI场景ID' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`pos` BIGINT NOT NULL COMMENT '排序默认值5000' ,
`environment_type` VARCHAR(20) COMMENT '环境类型' ,
`environment` LONGTEXT COMMENT '所属环境' ,
`environment_group_id` VARCHAR(50) COMMENT '环境组ID' ,
PRIMARY KEY (id)
) COMMENT = '测试计划关联UI场景';
CREATE INDEX idx_ui_scenario_id ON test_plan_ui_scenario(ui_scenario_id);
CREATE INDEX idx_test_plan_id ON test_plan_ui_scenario(test_plan_id);
CREATE INDEX idx_create_user ON test_plan_ui_scenario(create_user);
-- set innodb lock wait timeout to default -- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT; SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -55,7 +55,7 @@ public class RestControllerExceptionHandler {
IResultCode errorCode = e.getErrorCode(); IResultCode errorCode = e.getErrorCode();
if (errorCode == null) { if (errorCode == null) {
// 如果抛出异常没有设置状态码则返回错误 message // 如果抛出异常没有设置状态码则返回错误 message
return ResponseEntity.ok() return ResponseEntity.internalServerError()
.body(new ResultHolder(MsHttpResultCode.FAILED.getCode(), .body(new ResultHolder(MsHttpResultCode.FAILED.getCode(),
MsHttpResultCode.FAILED.getMessage(), e.getMessage())); MsHttpResultCode.FAILED.getMessage(), e.getMessage()));
} }
@ -71,6 +71,13 @@ public class RestControllerExceptionHandler {
} }
} }
@ExceptionHandler({Exception.class})
public ResponseEntity<ResultHolder> handlerMSException(Exception e) {
return ResponseEntity.internalServerError()
.body(new ResultHolder(MsHttpResultCode.FAILED.getCode(),
MsHttpResultCode.FAILED.getMessage(), e.getMessage()));
}
/*=========== Shiro 异常拦截==============*/ /*=========== Shiro 异常拦截==============*/
@ExceptionHandler(ShiroException.class) @ExceptionHandler(ShiroException.class)
public ResultHolder exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { public ResultHolder exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {

View File

@ -2,13 +2,15 @@ package io.metersphere.plan.controller;
import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.dto.TestPlanDTO;
import io.metersphere.plan.service.TestPlanService; import io.metersphere.plan.service.TestPlanService;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.validation.groups.Created; import io.metersphere.validation.groups.Created;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import java.util.List;
import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/test-plan") @RequestMapping("/test-plan")
@ -20,4 +22,17 @@ public class TestPlanController {
public TestPlanDTO addUser(@Validated({Created.class}) @RequestBody TestPlanDTO testPlan) { public TestPlanDTO addUser(@Validated({Created.class}) @RequestBody TestPlanDTO testPlan) {
return testPlanService.add(testPlan); return testPlanService.add(testPlan);
} }
@PostMapping("/delete/batch")
public void deleteBatch(@RequestBody List<String> idList) {
if (CollectionUtils.isEmpty(idList)) {
MSException.throwException("The ids cannot be empty!");
}
testPlanService.batchDelete(idList);
}
@GetMapping("/delete/{id}")
public void delete(@NotBlank @PathVariable String id) {
testPlanService.delete(id);
}
} }

View File

@ -3,9 +3,11 @@ package io.metersphere.plan.dto;
import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlan;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List; import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data @Data
public class TestPlanDTO extends TestPlan { public class TestPlanDTO extends TestPlan {
@Schema(title = "测试计划责任人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(title = "测试计划责任人", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@ -13,4 +15,13 @@ public class TestPlanDTO extends TestPlan {
@Schema(title = "测试计划关注人") @Schema(title = "测试计划关注人")
private List<String> followers; private List<String> followers;
@Schema(title = "是否自定更新功能用例状态", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean automaticStatusUpdate;
@Schema(title = "是否允许重复添加用例", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean repeatCase;
@Schema(title = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED)
private int passThreshold = 100;
} }

View File

@ -0,0 +1,11 @@
package io.metersphere.plan.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestPlanMapper {
List<String> selectByParentId(String parentId);
List<String> selectByParentIdList(@Param("list") List<String> parentTestPlanId);
}

View File

@ -0,0 +1,14 @@
<?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.plan.mapper.ExtTestPlanMapper">
<select id="selectByParentId" resultType="java.lang.String">
SELECT id FROM test_plan WHERE parent_id = #{parentId}
</select>
<select id="selectByParentIdList" resultType="java.lang.String">
SELECT id FROM test_plan WHERE parent_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@ -9,6 +9,8 @@ import jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class TestPlanApiCaseService { public class TestPlanApiCaseService {
@ -45,4 +47,16 @@ public class TestPlanApiCaseService {
BeanUtils.copyBean(testPlanApiCaseDTO, testPlanApiCase); BeanUtils.copyBean(testPlanApiCaseDTO, testPlanApiCase);
return testPlanApiCaseDTO; return testPlanApiCaseDTO;
} }
public int deleteByTestPlanId(String testPlanId) {
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
return testPlanApiCaseMapper.deleteByExample(example);
}
public int deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
return testPlanApiCaseMapper.deleteByExample(example);
}
} }

View File

@ -0,0 +1,28 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanApiScenarioExample;
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanApiScenarioService {
@Resource
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
public int deleteByTestPlanId(String testPlanId) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
return testPlanApiScenarioMapper.deleteByExample(example);
}
public int deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
return testPlanApiScenarioMapper.deleteByExample(example);
}
}

View File

@ -0,0 +1,28 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanConfigExample;
import io.metersphere.plan.mapper.TestPlanConfigMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanConfigService {
@Resource
private TestPlanConfigMapper testPlanConfigMapper;
public void delete(String testPlanId) {
TestPlanConfigExample example = new TestPlanConfigExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
testPlanConfigMapper.deleteByExample(example);
}
public void deleteBatch(List<String> testPlanIdList) {
TestPlanConfigExample example = new TestPlanConfigExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
testPlanConfigMapper.deleteByExample(example);
}
}

View File

@ -3,6 +3,10 @@ package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanFollower; import io.metersphere.plan.domain.TestPlanFollower;
import io.metersphere.plan.mapper.TestPlanFollowerMapper; import io.metersphere.plan.mapper.TestPlanFollowerMapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -13,11 +17,23 @@ import java.util.List;
public class TestPlanFollowerService { public class TestPlanFollowerService {
@Resource @Resource
TestPlanFollowerMapper testPlanFollowerMapper; private SqlSessionFactory sqlSessionFactory;
public void batchSave(List<TestPlanFollower> testPlanFollowerList) { public void batchSave(List<TestPlanFollower> testPlanFollowerList) {
for (TestPlanFollower testPlanFollower : testPlanFollowerList) { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
testPlanFollowerMapper.insert(testPlanFollower); TestPlanFollowerMapper testPlanFollowerMapper = sqlSession.getMapper(TestPlanFollowerMapper.class);
try {
int insertIndex = 0;
for (TestPlanFollower testPlanFollower : testPlanFollowerList) {
testPlanFollowerMapper.insert(testPlanFollower);
insertIndex++;
if (insertIndex % 50 == 0) {
sqlSession.flushStatements();
}
}
sqlSession.flushStatements();
} finally {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} }
} }
} }

View File

@ -0,0 +1,28 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanFunctionCaseExample;
import io.metersphere.plan.mapper.TestPlanFunctionCaseMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanFunctionCaseService {
@Resource
private TestPlanFunctionCaseMapper testPlanFunctionCaseMapper;
public int deleteByTestPlanId(String testPlanId) {
TestPlanFunctionCaseExample example = new TestPlanFunctionCaseExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
return testPlanFunctionCaseMapper.deleteByExample(example);
}
public int deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanFunctionCaseExample example = new TestPlanFunctionCaseExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
return testPlanFunctionCaseMapper.deleteByExample(example);
}
}

View File

@ -0,0 +1,28 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanLoadCaseExample;
import io.metersphere.plan.mapper.TestPlanLoadCaseMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanLoadCaseService {
@Resource
private TestPlanLoadCaseMapper testPlanLoadCaseMapper;
public void deleteByTestPlanId(String testPlanId) {
TestPlanLoadCaseExample example = new TestPlanLoadCaseExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
testPlanLoadCaseMapper.deleteByExample(example);
}
public void deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanLoadCaseExample example = new TestPlanLoadCaseExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
testPlanLoadCaseMapper.deleteByExample(example);
}
}

View File

@ -3,7 +3,10 @@ package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanPrincipal; import io.metersphere.plan.domain.TestPlanPrincipal;
import io.metersphere.plan.mapper.TestPlanPrincipalMapper; import io.metersphere.plan.mapper.TestPlanPrincipalMapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty; import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -14,11 +17,23 @@ import java.util.List;
public class TestPlanPrincipalService { public class TestPlanPrincipalService {
@Resource @Resource
TestPlanPrincipalMapper testPlanPrincipalMapper; private SqlSessionFactory sqlSessionFactory;
public void batchSave(@NotEmpty List<TestPlanPrincipal> testPlanPrincipalList) { public void batchSave(List<TestPlanPrincipal> testPlanPrincipalList) {
for (TestPlanPrincipal testPlanPrincipal : testPlanPrincipalList) { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
testPlanPrincipalMapper.insert(testPlanPrincipal); TestPlanPrincipalMapper testPlanPrincipalMapper = sqlSession.getMapper(TestPlanPrincipalMapper.class);
try {
int insertIndex = 0;
for (TestPlanPrincipal testPlanPrincipal : testPlanPrincipalList) {
testPlanPrincipalMapper.insert(testPlanPrincipal);
insertIndex++;
if (insertIndex % 50 == 0) {
sqlSession.flushStatements();
}
}
sqlSession.flushStatements();
} finally {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} }
} }
} }

View File

@ -1,15 +1,21 @@
package io.metersphere.plan.service; package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.*;
import io.metersphere.plan.domain.TestPlanFollower;
import io.metersphere.plan.domain.TestPlanPrincipal;
import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.dto.TestPlanDTO;
import io.metersphere.plan.mapper.ExtTestPlanMapper;
import io.metersphere.plan.mapper.TestPlanConfigMapper;
import io.metersphere.plan.mapper.TestPlanMapper; import io.metersphere.plan.mapper.TestPlanMapper;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.SessionUtils;
import io.metersphere.system.domain.User;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,23 +27,59 @@ import java.util.UUID;
public class TestPlanService { public class TestPlanService {
@Resource @Resource
private TestPlanMapper testPlanMapper; private TestPlanMapper testPlanMapper;
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private TestPlanConfigMapper testPlanConfigMapper;
@Resource
private TestPlanConfigService testPlanConfigService;
@Resource @Resource
private TestPlanPrincipalService testPlanPrincipalService; private TestPlanPrincipalService testPlanPrincipalService;
@Resource @Resource
private TestPlanFollowerService testPlanFollowerService; private TestPlanFollowerService testPlanFollowerService;
@Resource
private TestPlanApiCaseService testPlanApiCaseService;
@Resource
private TestPlanApiScenarioService testPlanApiScenarioService;
@Resource
private TestPlanLoadCaseService testPlanLoadCaseService;
@Resource
private TestPlanUiScenarioService testPlanUiScenarioService;
@Resource
private TestPlanFunctionCaseService testPlanFunctionCaseService;
public TestPlanDTO add(@NotNull TestPlanDTO testPlanCreateRequest) {
User user = SessionUtils.getUser();
if (user == null) {
MSException.throwException("Cannot find user!");
}
if (StringUtils.equals(testPlanCreateRequest.getParentId(), testPlanCreateRequest.getId())) {
MSException.throwException("The parent test plan cannot be the same as the current test plan!");
}
if (StringUtils.isBlank(testPlanCreateRequest.getId())) {
testPlanCreateRequest.setId(UUID.randomUUID().toString());
}
testPlanCreateRequest.setCreateUser(user.getId());
testPlanCreateRequest.setUpdateUser(user.getId());
testPlanCreateRequest.setCreateTime(System.currentTimeMillis());
testPlanCreateRequest.setUpdateTime(System.currentTimeMillis());
public TestPlanDTO add(@NotNull TestPlanDTO testPlanDTO) {
TestPlan testPlan = new TestPlan(); TestPlan testPlan = new TestPlan();
BeanUtils.copyBean(testPlan, testPlanDTO); BeanUtils.copyBean(testPlan, testPlanCreateRequest);
testPlan.setId(UUID.randomUUID().toString());
//todo SongTianyang:暂时没有SessionUtil创建人先根据前台传值保存
testPlan.setCreateTime(System.currentTimeMillis());
testPlanMapper.insert(testPlan); testPlanMapper.insert(testPlan);
if (CollectionUtils.isNotEmpty(testPlanDTO.getFollowers())) { TestPlanConfig testPlanConfig = new TestPlanConfig();
testPlanConfig.setTestPlanId(testPlan.getId());
testPlanConfig.setAutomaticStatusUpdate(testPlanCreateRequest.isAutomaticStatusUpdate());
testPlanConfig.setRepeatCase(testPlanCreateRequest.isRepeatCase());
testPlanConfig.setPassThreshold(testPlanCreateRequest.getPassThreshold());
testPlanConfigMapper.insert(testPlanConfig);
if (CollectionUtils.isNotEmpty(testPlanCreateRequest.getFollowers())) {
List<TestPlanFollower> testPlanFollowerList = new ArrayList<>(); List<TestPlanFollower> testPlanFollowerList = new ArrayList<>();
for (String follower : testPlanDTO.getFollowers()) { for (String follower : testPlanCreateRequest.getFollowers()) {
TestPlanFollower testPlanFollower = new TestPlanFollower(); TestPlanFollower testPlanFollower = new TestPlanFollower();
testPlanFollower.setTestPlanId(testPlan.getId()); testPlanFollower.setTestPlanId(testPlan.getId());
testPlanFollower.setUserId(follower); testPlanFollower.setUserId(follower);
@ -46,9 +88,9 @@ public class TestPlanService {
testPlanFollowerService.batchSave(testPlanFollowerList); testPlanFollowerService.batchSave(testPlanFollowerList);
} }
if (CollectionUtils.isNotEmpty(testPlanDTO.getPrincipals())) { if (CollectionUtils.isNotEmpty(testPlanCreateRequest.getPrincipals())) {
List<TestPlanPrincipal> testPlanPrincipalList = new ArrayList<>(); List<TestPlanPrincipal> testPlanPrincipalList = new ArrayList<>();
for (String principal : testPlanDTO.getPrincipals()) { for (String principal : testPlanCreateRequest.getPrincipals()) {
TestPlanPrincipal testPlanPrincipal = new TestPlanPrincipal(); TestPlanPrincipal testPlanPrincipal = new TestPlanPrincipal();
testPlanPrincipal.setTestPlanId(testPlan.getId()); testPlanPrincipal.setTestPlanId(testPlan.getId());
testPlanPrincipal.setUserId(principal); testPlanPrincipal.setUserId(principal);
@ -56,6 +98,60 @@ public class TestPlanService {
} }
testPlanPrincipalService.batchSave(testPlanPrincipalList); testPlanPrincipalService.batchSave(testPlanPrincipalList);
} }
return testPlanDTO; return testPlanCreateRequest;
}
public void batchDelete(List<String> idList) {
TestPlanExample example = new TestPlanExample();
example.createCriteria().andIdIn(idList);
testPlanMapper.deleteByExample(example);
this.cascadeDelete(idList);
}
public void delete(@NotBlank String id) {
TestPlanExample example = new TestPlanExample();
example.createCriteria().andIdEqualTo(id);
testPlanMapper.deleteByExample(example);
this.cascadeDelete(id);
}
public void deleteByParentId(String parentTestPlanId) {
List<String> childrenTestPlanIdList = extTestPlanMapper.selectByParentId(parentTestPlanId);
if (CollectionUtils.isNotEmpty(childrenTestPlanIdList)) {
this.batchDelete(childrenTestPlanIdList);
}
}
@Validated
public void deleteBatchByParentId(List<String> parentTestPlanId) {
List<String> childrenTestPlanIdList = extTestPlanMapper.selectByParentIdList(parentTestPlanId);
if (CollectionUtils.isNotEmpty(childrenTestPlanIdList)) {
this.batchDelete(childrenTestPlanIdList);
}
}
private void cascadeDelete(String id) {
//删除子计划
this.deleteByParentId(id);
//删除当前计划对应的资源
this.testPlanConfigService.delete(id);
this.testPlanFunctionCaseService.deleteByTestPlanId(id);
this.testPlanApiCaseService.deleteByTestPlanId(id);
this.testPlanApiScenarioService.deleteByTestPlanId(id);
this.testPlanUiScenarioService.deleteByTestPlanId(id);
this.testPlanLoadCaseService.deleteByTestPlanId(id);
}
private void cascadeDelete(List<String> idList) {
//删除子计划
this.deleteBatchByParentId(idList);
//删除当前计划对应的资源
this.testPlanConfigService.deleteBatch(idList);
this.testPlanFunctionCaseService.deleteBatchByTestPlanId(idList);
this.testPlanApiCaseService.deleteBatchByTestPlanId(idList);
this.testPlanApiScenarioService.deleteBatchByTestPlanId(idList);
this.testPlanUiScenarioService.deleteBatchByTestPlanId(idList);
this.testPlanLoadCaseService.deleteBatchByTestPlanId(idList);
} }
} }

View File

@ -0,0 +1,28 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanUiScenarioExample;
import io.metersphere.plan.mapper.TestPlanUiScenarioMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanUiScenarioService {
@Resource
private TestPlanUiScenarioMapper TestPlanUiScenarioMapper;
public int deleteByTestPlanId(String testPlanId) {
TestPlanUiScenarioExample example = new TestPlanUiScenarioExample();
example.createCriteria().andTestPlanIdEqualTo(testPlanId);
return TestPlanUiScenarioMapper.deleteByExample(example);
}
public int deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanUiScenarioExample example = new TestPlanUiScenarioExample();
example.createCriteria().andTestPlanIdIn(testPlanIdList);
return TestPlanUiScenarioMapper.deleteByExample(example);
}
}

View File

@ -4,21 +4,23 @@ import com.jayway.jsonpath.JsonPath;
import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.dto.TestPlanDTO;
import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.controller.handler.ResultHolder;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.utils.JsonUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.junit.jupiter.api.MethodOrderer; import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@ -35,13 +37,15 @@ public class TestPlanControllerTests {
private static String sessionId; private static String sessionId;
private static String csrfToken; private static String csrfToken;
static List<TestPlanDTO> SAVED_TEST_PLAN_DTO_LIST = new ArrayList<>();
static final String STATIC_UUID = UUID.randomUUID().toString();
private TestPlanDTO getSimpleTestPlan() { private TestPlanDTO getSimpleTestPlan() {
TestPlanDTO testPlan = new TestPlanDTO(); TestPlanDTO testPlan = new TestPlanDTO();
testPlan.setId("test");
testPlan.setName("test"); testPlan.setName("test");
testPlan.setProjectId("1"); testPlan.setProjectId("test-project-id");
testPlan.setParentId("1"); testPlan.setParentId("root");
testPlan.setCreateUser("JianGuo"); testPlan.setCreateUser("JianGuo");
testPlan.setStage("Smock"); testPlan.setStage("Smock");
testPlan.setStatus("PREPARE"); testPlan.setStatus("PREPARE");
@ -49,23 +53,46 @@ public class TestPlanControllerTests {
return testPlan; return testPlan;
} }
private void isPreDataOk() throws Exception {
if (SAVED_TEST_PLAN_DTO_LIST.isEmpty()) {
this.testAddSuccess();
}
}
@Test private void addTestPlanToSavedList(MockHttpServletResponse mockResponse) throws Exception {
@Order(0)
String returnData = mockResponse.getContentAsString();
ResultHolder resultHolder = JsonUtils.parseObject(returnData, ResultHolder.class);
//返回请求正常
Assertions.assertNotNull(resultHolder);
TestPlanDTO testPlanDTO = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanDTO.class);
//返回值不为空
Assertions.assertNotNull(testPlanDTO);
SAVED_TEST_PLAN_DTO_LIST.add(testPlanDTO);
}
@BeforeEach
public void login() throws Exception { public void login() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login") if (StringUtils.isAnyBlank(sessionId, csrfToken)) {
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}") MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login")
.contentType(MediaType.APPLICATION_JSON)) .content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
.andExpect(status().isOk()) .contentType(MediaType.APPLICATION_JSON))
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk())
.andReturn(); .andExpect(content().contentType(MediaType.APPLICATION_JSON))
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId"); .andReturn();
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken"); sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
}
} }
@Test @Test
@Order(1) @Order(1)
public void testAdd1() throws Exception { public void testAddSuccess() throws Exception {
//测试有责任人关注人
TestPlanDTO testPlan = this.getSimpleTestPlan(); TestPlanDTO testPlan = this.getSimpleTestPlan();
List<String> followerList = new ArrayList<>(); List<String> followerList = new ArrayList<>();
@ -80,72 +107,139 @@ public class TestPlanControllerTests {
participantList.add("SongGuoyu"); participantList.add("SongGuoyu");
testPlan.setPrincipals(participantList); testPlan.setPrincipals(participantList);
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken) .header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan)) .content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
} this.addTestPlanToSavedList(mvcResult.getResponse());
@Test //测试自动赋予了UUID
@Order(2) testPlan = this.getSimpleTestPlan();
public void testAdd2() throws Exception { testPlan.setId(STATIC_UUID);
TestPlanDTO testPlan = this.getSimpleTestPlan(); mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
this.addTestPlanToSavedList(mvcResult.getResponse());
List<String> followerList = new ArrayList<>(); //测试创建子计划
testPlan = this.getSimpleTestPlan();
followerList = new ArrayList<>();
followerList.add("JianGuo"); followerList.add("JianGuo");
followerList.add("SongGuoyu"); followerList.add("SongGuoyu");
followerList.add("SongYingyu"); followerList.add("SongYingyu");
followerList.add("SongFanti"); followerList.add("SongFanti");
testPlan.setFollowers(followerList); testPlan.setFollowers(followerList);
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") participantList = new ArrayList<>();
participantList.add("JianGuo");
participantList.add("SongGuoyu");
testPlan.setPrincipals(participantList);
testPlan.setParentId(SAVED_TEST_PLAN_DTO_LIST.get(0).getId());
mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken) .header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan)) .content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
this.addTestPlanToSavedList(mvcResult.getResponse());
//测试只有关注人
testPlan = this.getSimpleTestPlan();
followerList = new ArrayList<>();
followerList.add("JianGuo");
followerList.add("SongGuoyu");
followerList.add("SongYingyu");
followerList.add("SongFanti");
testPlan.setFollowers(followerList);
mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
this.addTestPlanToSavedList(mvcResult.getResponse());
//测试只有责任人
testPlan = this.getSimpleTestPlan();
participantList = new ArrayList<>();
participantList.add("JianGuo");
participantList.add("SongGuoyu");
testPlan.setPrincipals(participantList);
mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
this.addTestPlanToSavedList(mvcResult.getResponse());
//测试没有责任人没有关注人
testPlan = this.getSimpleTestPlan();
mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
this.addTestPlanToSavedList(mvcResult.getResponse());
} }
@Test @Test
@Order(2) @Order(2)
public void testAdd3() throws Exception { public void testDeleteSuccess() throws Exception {
TestPlanDTO testPlan = this.getSimpleTestPlan(); this.isPreDataOk();
List<String> participantList = new ArrayList<>(); String testPlanId = SAVED_TEST_PLAN_DTO_LIST.get(SAVED_TEST_PLAN_DTO_LIST.size() - 1).getId();
participantList.add("JianGuo"); mockMvc.perform(MockMvcRequestBuilders.get("/test-plan/delete/" + testPlanId)
participantList.add("SongGuoyu");
testPlan.setPrincipals(participantList);
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken) .header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); .andExpect(content().contentType(MediaType.APPLICATION_JSON));
} }
@Test @Test
@Order(4) @Order(3)
public void testAdd4() throws Exception { public void testDeleteBatchSuccess() throws Exception {
TestPlanDTO testPlan = this.getSimpleTestPlan(); this.isPreDataOk();
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") List<String> testPlanIdList = new ArrayList<>();
testPlanIdList.add(SAVED_TEST_PLAN_DTO_LIST.get(0).getId());
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/delete/batch")
.header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken) .header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan)) .content(JSON.toJSONString(testPlanIdList))
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) .andExpect(status().isOk());
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print());
} }
@Test @Test
@Order(5) public void testBatchDelete_Error_System() throws Exception {
public void testAddUserFalse() throws Exception { //没有必填项
List<String> testPlanIdList = new ArrayList<>();
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/delete/batch")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlanIdList))
.contentType(MediaType.APPLICATION_JSON)
).andExpect(status().is5xxServerError());
}
//添加测试计划反例校验:参数不合法
@Test
public void testAdd_Error_Param() throws Exception {
//没有必填项
TestPlan testPlan = new TestPlan(); TestPlan testPlan = new TestPlan();
testPlan.setName("test"); testPlan.setName("test");
@ -156,5 +250,32 @@ public class TestPlanControllerTests {
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)); .andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
//添加测试计划反例校验系统错误例如ID重复parentId不合法
@Test
public void testAdd_Error_System() throws Exception {
this.isPreDataOk();
//测试重复存储UUID不成功
TestPlanDTO testPlan = this.getSimpleTestPlan();
testPlan.setId(STATIC_UUID);
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError()).andDo(print());
//测试parentId和id相同
testPlan = this.getSimpleTestPlan();
testPlan.setId(UUID.randomUUID().toString());
testPlan.setParentId(testPlan.getId());
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(testPlan))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError());
} }
} }