fix(测试跟踪): 测试计划性能用例列表批量执行状态未更新
--bug=1024704 --user=李玉号 【测试跟踪】测试计划-性能用例列表-批量执行-串行/并行执行完成-状态、执行结果、报告字段未实时更新 https://www.tapd.cn/55049933/s/1356057
This commit is contained in:
parent
aace6c95e4
commit
2170e068d7
|
@ -8,8 +8,11 @@ import org.apache.ibatis.annotations.Select;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ExtTestPlanLoadCaseMapper {
|
public interface ExtTestPlanLoadCaseMapper {
|
||||||
@Select("SELECT id,test_plan_id,load_case_id,status FROM test_plan_load_case WHERE id = #{0} ")
|
@Select("SELECT id,test_plan_id,load_case_id,status,load_report_id FROM test_plan_load_case WHERE id = #{0} ")
|
||||||
TestPlanLoadCase selectBaseInfoById(String testId);
|
TestPlanLoadCase selectBaseInfoById(String testId);
|
||||||
|
|
||||||
List<CaseExecResult> selectExecResult(@Param("testPlanId") String testPlanId, @Param("ids") List<String> relevanceApiCaseList);
|
List<CaseExecResult> selectExecResult(@Param("testPlanId") String testPlanId, @Param("ids") List<String> relevanceApiCaseList);
|
||||||
|
|
||||||
|
@Select("select status from load_test_report where id = #{0}")
|
||||||
|
String queryReportStatus(String reportId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package io.metersphere.dto;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
public class TestPlanLoadCaseStatusDTO {
|
||||||
|
private String planCaseId;
|
||||||
|
private String planCaseStatus;
|
||||||
|
private String planCaseReportId;
|
||||||
|
private String planCaseReportStatus;
|
||||||
|
}
|
|
@ -9,8 +9,10 @@ import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
|
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtTestPlanUiCaseMapper;
|
import io.metersphere.base.mapper.ext.ExtTestPlanUiCaseMapper;
|
||||||
import io.metersphere.dto.TestPlanCaseStatusDTO;
|
import io.metersphere.dto.TestPlanCaseStatusDTO;
|
||||||
|
import io.metersphere.dto.TestPlanLoadCaseStatusDTO;
|
||||||
import io.metersphere.utils.JsonUtils;
|
import io.metersphere.utils.JsonUtils;
|
||||||
import io.metersphere.utils.LoggerUtil;
|
import io.metersphere.utils.LoggerUtil;
|
||||||
|
import io.metersphere.websocket.LoadCaseStatusHandleSocket;
|
||||||
import io.metersphere.websocket.UICaseStatusHandleSocket;
|
import io.metersphere.websocket.UICaseStatusHandleSocket;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
@ -81,7 +83,21 @@ public class AutomationCaseExecOverService {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LoggerUtil.error("ui执行完成发送socket失败!", e);
|
LoggerUtil.error("ui执行完成发送socket失败!", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (testPlanLoadCase != null) {
|
||||||
|
try {
|
||||||
|
String reportStatus = extTestPlanLoadCaseMapper.queryReportStatus(testPlanLoadCase.getLoadReportId());
|
||||||
|
TestPlanLoadCaseStatusDTO loadCaseStatusDTO = TestPlanLoadCaseStatusDTO
|
||||||
|
.builder()
|
||||||
|
.planCaseId(testPlanLoadCase.getId())
|
||||||
|
.planCaseStatus(triggerCaseExecResult)
|
||||||
|
.planCaseReportId(testPlanLoadCase.getLoadReportId())
|
||||||
|
.planCaseReportStatus(reportStatus)
|
||||||
|
.build();
|
||||||
|
LoadCaseStatusHandleSocket.sendMessageSingle(planId, JsonUtils.toJSONString(loadCaseStatusDTO));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LoggerUtil.error("load执行完成发送socket失败!", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package io.metersphere.websocket;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import io.metersphere.utils.LoggerUtil;
|
||||||
|
import jakarta.websocket.*;
|
||||||
|
import jakarta.websocket.server.PathParam;
|
||||||
|
import jakarta.websocket.server.ServerEndpoint;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送消息更新UI列表用例状态
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ServerEndpoint("/websocket/plan/load/{planId}")
|
||||||
|
public class LoadCaseStatusHandleSocket {
|
||||||
|
|
||||||
|
public static final Map<String, Set<Session>> ONLINE_PLAN_LOAD_SESSIONS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void sendMessage(Session session, String message) {
|
||||||
|
if (session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 替换了web容器后 jetty没有设置永久有效的参数,这里暂时设置超时时间为一天
|
||||||
|
session.setMaxIdleTimeout(86400000L);
|
||||||
|
RemoteEndpoint.Async async = session.getAsyncRemote();
|
||||||
|
if (async == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
async.sendText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendMessageSingle(String planId, String message) {
|
||||||
|
ONLINE_PLAN_LOAD_SESSIONS.get(planId).forEach(session -> sendMessage(session, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接成功响应
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void openSession(@PathParam("planId") String planId, Session session) {
|
||||||
|
Set<Session> sessions = ONLINE_PLAN_LOAD_SESSIONS.getOrDefault(planId, new HashSet<>());
|
||||||
|
sessions.add(session);
|
||||||
|
ONLINE_PLAN_LOAD_SESSIONS.put(planId, sessions);
|
||||||
|
sendMessage(session, "CONN_SUCCEEDED");
|
||||||
|
LoggerUtil.info("客户端: [" + planId + "] : 连接成功!" + ONLINE_PLAN_LOAD_SESSIONS.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到消息响应
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(@PathParam("planId") String planId, String message) {
|
||||||
|
LoggerUtil.info("服务器收到:[" + planId + "] : " + message);
|
||||||
|
sendMessageSingle(planId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭响应
|
||||||
|
*/
|
||||||
|
@OnClose
|
||||||
|
public void onClose(@PathParam("planId") String planId, Session session) throws IOException {
|
||||||
|
//当前的Session 移除
|
||||||
|
Set<Session> sessions = ONLINE_PLAN_LOAD_SESSIONS.get(planId);
|
||||||
|
if (CollectionUtils.isNotEmpty(sessions)) {
|
||||||
|
LogUtil.info("剔除一个socket链接: {} - {}", planId, session.getId());
|
||||||
|
sessions.remove(session);
|
||||||
|
LogUtil.info("在线socket链接: {}, size: {}", planId, sessions.size());
|
||||||
|
} else {
|
||||||
|
ONLINE_PLAN_LOAD_SESSIONS.remove(planId);
|
||||||
|
LoggerUtil.info("[" + planId + "] : 断开连接!" + ONLINE_PLAN_LOAD_SESSIONS.size());
|
||||||
|
//并且通知其他人当前用户已经断开连接了
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接异常响应
|
||||||
|
*/
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable throwable) throws IOException {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每一分钟群发一次心跳检查
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "0 0/1 * * * ?")
|
||||||
|
public void heartbeatCheck() {
|
||||||
|
for (Set<Session> sessions : ONLINE_PLAN_LOAD_SESSIONS.values()) {
|
||||||
|
sessions.forEach(session -> sendMessage(session, "heartbeat check"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -174,7 +174,9 @@ import {
|
||||||
testPlanLoadCaseSelectAllTableRows, testPlanLoadCaseUpdate, testPlanLoadList
|
testPlanLoadCaseSelectAllTableRows, testPlanLoadCaseUpdate, testPlanLoadList
|
||||||
} from "@/api/remote/plan/test-plan-load-case";
|
} from "@/api/remote/plan/test-plan-load-case";
|
||||||
import MicroApp from "metersphere-frontend/src/components/MicroApp";
|
import MicroApp from "metersphere-frontend/src/components/MicroApp";
|
||||||
|
import {baseSocket} from "@/api/base-network";
|
||||||
|
|
||||||
|
let refreshTable = undefined;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestPlanLoadCaseList",
|
name: "TestPlanLoadCaseList",
|
||||||
|
@ -278,6 +280,7 @@ export default {
|
||||||
created() {
|
created() {
|
||||||
this.initTable();
|
this.initTable();
|
||||||
this.refreshStatus();
|
this.refreshStatus();
|
||||||
|
this.initWebSocket();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectProjectId() {
|
selectProjectId() {
|
||||||
|
@ -323,7 +326,6 @@ export default {
|
||||||
let obj = {config: config, requests: runArr, userId: getCurrentUser().id};
|
let obj = {config: config, requests: runArr, userId: getCurrentUser().id};
|
||||||
this._runBatch(obj);
|
this._runBatch(obj);
|
||||||
this.initTable();
|
this.initTable();
|
||||||
this.refreshStatus();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let runArr = [];
|
let runArr = [];
|
||||||
|
@ -339,7 +341,6 @@ export default {
|
||||||
let obj = {config: config, requests: runArr, userId: getCurrentUser().id};
|
let obj = {config: config, requests: runArr, userId: getCurrentUser().id};
|
||||||
this._runBatch(obj);
|
this._runBatch(obj);
|
||||||
this.initTable();
|
this.initTable();
|
||||||
this.refreshStatus();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_runBatch(loadCases) {
|
_runBatch(loadCases) {
|
||||||
|
@ -347,8 +348,10 @@ export default {
|
||||||
this.$success(this.$t('test_track.plan.load_case.exec'));
|
this.$success(this.$t('test_track.plan.load_case.exec'));
|
||||||
this.$message(this.$t('commons.run_message'));
|
this.$message(this.$t('commons.run_message'));
|
||||||
this.$refs.taskCenter.open();
|
this.$refs.taskCenter.open();
|
||||||
|
// 批量执行,10s后刷新一次列表状态,后续执行结果由socket推送
|
||||||
|
refreshTable = window.setTimeout(() => {
|
||||||
this.initTable();
|
this.initTable();
|
||||||
this.refreshStatus();
|
}, 10 * 1000);
|
||||||
},
|
},
|
||||||
search() {
|
search() {
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
|
@ -518,9 +521,45 @@ export default {
|
||||||
});
|
});
|
||||||
window.open(performanceResolve.href, '_blank');
|
window.open(performanceResolve.href, '_blank');
|
||||||
},
|
},
|
||||||
|
initWebSocket() {
|
||||||
|
this.websocket = baseSocket("/plan/load/" + this.planId);
|
||||||
|
this.websocket.onmessage = this.onMessage;
|
||||||
|
this.websocket.onopen = this.onOpen;
|
||||||
|
this.websocket.onerror = this.onError;
|
||||||
|
this.websocket.onclose = this.onClose;
|
||||||
|
},
|
||||||
|
onOpen() {
|
||||||
|
},
|
||||||
|
onError() {
|
||||||
|
},
|
||||||
|
onMessage(e) {
|
||||||
|
if (e.data === 'CONN_SUCCEEDED') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(e.data);
|
||||||
|
let {planCaseId, planCaseStatus, planCaseReportStatus, planCaseReportId} = obj;
|
||||||
|
let data = this.tableData.filter(d => d.id === planCaseId);
|
||||||
|
if (data.length > 0) {
|
||||||
|
data[0]['status'] = planCaseReportStatus;
|
||||||
|
data[0]['caseStatus'] = planCaseStatus;
|
||||||
|
data[0]['loadReportId'] = planCaseReportId;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
},
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.cancelRefresh();
|
this.cancelRefresh();
|
||||||
|
if (refreshTable) {
|
||||||
|
window.clearTimeout(refreshTable);
|
||||||
|
}
|
||||||
|
if (this.websocket && this.websocket.close instanceof Function) {
|
||||||
|
this.websocket.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue