fix(接口测试): 修复报告显示等待时间错误的缺陷

--bug=1038726 --user=王孝刚 【接口测试】场景报告-全局等待时间显示错误
https://www.tapd.cn/55049933/s/1491033
This commit is contained in:
wxg0103 2024-04-08 20:50:05 +08:00 committed by Craftsman
parent 6178b24103
commit 40638d10d5
16 changed files with 139 additions and 43 deletions

View File

@ -17,7 +17,7 @@ public class ApiScenarioReport implements Serializable {
@Schema(description = "报告名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario_report.name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{api_scenario_report.name.length_range}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 300, message = "{api_scenario_report.name.length_range}", groups = {Created.class, Updated.class})
private String name;
@Schema(description = "测试计划id", requiredMode = Schema.RequiredMode.REQUIRED)
@ -139,6 +139,9 @@ public class ApiScenarioReport implements Serializable {
@Schema(description = "脚本标识")
private String scriptIdentifier;
@Schema(description = "等待时间")
private Long waitingTime;
private static final long serialVersionUID = 1L;
public enum Column {
@ -172,7 +175,8 @@ public class ApiScenarioReport implements Serializable {
requestFakeErrorRate("request_fake_error_rate", "requestFakeErrorRate", "VARCHAR", false),
requestPassRate("request_pass_rate", "requestPassRate", "VARCHAR", false),
assertionPassRate("assertion_pass_rate", "assertionPassRate", "VARCHAR", false),
scriptIdentifier("script_identifier", "scriptIdentifier", "VARCHAR", false);
scriptIdentifier("script_identifier", "scriptIdentifier", "VARCHAR", false),
waitingTime("waiting_time", "waitingTime", "BIGINT", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -2143,6 +2143,66 @@ public class ApiScenarioReportExample {
addCriterion("script_identifier not between", value1, value2, "scriptIdentifier");
return (Criteria) this;
}
public Criteria andWaitingTimeIsNull() {
addCriterion("waiting_time is null");
return (Criteria) this;
}
public Criteria andWaitingTimeIsNotNull() {
addCriterion("waiting_time is not null");
return (Criteria) this;
}
public Criteria andWaitingTimeEqualTo(Long value) {
addCriterion("waiting_time =", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeNotEqualTo(Long value) {
addCriterion("waiting_time <>", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeGreaterThan(Long value) {
addCriterion("waiting_time >", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeGreaterThanOrEqualTo(Long value) {
addCriterion("waiting_time >=", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeLessThan(Long value) {
addCriterion("waiting_time <", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeLessThanOrEqualTo(Long value) {
addCriterion("waiting_time <=", value, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeIn(List<Long> values) {
addCriterion("waiting_time in", values, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeNotIn(List<Long> values) {
addCriterion("waiting_time not in", values, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeBetween(Long value1, Long value2) {
addCriterion("waiting_time between", value1, value2, "waitingTime");
return (Criteria) this;
}
public Criteria andWaitingTimeNotBetween(Long value1, Long value2) {
addCriterion("waiting_time not between", value1, value2, "waitingTime");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -33,6 +33,7 @@
<result column="request_pass_rate" jdbcType="VARCHAR" property="requestPassRate" />
<result column="assertion_pass_rate" jdbcType="VARCHAR" property="assertionPassRate" />
<result column="script_identifier" jdbcType="VARCHAR" property="scriptIdentifier" />
<result column="waiting_time" jdbcType="BIGINT" property="waitingTime" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -97,7 +98,8 @@
update_time, start_time, end_time, request_duration, `status`, trigger_mode, run_mode,
pool_id, integrated, project_id, environment_id, error_count, fake_error_count, pending_count,
success_count, assertion_count, assertion_success_count, request_error_rate, request_pending_rate,
request_fake_error_rate, request_pass_rate, assertion_pass_rate, script_identifier
request_fake_error_rate, request_pass_rate, assertion_pass_rate, script_identifier,
waiting_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.api.domain.ApiScenarioReportExample" resultMap="BaseResultMap">
select
@ -140,8 +142,8 @@
pending_count, success_count, assertion_count,
assertion_success_count, request_error_rate,
request_pending_rate, request_fake_error_rate,
request_pass_rate, assertion_pass_rate, script_identifier
)
request_pass_rate, assertion_pass_rate, script_identifier,
waiting_time)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{testPlanId,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR}, #{deleteTime,jdbcType=BIGINT}, #{deleteUser,jdbcType=VARCHAR},
#{deleted,jdbcType=BIT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT},
@ -152,8 +154,8 @@
#{pendingCount,jdbcType=BIGINT}, #{successCount,jdbcType=BIGINT}, #{assertionCount,jdbcType=BIGINT},
#{assertionSuccessCount,jdbcType=BIGINT}, #{requestErrorRate,jdbcType=VARCHAR},
#{requestPendingRate,jdbcType=VARCHAR}, #{requestFakeErrorRate,jdbcType=VARCHAR},
#{requestPassRate,jdbcType=VARCHAR}, #{assertionPassRate,jdbcType=VARCHAR}, #{scriptIdentifier,jdbcType=VARCHAR}
)
#{requestPassRate,jdbcType=VARCHAR}, #{assertionPassRate,jdbcType=VARCHAR}, #{scriptIdentifier,jdbcType=VARCHAR},
#{waitingTime,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioReport">
insert into api_scenario_report
@ -251,6 +253,9 @@
<if test="scriptIdentifier != null">
script_identifier,
</if>
<if test="waitingTime != null">
waiting_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -346,6 +351,9 @@
<if test="scriptIdentifier != null">
#{scriptIdentifier,jdbcType=VARCHAR},
</if>
<if test="waitingTime != null">
#{waitingTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.api.domain.ApiScenarioReportExample" resultType="java.lang.Long">
@ -450,6 +458,9 @@
<if test="record.scriptIdentifier != null">
script_identifier = #{record.scriptIdentifier,jdbcType=VARCHAR},
</if>
<if test="record.waitingTime != null">
waiting_time = #{record.waitingTime,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -487,7 +498,8 @@
request_fake_error_rate = #{record.requestFakeErrorRate,jdbcType=VARCHAR},
request_pass_rate = #{record.requestPassRate,jdbcType=VARCHAR},
assertion_pass_rate = #{record.assertionPassRate,jdbcType=VARCHAR},
script_identifier = #{record.scriptIdentifier,jdbcType=VARCHAR}
script_identifier = #{record.scriptIdentifier,jdbcType=VARCHAR},
waiting_time = #{record.waitingTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -585,6 +597,9 @@
<if test="scriptIdentifier != null">
script_identifier = #{scriptIdentifier,jdbcType=VARCHAR},
</if>
<if test="waitingTime != null">
waiting_time = #{waitingTime,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -619,7 +634,8 @@
request_fake_error_rate = #{requestFakeErrorRate,jdbcType=VARCHAR},
request_pass_rate = #{requestPassRate,jdbcType=VARCHAR},
assertion_pass_rate = #{assertionPassRate,jdbcType=VARCHAR},
script_identifier = #{scriptIdentifier,jdbcType=VARCHAR}
script_identifier = #{scriptIdentifier,jdbcType=VARCHAR},
waiting_time = #{waitingTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
@ -629,7 +645,7 @@
pool_id, integrated, project_id, environment_id, error_count, fake_error_count,
pending_count, success_count, assertion_count, assertion_success_count, request_error_rate,
request_pending_rate, request_fake_error_rate, request_pass_rate, assertion_pass_rate,
script_identifier)
script_identifier, waiting_time)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.testPlanId,jdbcType=VARCHAR},
@ -643,7 +659,7 @@
#{item.assertionSuccessCount,jdbcType=BIGINT}, #{item.requestErrorRate,jdbcType=VARCHAR},
#{item.requestPendingRate,jdbcType=VARCHAR}, #{item.requestFakeErrorRate,jdbcType=VARCHAR},
#{item.requestPassRate,jdbcType=VARCHAR}, #{item.assertionPassRate,jdbcType=VARCHAR},
#{item.scriptIdentifier,jdbcType=VARCHAR})
#{item.scriptIdentifier,jdbcType=VARCHAR}, #{item.waitingTime,jdbcType=BIGINT})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -749,6 +765,9 @@
<if test="'script_identifier'.toString() == column.value">
#{item.scriptIdentifier,jdbcType=VARCHAR}
</if>
<if test="'waiting_time'.toString() == column.value">
#{item.waitingTime,jdbcType=BIGINT}
</if>
</foreach>
)
</foreach>

View File

@ -123,7 +123,7 @@ CREATE TABLE IF NOT EXISTS schedule
`create_time` BIGINT NOT NULL COMMENT '创建时间',
`update_time` BIGINT NOT NULL COMMENT '更新时间',
`project_id` VARCHAR(50) COMMENT '项目ID',
`name` VARCHAR(100) COMMENT '名称',
`name` VARCHAR(255) COMMENT '名称',
`config` VARCHAR(1000) COMMENT '配置',
PRIMARY KEY (id)
) ENGINE = InnoDB

View File

@ -368,6 +368,7 @@ CREATE TABLE IF NOT EXISTS api_scenario_report(
`request_pass_rate` VARCHAR(20) NOT NULL DEFAULT 'Calculating' COMMENT '请求通过率' ,
`assertion_pass_rate` VARCHAR(20) NOT NULL DEFAULT 'Calculating' COMMENT '断言通过率' ,
`script_identifier` VARCHAR(255) COMMENT '脚本标识' ,
`waiting_time` BIGINT COMMENT '等待时间' ,
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4

View File

@ -22,8 +22,6 @@ public class ApiScenarioReportDTO extends ApiScenarioReport {
private String creatUserName;
@Schema(description = "请求总数")
private Long requestTotal;
@Schema(description = "等待时间")
private Long waitingTime;
@Schema(description = "步骤失败数")
private Long stepErrorCount;
@Schema(description = "步骤误报数")

View File

@ -19,6 +19,7 @@ import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.schedule.BaseScheduleJob;
import io.metersphere.system.uid.IDGenerator;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
@ -73,6 +74,11 @@ public class ApiScenarioScheduleJob extends BaseScheduleJob {
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(apiRunModeConfigDTO.getPoolId());
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
if (parseParam.getScenarioConfig() != null
&& parseParam.getScenarioConfig().getOtherConfig() != null
&& BooleanUtils.isTrue(parseParam.getScenarioConfig().getOtherConfig().getEnableStepWait())) {
scenarioReport.setWaitingTime(parseParam.getScenarioConfig().getOtherConfig().getStepWaitTime());
}
apiScenarioService.initApiReport(apiScenarioDetail, scenarioReport);
// 初始化报告步骤

View File

@ -1,6 +1,5 @@
package io.metersphere.api.service.scenario;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioRecord;
import io.metersphere.api.domain.ApiScenarioReport;
@ -9,7 +8,10 @@ import io.metersphere.api.dto.ApiScenarioParamConfig;
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.dto.scenario.ApiScenarioParseParam;
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
import io.metersphere.api.mapper.ApiScenarioReportMapper;
import io.metersphere.api.mapper.ExtApiScenarioMapper;
import io.metersphere.api.service.ApiBatchRunBaseService;
@ -22,7 +24,10 @@ import io.metersphere.sdk.dto.api.task.CollectionReportDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.*;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
@ -31,7 +36,10 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
@ -311,6 +319,14 @@ public class ApiScenarioBatchRunService {
if (runModeConfig.isIntegratedReport()) {
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
} else {
if (parseParam.getScenarioConfig() != null
&& parseParam.getScenarioConfig().getOtherConfig() != null
&& BooleanUtils.isTrue(parseParam.getScenarioConfig().getOtherConfig().getEnableStepWait())) {
ApiScenarioReport apiScenarioReport = new ApiScenarioReport();
apiScenarioReport.setId(reportId);
apiScenarioReport.setWaitingTime(parseParam.getScenarioConfig().getOtherConfig().getStepWaitTime());
apiScenarioReportMapper.updateByPrimaryKeySelective(apiScenarioReport);
}
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}

View File

@ -8,7 +8,6 @@ import io.metersphere.api.dto.report.ApiScenarioReportListDTO;
import io.metersphere.api.dto.scenario.ApiScenarioReportDTO;
import io.metersphere.api.dto.scenario.ApiScenarioReportDetailDTO;
import io.metersphere.api.dto.scenario.ApiScenarioReportStepDTO;
import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.api.mapper.*;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.sdk.constants.ApiReportStatus;
@ -18,7 +17,6 @@ import io.metersphere.sdk.dto.api.result.RequestResult;
import io.metersphere.sdk.mapper.EnvironmentGroupMapper;
import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.mapper.TestResourcePoolMapper;
@ -26,7 +24,6 @@ import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.service.UserLoginService;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
@ -186,13 +183,6 @@ public class ApiScenarioReportService {
BeanUtils.copyBean(scenarioReportDTO, scenarioReport);
//需要查询出所有的步骤
List<ApiScenarioReportStepDTO> scenarioReportSteps = extApiScenarioReportMapper.selectStepByReportId(id);
if (BooleanUtils.isFalse(scenarioReport.getIntegrated())) {
ApiScenarioBlob apiScenarioBlob = extApiScenarioReportMapper.getScenarioBlob(id);
if (apiScenarioBlob != null) {
ScenarioConfig scenarioConfig = JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class);
scenarioReportDTO.setWaitingTime(scenarioConfig.getOtherConfig().getStepWaitTime());
}
}
//查询所有步骤的detail
List<ApiScenarioReportStepDTO> deatilList = extApiScenarioReportMapper.selectStepDeatilByReportId(id);
//根据stepId进行分组

View File

@ -1356,6 +1356,11 @@ public class ApiScenarioService extends MoveNodeService {
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
if (parseParam.getScenarioConfig() != null
&& parseParam.getScenarioConfig().getOtherConfig() != null
&& BooleanUtils.isTrue(parseParam.getScenarioConfig().getOtherConfig().getEnableStepWait())) {
scenarioReport.setWaitingTime(parseParam.getScenarioConfig().getOtherConfig().getStepWaitTime());
}
initApiReport(apiScenario, scenarioReport);
// 初始化报告步骤
@ -2488,6 +2493,7 @@ public class ApiScenarioService extends MoveNodeService {
public void deleteScheduleConfig(String scenarioId) {
scheduleService.deleteByResourceId(scenarioId, ApiScenarioScheduleJob.getJobKey(scenarioId), ApiScenarioScheduleJob.getTriggerKey(scenarioId));
}
public String scheduleConfig(ApiScenarioScheduleConfigRequest scheduleRequest, String operator) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(scheduleRequest.getScenarioId());
ScheduleConfig scheduleConfig = ScheduleConfig.builder()

View File

@ -381,6 +381,7 @@
assertions.value.splice(currentIndex, 1);
activeKey.value = currentIndex > 0 ? assertions.value[currentIndex - 1].id : '';
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},

View File

@ -20,7 +20,9 @@
<div class="flex-shrink-0 text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
<div>
{{
props.detail.environmentName || props.showType === 'CASE'
props.detail.environmentName
? props.detail.environmentName
: props.showType === 'CASE'
? t('report.detail.api.caseSaveEnv')
: t('report.detail.api.scenarioSavedEnv')
}}
@ -40,7 +42,7 @@
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="!props.detail.integrated && props.showType === 'API'">
<span v-if="!props.detail.integrated && props.showType === 'API' && props.detail.waitingTime">
{{ props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[0] : '-' }}
<span>{{ props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[1] : 'ms' }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>

View File

@ -134,7 +134,7 @@
<ExecutionStatus
:module-type="props.moduleType"
:status="record.status"
:script-identifier="record.scriptIdentifier"
:script-identifier="props.moduleType === ReportEnum.API_SCENARIO_REPORT ? record.scriptIdentifier : null"
/>
</template>
<template #triggerMode="{ record }">

View File

@ -2,7 +2,7 @@
<div class="flex items-center justify-start">
<MsIcon :type="getExecutionResult().icon" :class="getExecutionResult()?.color" size="14" />
<span class="ml-1">{{ t(getExecutionResult().label) }}</span>
<a-tooltip v-if="props.scriptIdentifier" :content="getMsg()">
<a-tooltip v-if="props.scriptIdentifier">
<MsTag
class="ml-2"
:self-style="{
@ -11,7 +11,7 @@
backgroundColor: 'white',
}"
>
{{ t('report.detail.script.error') }}
{{ t('report.detail.scenario.errorTip') }}
</MsTag>
</a-tooltip>
</div>
@ -119,13 +119,6 @@
return iconTypeStatus.value[props.moduleType][props.status];
}
const methodColor = 'rgb(var(--warning-7))';
function getMsg() {
if (props.moduleType === ReportEnum.API_SCENARIO_REPORT && props.scriptIdentifier) {
return t('report.detail.scenario.errorTip');
}
return t('report.detail.api.errorTip');
}
</script>
<style scoped></style>

View File

@ -30,7 +30,7 @@ export default {
'report.detail.stepTotal': 'total',
'report.detail.script.error': 'Script error',
'report.detail.scenario.errorTip':
'There is a pre/post script running error in the execution step of the current scenario.',
'There is a pre/post setup script run error in the execution environment of the current scene.',
'report.detail.api.errorTip': 'There are pre/post script running errors in the current use case execution.',
'report.detail.api.resContent': 'Response content',
'report.detail.api.executionTime': 'Execution time',

View File

@ -29,7 +29,7 @@ export default {
'report.detail.pendingCount': '未执行',
'report.detail.stepTotal': '总数',
'report.detail.script.error': '脚本失败',
'report.detail.scenario.errorTip': '当前场景的执行步骤中存在 前/后置脚本运行错误',
'report.detail.scenario.errorTip': '当前场景的执行环境中存在 前/后置脚本 运行错误',
'report.detail.api.errorTip': '当前用例执行存在 前/后置脚本运行错误',
'report.detail.api.resContent': '响应内容',
'report.detail.api.executionTime': '执行时间',